Refactor more code in templates (#29236)
Follow #29165. * Introduce JSONTemplate to help to render JSON templates * Introduce JSEscapeSafe for templates. Now only use `{{ ... | JSEscape}}` instead of `{{ ... | JSEscape | Safe}}` * Simplify "UserLocationMapURL" useage (cherry picked from commit 31bb9f3247388b993c61a10190cfd512408ce57e)
This commit is contained in:
parent
bdf470785d
commit
81925ebb0c
4
Makefile
4
Makefile
|
@ -156,8 +156,8 @@ endif
|
||||||
FORGEJO_API_SPEC := public/assets/forgejo/api.v1.yml
|
FORGEJO_API_SPEC := public/assets/forgejo/api.v1.yml
|
||||||
|
|
||||||
SWAGGER_SPEC := templates/swagger/v1_json.tmpl
|
SWAGGER_SPEC := templates/swagger/v1_json.tmpl
|
||||||
SWAGGER_SPEC_S_TMPL := s|"basePath": *"/api/v1"|"basePath": "{{AppSubUrl \| JSEscape \| Safe}}/api/v1"|g
|
SWAGGER_SPEC_S_TMPL := s|"basePath": *"/api/v1"|"basePath": "{{AppSubUrl \| JSEscape}}/api/v1"|g
|
||||||
SWAGGER_SPEC_S_JSON := s|"basePath": *"{{AppSubUrl \| JSEscape \| Safe}}/api/v1"|"basePath": "/api/v1"|g
|
SWAGGER_SPEC_S_JSON := s|"basePath": *"{{AppSubUrl \| JSEscape}}/api/v1"|"basePath": "/api/v1"|g
|
||||||
SWAGGER_EXCLUDE := code.gitea.io/sdk
|
SWAGGER_EXCLUDE := code.gitea.io/sdk
|
||||||
SWAGGER_NEWLINE_COMMAND := -e '$$a\'
|
SWAGGER_NEWLINE_COMMAND := -e '$$a\'
|
||||||
SWAGGER_SPEC_BRANDING := s|Gitea API|Forgejo API|g
|
SWAGGER_SPEC_BRANDING := s|Gitea API|Forgejo API|g
|
||||||
|
|
|
@ -90,6 +90,20 @@ func (ctx *Context) HTML(status int, name base.TplName) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// JSONTemplate renders the template as JSON response
|
||||||
|
// keep in mind that the template is processed in HTML context, so JSON-things should be handled carefully, eg: by JSEscape
|
||||||
|
func (ctx *Context) JSONTemplate(tmpl base.TplName) {
|
||||||
|
t, err := ctx.Render.TemplateLookup(string(tmpl), nil)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("unable to find template", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.Resp.Header().Set("Content-Type", "application/json")
|
||||||
|
if err = t.Execute(ctx.Resp, ctx.Data); err != nil {
|
||||||
|
ctx.ServerError("unable to execute template", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// RenderToString renders the template content to a string
|
// RenderToString renders the template content to a string
|
||||||
func (ctx *Context) RenderToString(name base.TplName, data map[string]any) (string, error) {
|
func (ctx *Context) RenderToString(name base.TplName, data map[string]any) (string, error) {
|
||||||
var buf strings.Builder
|
var buf strings.Builder
|
||||||
|
|
|
@ -38,7 +38,7 @@ func NewFuncMap() template.FuncMap {
|
||||||
"Safe": Safe,
|
"Safe": Safe,
|
||||||
"Escape": Escape,
|
"Escape": Escape,
|
||||||
"QueryEscape": url.QueryEscape,
|
"QueryEscape": url.QueryEscape,
|
||||||
"JSEscape": template.JSEscapeString,
|
"JSEscape": JSEscapeSafe,
|
||||||
"Str2html": Str2html, // TODO: rename it to SanitizeHTML
|
"Str2html": Str2html, // TODO: rename it to SanitizeHTML
|
||||||
"URLJoin": util.URLJoin,
|
"URLJoin": util.URLJoin,
|
||||||
"DotEscape": DotEscape,
|
"DotEscape": DotEscape,
|
||||||
|
@ -214,6 +214,10 @@ func Escape(s any) template.HTML {
|
||||||
panic(fmt.Sprintf("unexpected type %T", s))
|
panic(fmt.Sprintf("unexpected type %T", s))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func JSEscapeSafe(s string) template.HTML {
|
||||||
|
return template.HTML(template.JSEscapeString(s))
|
||||||
|
}
|
||||||
|
|
||||||
func RenderEmojiPlain(s any) any {
|
func RenderEmojiPlain(s any) any {
|
||||||
switch v := s.(type) {
|
switch v := s.(type) {
|
||||||
case string:
|
case string:
|
||||||
|
|
|
@ -52,3 +52,7 @@ func TestSubjectBodySeparator(t *testing.T) {
|
||||||
"",
|
"",
|
||||||
"Insuficient\n--\nSeparators")
|
"Insuficient\n--\nSeparators")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestJSEscapeSafe(t *testing.T) {
|
||||||
|
assert.EqualValues(t, `\u0026\u003C\u003E\'\"`, JSEscapeSafe(`&<>'"`))
|
||||||
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
//
|
//
|
||||||
// Schemes: https, http
|
// Schemes: https, http
|
||||||
// BasePath: /api/v1
|
// BasePath: /api/v1
|
||||||
// Version: {{AppVer | JSEscape | Safe}}
|
// Version: {{AppVer | JSEscape}}
|
||||||
// License: MIT http://opensource.org/licenses/MIT
|
// License: MIT http://opensource.org/licenses/MIT
|
||||||
//
|
//
|
||||||
// Consumes:
|
// Consumes:
|
||||||
|
|
|
@ -579,16 +579,8 @@ func GrantApplicationOAuth(ctx *context.Context) {
|
||||||
|
|
||||||
// OIDCWellKnown generates JSON so OIDC clients know Gitea's capabilities
|
// OIDCWellKnown generates JSON so OIDC clients know Gitea's capabilities
|
||||||
func OIDCWellKnown(ctx *context.Context) {
|
func OIDCWellKnown(ctx *context.Context) {
|
||||||
t, err := ctx.Render.TemplateLookup("user/auth/oidc_wellknown", nil)
|
|
||||||
if err != nil {
|
|
||||||
ctx.ServerError("unable to find template", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ctx.Resp.Header().Set("Content-Type", "application/json")
|
|
||||||
ctx.Data["SigningKey"] = oauth2.DefaultSigningKey
|
ctx.Data["SigningKey"] = oauth2.DefaultSigningKey
|
||||||
if err = t.Execute(ctx.Resp, ctx.Data); err != nil {
|
ctx.JSONTemplate("user/auth/oidc_wellknown")
|
||||||
ctx.ServerError("unable to execute template", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// OIDCKeys generates the JSON Web Key Set
|
// OIDCKeys generates the JSON Web Key Set
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
package user
|
package user
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/url"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
"code.gitea.io/gitea/models/organization"
|
"code.gitea.io/gitea/models/organization"
|
||||||
access_model "code.gitea.io/gitea/models/perm/access"
|
access_model "code.gitea.io/gitea/models/perm/access"
|
||||||
|
@ -37,7 +39,7 @@ func PrepareContextForProfileBigAvatar(ctx *context.Context) {
|
||||||
ctx.Data["IsFollowing"] = ctx.Doer != nil && user_model.IsFollowing(ctx, ctx.Doer.ID, ctx.ContextUser.ID)
|
ctx.Data["IsFollowing"] = ctx.Doer != nil && user_model.IsFollowing(ctx, ctx.Doer.ID, ctx.ContextUser.ID)
|
||||||
ctx.Data["IsBlocked"] = ctx.Doer != nil && user_model.IsBlocked(ctx, ctx.Doer.ID, ctx.ContextUser.ID)
|
ctx.Data["IsBlocked"] = ctx.Doer != nil && user_model.IsBlocked(ctx, ctx.Doer.ID, ctx.ContextUser.ID)
|
||||||
ctx.Data["ShowUserEmail"] = setting.UI.ShowUserEmail && ctx.ContextUser.Email != "" && ctx.IsSigned && !ctx.ContextUser.KeepEmailPrivate
|
ctx.Data["ShowUserEmail"] = setting.UI.ShowUserEmail && ctx.ContextUser.Email != "" && ctx.IsSigned && !ctx.ContextUser.KeepEmailPrivate
|
||||||
ctx.Data["UserLocationMapURL"] = setting.Service.UserLocationMapURL
|
ctx.Data["ContextUserLocationMapURL"] = setting.Service.UserLocationMapURL + url.QueryEscape(ctx.ContextUser.Location)
|
||||||
|
|
||||||
// Show OpenID URIs
|
// Show OpenID URIs
|
||||||
openIDs, err := user_model.GetUserOpenIDs(ctx, ctx.ContextUser.ID)
|
openIDs, err := user_model.GetUserOpenIDs(ctx, ctx.ContextUser.ID)
|
||||||
|
|
|
@ -4,22 +4,10 @@
|
||||||
package web
|
package web
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.gitea.io/gitea/modules/base"
|
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
// tplSwaggerV1Json swagger v1 json template
|
|
||||||
const tplSwaggerV1Json base.TplName = "swagger/v1_json"
|
|
||||||
|
|
||||||
// SwaggerV1Json render swagger v1 json
|
// SwaggerV1Json render swagger v1 json
|
||||||
func SwaggerV1Json(ctx *context.Context) {
|
func SwaggerV1Json(ctx *context.Context) {
|
||||||
t, err := ctx.Render.TemplateLookup(string(tplSwaggerV1Json), nil)
|
ctx.JSONTemplate("swagger/v1_json")
|
||||||
if err != nil {
|
|
||||||
ctx.ServerError("unable to find template", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ctx.Resp.Header().Set("Content-Type", "application/json")
|
|
||||||
if err = t.Execute(ctx.Resp, ctx.Data); err != nil {
|
|
||||||
ctx.ServerError("unable to execute template", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,9 +31,8 @@
|
||||||
<li>
|
<li>
|
||||||
{{svg "octicon-location"}}
|
{{svg "octicon-location"}}
|
||||||
<span class="gt-f1">{{.ContextUser.Location}}</span>
|
<span class="gt-f1">{{.ContextUser.Location}}</span>
|
||||||
{{if .UserLocationMapURL}}
|
{{if .ContextUserLocationMapURL}}
|
||||||
{{/* We presume that the UserLocationMapURL is safe, as it is provided by the site administrator. */}}
|
<a href="{{.ContextUserLocationMapURL}}" rel="nofollow noreferrer" data-tooltip-content="{{ctx.Locale.Tr "user.show_on_map"}}">
|
||||||
<a href="{{.UserLocationMapURL | Safe}}{{.ContextUser.Location | QueryEscape}}" rel="nofollow noreferrer" data-tooltip-content="{{ctx.Locale.Tr "user.show_on_map"}}">
|
|
||||||
{{svg "octicon-link-external"}}
|
{{svg "octicon-link-external"}}
|
||||||
</a>
|
</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
|
@ -19,9 +19,9 @@
|
||||||
"name": "MIT",
|
"name": "MIT",
|
||||||
"url": "http://opensource.org/licenses/MIT"
|
"url": "http://opensource.org/licenses/MIT"
|
||||||
},
|
},
|
||||||
"version": "{{AppVer | JSEscape | Safe}}"
|
"version": "{{AppVer | JSEscape}}"
|
||||||
},
|
},
|
||||||
"basePath": "{{AppSubUrl | JSEscape | Safe}}/api/v1",
|
"basePath": "{{AppSubUrl | JSEscape}}/api/v1",
|
||||||
"paths": {
|
"paths": {
|
||||||
"/activitypub/user-id/{user-id}": {
|
"/activitypub/user-id/{user-id}": {
|
||||||
"get": {
|
"get": {
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
{
|
{
|
||||||
"issuer": "{{AppUrl | JSEscape | Safe}}",
|
"issuer": "{{AppUrl | JSEscape}}",
|
||||||
"authorization_endpoint": "{{AppUrl | JSEscape | Safe}}login/oauth/authorize",
|
"authorization_endpoint": "{{AppUrl | JSEscape}}login/oauth/authorize",
|
||||||
"token_endpoint": "{{AppUrl | JSEscape | Safe}}login/oauth/access_token",
|
"token_endpoint": "{{AppUrl | JSEscape}}login/oauth/access_token",
|
||||||
"jwks_uri": "{{AppUrl | JSEscape | Safe}}login/oauth/keys",
|
"jwks_uri": "{{AppUrl | JSEscape}}login/oauth/keys",
|
||||||
"userinfo_endpoint": "{{AppUrl | JSEscape | Safe}}login/oauth/userinfo",
|
"userinfo_endpoint": "{{AppUrl | JSEscape}}login/oauth/userinfo",
|
||||||
"introspection_endpoint": "{{AppUrl | JSEscape | Safe}}login/oauth/introspect",
|
"introspection_endpoint": "{{AppUrl | JSEscape}}login/oauth/introspect",
|
||||||
"response_types_supported": [
|
"response_types_supported": [
|
||||||
"code",
|
"code",
|
||||||
"id_token"
|
"id_token"
|
||||||
],
|
],
|
||||||
"id_token_signing_alg_values_supported": [
|
"id_token_signing_alg_values_supported": [
|
||||||
"{{.SigningKey.SigningMethod.Alg | JSEscape | Safe}}"
|
"{{.SigningKey.SigningMethod.Alg | JSEscape}}"
|
||||||
],
|
],
|
||||||
"subject_types_supported": [
|
"subject_types_supported": [
|
||||||
"public"
|
"public"
|
||||||
|
|
Loading…
Reference in New Issue