![image](https://github.com/go-gitea/gitea/assets/2114189/b7a14ed6-db89-4f21-a590-66cd33307233)tags/v1.22.0-rc0
@@ -231,7 +231,7 @@ func CreateBranch(ctx *context.Context) { | |||
if len(e.Message) == 0 { | |||
ctx.Flash.Error(ctx.Tr("repo.editor.push_rejected_no_message")) | |||
} else { | |||
flashError, err := ctx.RenderToString(tplAlertDetails, map[string]any{ | |||
flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{ | |||
"Message": ctx.Tr("repo.editor.push_rejected"), | |||
"Summary": ctx.Tr("repo.editor.push_rejected_summary"), | |||
"Details": utils.SanitizeFlashErrorString(e.Message), |
@@ -341,7 +341,7 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b | |||
if len(errPushRej.Message) == 0 { | |||
ctx.RenderWithErr(ctx.Tr("repo.editor.push_rejected_no_message"), tplEditFile, &form) | |||
} else { | |||
flashError, err := ctx.RenderToString(tplAlertDetails, map[string]any{ | |||
flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{ | |||
"Message": ctx.Tr("repo.editor.push_rejected"), | |||
"Summary": ctx.Tr("repo.editor.push_rejected_summary"), | |||
"Details": utils.SanitizeFlashErrorString(errPushRej.Message), | |||
@@ -353,7 +353,7 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b | |||
ctx.RenderWithErr(flashError, tplEditFile, &form) | |||
} | |||
} else { | |||
flashError, err := ctx.RenderToString(tplAlertDetails, map[string]any{ | |||
flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{ | |||
"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()), | |||
@@ -542,7 +542,7 @@ func DeleteFilePost(ctx *context.Context) { | |||
if len(errPushRej.Message) == 0 { | |||
ctx.RenderWithErr(ctx.Tr("repo.editor.push_rejected_no_message"), tplDeleteFile, &form) | |||
} else { | |||
flashError, err := ctx.RenderToString(tplAlertDetails, map[string]any{ | |||
flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{ | |||
"Message": ctx.Tr("repo.editor.push_rejected"), | |||
"Summary": ctx.Tr("repo.editor.push_rejected_summary"), | |||
"Details": utils.SanitizeFlashErrorString(errPushRej.Message), | |||
@@ -742,7 +742,7 @@ func UploadFilePost(ctx *context.Context) { | |||
if len(errPushRej.Message) == 0 { | |||
ctx.RenderWithErr(ctx.Tr("repo.editor.push_rejected_no_message"), tplUploadFile, &form) | |||
} else { | |||
flashError, err := ctx.RenderToString(tplAlertDetails, map[string]any{ | |||
flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{ | |||
"Message": ctx.Tr("repo.editor.push_rejected"), | |||
"Summary": ctx.Tr("repo.editor.push_rejected_summary"), | |||
"Details": utils.SanitizeFlashErrorString(errPushRej.Message), |
@@ -9,6 +9,7 @@ import ( | |||
stdCtx "context" | |||
"errors" | |||
"fmt" | |||
"html/template" | |||
"math/big" | |||
"net/http" | |||
"net/url" | |||
@@ -1016,7 +1017,7 @@ func NewIssue(ctx *context.Context) { | |||
ctx.HTML(http.StatusOK, tplIssueNew) | |||
} | |||
func renderErrorOfTemplates(ctx *context.Context, errs map[string]error) string { | |||
func renderErrorOfTemplates(ctx *context.Context, errs map[string]error) template.HTML { | |||
var files []string | |||
for k := range errs { | |||
files = append(files, k) | |||
@@ -1028,14 +1029,14 @@ func renderErrorOfTemplates(ctx *context.Context, errs map[string]error) string | |||
lines = append(lines, fmt.Sprintf("%s: %v", file, errs[file])) | |||
} | |||
flashError, err := ctx.RenderToString(tplAlertDetails, map[string]any{ | |||
flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{ | |||
"Message": ctx.Tr("repo.issues.choose.ignore_invalid_templates"), | |||
"Summary": ctx.Tr("repo.issues.choose.invalid_templates", len(errs)), | |||
"Details": utils.SanitizeFlashErrorString(strings.Join(lines, "\n")), | |||
}) | |||
if err != nil { | |||
log.Debug("render flash error: %v", err) | |||
flashError = ctx.Locale.TrString("repo.issues.choose.ignore_invalid_templates") | |||
flashError = ctx.Locale.Tr("repo.issues.choose.ignore_invalid_templates") | |||
} | |||
return flashError | |||
} | |||
@@ -3296,7 +3297,7 @@ func ChangeIssueReaction(ctx *context.Context) { | |||
return | |||
} | |||
html, err := ctx.RenderToString(tplReactions, map[string]any{ | |||
html, err := ctx.RenderToHTML(tplReactions, map[string]any{ | |||
"ctxData": ctx.Data, | |||
"ActionURL": fmt.Sprintf("%s/issues/%d/reactions", ctx.Repo.RepoLink, issue.Index), | |||
"Reactions": issue.Reactions.GroupByType(), | |||
@@ -3403,7 +3404,7 @@ func ChangeCommentReaction(ctx *context.Context) { | |||
return | |||
} | |||
html, err := ctx.RenderToString(tplReactions, map[string]any{ | |||
html, err := ctx.RenderToHTML(tplReactions, map[string]any{ | |||
"ctxData": ctx.Data, | |||
"ActionURL": fmt.Sprintf("%s/comments/%d/reactions", ctx.Repo.RepoLink, comment.ID), | |||
"Reactions": comment.Reactions.GroupByType(), | |||
@@ -3546,8 +3547,8 @@ func updateAttachments(ctx *context.Context, item any, files []string) error { | |||
return err | |||
} | |||
func attachmentsHTML(ctx *context.Context, attachments []*repo_model.Attachment, content string) string { | |||
attachHTML, err := ctx.RenderToString(tplAttachment, map[string]any{ | |||
func attachmentsHTML(ctx *context.Context, attachments []*repo_model.Attachment, content string) template.HTML { | |||
attachHTML, err := ctx.RenderToHTML(tplAttachment, map[string]any{ | |||
"ctxData": ctx.Data, | |||
"Attachments": attachments, | |||
"Content": content, |
@@ -1129,7 +1129,7 @@ func UpdatePullRequest(ctx *context.Context) { | |||
if err = pull_service.Update(ctx, issue.PullRequest, ctx.Doer, message, rebase); err != nil { | |||
if models.IsErrMergeConflicts(err) { | |||
conflictError := err.(models.ErrMergeConflicts) | |||
flashError, err := ctx.RenderToString(tplAlertDetails, map[string]any{ | |||
flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{ | |||
"Message": ctx.Tr("repo.pulls.merge_conflict"), | |||
"Summary": ctx.Tr("repo.pulls.merge_conflict_summary"), | |||
"Details": utils.SanitizeFlashErrorString(conflictError.StdErr) + "<br>" + utils.SanitizeFlashErrorString(conflictError.StdOut), | |||
@@ -1143,7 +1143,7 @@ func UpdatePullRequest(ctx *context.Context) { | |||
return | |||
} else if models.IsErrRebaseConflicts(err) { | |||
conflictError := err.(models.ErrRebaseConflicts) | |||
flashError, err := ctx.RenderToString(tplAlertDetails, map[string]any{ | |||
flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{ | |||
"Message": ctx.Tr("repo.pulls.rebase_conflict", utils.SanitizeFlashErrorString(conflictError.CommitSHA)), | |||
"Summary": ctx.Tr("repo.pulls.rebase_conflict_summary"), | |||
"Details": utils.SanitizeFlashErrorString(conflictError.StdErr) + "<br>" + utils.SanitizeFlashErrorString(conflictError.StdOut), | |||
@@ -1275,7 +1275,7 @@ func MergePullRequest(ctx *context.Context) { | |||
ctx.JSONError(ctx.Tr("repo.pulls.invalid_merge_option")) | |||
} else if models.IsErrMergeConflicts(err) { | |||
conflictError := err.(models.ErrMergeConflicts) | |||
flashError, err := ctx.RenderToString(tplAlertDetails, map[string]any{ | |||
flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{ | |||
"Message": ctx.Tr("repo.editor.merge_conflict"), | |||
"Summary": ctx.Tr("repo.editor.merge_conflict_summary"), | |||
"Details": utils.SanitizeFlashErrorString(conflictError.StdErr) + "<br>" + utils.SanitizeFlashErrorString(conflictError.StdOut), | |||
@@ -1288,7 +1288,7 @@ func MergePullRequest(ctx *context.Context) { | |||
ctx.JSONRedirect(issue.Link()) | |||
} else if models.IsErrRebaseConflicts(err) { | |||
conflictError := err.(models.ErrRebaseConflicts) | |||
flashError, err := ctx.RenderToString(tplAlertDetails, map[string]any{ | |||
flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{ | |||
"Message": ctx.Tr("repo.pulls.rebase_conflict", utils.SanitizeFlashErrorString(conflictError.CommitSHA)), | |||
"Summary": ctx.Tr("repo.pulls.rebase_conflict_summary"), | |||
"Details": utils.SanitizeFlashErrorString(conflictError.StdErr) + "<br>" + utils.SanitizeFlashErrorString(conflictError.StdOut), | |||
@@ -1318,7 +1318,7 @@ func MergePullRequest(ctx *context.Context) { | |||
if len(message) == 0 { | |||
ctx.Flash.Error(ctx.Tr("repo.pulls.push_rejected_no_message")) | |||
} else { | |||
flashError, err := ctx.RenderToString(tplAlertDetails, map[string]any{ | |||
flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{ | |||
"Message": ctx.Tr("repo.pulls.push_rejected"), | |||
"Summary": ctx.Tr("repo.pulls.push_rejected_summary"), | |||
"Details": utils.SanitizeFlashErrorString(pushrejErr.Message), | |||
@@ -1491,7 +1491,7 @@ func CompareAndPullRequestPost(ctx *context.Context) { | |||
ctx.JSONError(ctx.Tr("repo.pulls.push_rejected_no_message")) | |||
return | |||
} | |||
flashError, err := ctx.RenderToString(tplAlertDetails, map[string]any{ | |||
flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{ | |||
"Message": ctx.Tr("repo.pulls.push_rejected"), | |||
"Summary": ctx.Tr("repo.pulls.push_rejected_summary"), | |||
"Details": utils.SanitizeFlashErrorString(pushrejErr.Message), | |||
@@ -1500,8 +1500,7 @@ func CompareAndPullRequestPost(ctx *context.Context) { | |||
ctx.ServerError("CompareAndPullRequest.HTMLString", err) | |||
return | |||
} | |||
ctx.Flash.Error(flashError) | |||
ctx.JSONRedirect(ctx.Link + "?" + ctx.Req.URL.RawQuery) // FIXME: it's unfriendly, and will make the content lost | |||
ctx.JSONError(flashError) | |||
return | |||
} | |||
ctx.ServerError("NewPullRequest", err) |
@@ -6,6 +6,7 @@ package context | |||
import ( | |||
"errors" | |||
"fmt" | |||
"html/template" | |||
"net" | |||
"net/http" | |||
"net/url" | |||
@@ -104,11 +105,11 @@ func (ctx *Context) JSONTemplate(tmpl base.TplName) { | |||
} | |||
} | |||
// RenderToString renders the template content to a string | |||
func (ctx *Context) RenderToString(name base.TplName, data map[string]any) (string, error) { | |||
// RenderToHTML renders the template content to a HTML string | |||
func (ctx *Context) RenderToHTML(name base.TplName, data map[string]any) (template.HTML, error) { | |||
var buf strings.Builder | |||
err := ctx.Render.HTML(&buf, http.StatusOK, string(name), data, ctx.TemplateContext) | |||
return buf.String(), err | |||
err := ctx.Render.HTML(&buf, 0, string(name), data, ctx.TemplateContext) | |||
return template.HTML(buf.String()), err | |||
} | |||
// RenderWithErr used for page has form validation but need to prompt error to users. |
@@ -91,19 +91,24 @@ async function fetchActionDoRequest(actionElem, url, opt) { | |||
} else { | |||
window.location.reload(); | |||
} | |||
return; | |||
} else if (resp.status >= 400 && resp.status < 500) { | |||
const data = await resp.json(); | |||
// 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. | |||
showErrorToast(data.errorMessage || `server error: ${resp.status}`); | |||
if (data.errorMessage) { | |||
showErrorToast(data.errorMessage, {useHtmlBody: data.renderFormat === 'html'}); | |||
} else { | |||
showErrorToast(`server error: ${resp.status}`); | |||
} | |||
} else { | |||
showErrorToast(`server error: ${resp.status}`); | |||
} | |||
} catch (e) { | |||
console.error('error when doRequest', e); | |||
actionElem.classList.remove('is-loading', 'small-loading-icon'); | |||
showErrorToast(i18n.network_error); | |||
showErrorToast(`${i18n.network_error} ${e}`); | |||
} | |||
actionElem.classList.remove('is-loading', 'small-loading-icon'); | |||
} | |||
async function formFetchAction(e) { |
@@ -21,13 +21,12 @@ const levels = { | |||
}; | |||
// See https://github.com/apvarun/toastify-js#api for options | |||
function showToast(message, level, {gravity, position, duration, ...other} = {}) { | |||
function showToast(message, level, {gravity, position, duration, useHtmlBody, ...other} = {}) { | |||
const {icon, background, duration: levelDuration} = levels[level ?? 'info']; | |||
const toast = Toastify({ | |||
text: ` | |||
<div class='toast-icon'>${svg(icon)}</div> | |||
<div class='toast-body'>${htmlEscape(message)}</div> | |||
<div class='toast-body'>${useHtmlBody ? message : htmlEscape(message)}</div> | |||
<button class='toast-close'>${svg('octicon-x')}</button> | |||
`, | |||
escapeMarkup: false, |