0
0
mirror of https://github.com/go-gitea/gitea.git synced 2026-05-14 04:20:42 +02:00

chore: introduce HTMLBuilder (#37688)

This commit is contained in:
wxiaoguang 2026-05-14 01:06:53 +08:00 committed by GitHub
parent 701908a945
commit 523822090c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 47 additions and 12 deletions

View File

@ -83,3 +83,34 @@ func HTMLPrintTag(w io.Writer, tag template.HTML, attrs map[string]string) (writ
written += n
return written, err
}
func EscapeString(s string) template.HTML {
return template.HTML(template.HTMLEscapeString(s))
}
type HTMLBuilder struct {
sb strings.Builder
}
func (b *HTMLBuilder) WriteString(s string) *HTMLBuilder {
b.sb.WriteString(template.HTMLEscapeString(s))
return b
}
func (b *HTMLBuilder) WriteHTML(s template.HTML) *HTMLBuilder {
b.sb.WriteString(string(s))
return b
}
func (b *HTMLBuilder) WriteFormat(fmt template.HTML, args ...any) *HTMLBuilder {
_, _ = HTMLPrintf(&b.sb, fmt, args...)
return b
}
func (b *HTMLBuilder) HTMLString() template.HTML {
return template.HTML(b.sb.String())
}
func (b *HTMLBuilder) String() string {
return b.sb.String()
}

View File

@ -22,3 +22,10 @@ func TestHTMLFormat(t *testing.T) {
assert.Equal(t, template.HTML("&lt;&gt;"), HTMLFormat("%s", template.URL("<>")))
assert.Equal(t, template.HTML("&amp;StringMethod &amp;StringMethod"), HTMLFormat("%s %s", testStringer{}, &testStringer{}))
}
func TestHTMLBuilder(t *testing.T) {
b := &HTMLBuilder{}
b.WriteString("<").WriteHTML("<hr>").WriteFormat("<span>%s%s</span>", ">", EscapeString(">"))
assert.Equal(t, "&lt;<hr><span>&gt;&gt;</span>", b.String())
assert.Equal(t, template.HTML("&lt;<hr><span>&gt;&gt;</span>"), b.HTMLString())
}

View File

@ -77,6 +77,7 @@ func (r *RenderInternal) ProtectSafeAttrs(content template.HTML) template.HTML {
}
func (r *RenderInternal) FormatWithSafeAttrs(w io.Writer, fmt template.HTML, a ...any) error {
_, err := w.Write([]byte(r.ProtectSafeAttrs(htmlutil.HTMLFormat(fmt, a...))))
htmlStr := r.ProtectSafeAttrs(htmlutil.HTMLFormat(fmt, a...))
_, err := io.WriteString(w, string(htmlStr))
return err
}

View File

@ -106,31 +106,27 @@ func (r *orgWriter) resolveLink(link string) string {
// WriteRegularLink renders images, links or videos
func (r *orgWriter) WriteRegularLink(l org.RegularLink) {
link := r.resolveLink(l.URL)
printHTML := func(html template.HTML, a ...any) {
_, _ = fmt.Fprint(r, htmlutil.HTMLFormat(html, a...))
}
// Inspired by https://github.com/niklasfasching/go-org/blob/6eb20dbda93cb88c3503f7508dc78cbbc639378f/org/html_writer.go#L406-L427
switch l.Kind() {
case "image":
if l.Description == nil {
printHTML(`<img src="%s" alt="%s">`, link, link)
_, _ = htmlutil.HTMLPrintf(r, `<img src="%s" alt="%s">`, link, link)
} else {
imageSrc := r.resolveLink(org.String(l.Description...))
printHTML(`<a href="%s"><img src="%s" alt="%s"></a>`, link, imageSrc, imageSrc)
_, _ = htmlutil.HTMLPrintf(r, `<a href="%s"><img src="%s" alt="%s"></a>`, link, imageSrc, imageSrc)
}
case "video":
if l.Description == nil {
printHTML(`<video src="%s">%s</video>`, link, link)
_, _ = htmlutil.HTMLPrintf(r, `<video src="%s">%s</video>`, link, link)
} else {
videoSrc := r.resolveLink(org.String(l.Description...))
printHTML(`<a href="%s"><video src="%s">%s</video></a>`, link, videoSrc, videoSrc)
_, _ = htmlutil.HTMLPrintf(r, `<a href="%s"><video src="%s">%s</video></a>`, link, videoSrc, videoSrc)
}
default:
var description any = link
if l.Description != nil {
description = template.HTML(r.WriteNodesAsString(l.Description...)) // orgmode HTMLWriter outputs HTML content
}
printHTML(`<a href="%s">%s</a>`, link, description)
_, _ = htmlutil.HTMLPrintf(r, `<a href="%s">%s</a>`, link, description)
}
}

View File

@ -260,7 +260,7 @@ func (r *Router) normalizeRequestPath(resp http.ResponseWriter, req *http.Reques
// do not respond to other requests, to simulate a real sub-path environment
resp.Header().Add("Content-Type", "text/html; charset=utf-8")
resp.WriteHeader(http.StatusNotFound)
_, _ = resp.Write([]byte(htmlutil.HTMLFormat(`404 page not found, sub-path is: <a href="%s">%s</a>`, setting.AppSubURL, setting.AppSubURL)))
_, _ = htmlutil.HTMLPrintf(resp, `404 page not found, sub-path is: <a href="%s">%s</a>`, setting.AppSubURL, setting.AppSubURL)
return
}
normalized = true

View File

@ -276,7 +276,7 @@ func handleRepoViewSubmodule(ctx *context.Context, commitSubmoduleFile *git.Comm
redirectLink := submoduleWebLink.CommitWebLink
if isViewHomeOnlyContent(ctx) {
ctx.Resp.Header().Set("Content-Type", "text/html; charset=utf-8")
_, _ = ctx.Resp.Write([]byte(htmlutil.HTMLFormat(`<a href="%s">%s</a>`, redirectLink, redirectLink)))
_, _ = htmlutil.HTMLPrintf(ctx.Resp, `<a href="%s">%s</a>`, redirectLink, redirectLink)
} else if !httplib.IsCurrentGiteaSiteURL(ctx, redirectLink) {
// don't auto-redirect to external URL, to avoid open redirect or phishing
ctx.Data["NotFoundPrompt"] = redirectLink