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
					
				
			
		
					 11 changed files with 42 additions and 39 deletions
				
			
		
							
								
								
									
										4
									
								
								Makefile
									
										
									
									
									
								
							
							
						
						
									
										4
									
								
								Makefile
									
										
									
									
									
								
							|  | @ -156,8 +156,8 @@ endif | |||
| FORGEJO_API_SPEC := public/assets/forgejo/api.v1.yml | ||||
| 
 | ||||
| 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_JSON := s|"basePath": *"{{AppSubUrl \| JSEscape \| Safe}}/api/v1"|"basePath": "/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}}/api/v1"|"basePath": "/api/v1"|g | ||||
| SWAGGER_EXCLUDE := code.gitea.io/sdk | ||||
| SWAGGER_NEWLINE_COMMAND := -e '$$a\' | ||||
| 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 | ||||
| func (ctx *Context) RenderToString(name base.TplName, data map[string]any) (string, error) { | ||||
| 	var buf strings.Builder | ||||
|  |  | |||
|  | @ -38,7 +38,7 @@ func NewFuncMap() template.FuncMap { | |||
| 		"Safe":        Safe, | ||||
| 		"Escape":      Escape, | ||||
| 		"QueryEscape": url.QueryEscape, | ||||
| 		"JSEscape":    template.JSEscapeString, | ||||
| 		"JSEscape":    JSEscapeSafe, | ||||
| 		"Str2html":    Str2html, // TODO: rename it to SanitizeHTML | ||||
| 		"URLJoin":     util.URLJoin, | ||||
| 		"DotEscape":   DotEscape, | ||||
|  | @ -214,6 +214,10 @@ func Escape(s any) template.HTML { | |||
| 	panic(fmt.Sprintf("unexpected type %T", s)) | ||||
| } | ||||
| 
 | ||||
| func JSEscapeSafe(s string) template.HTML { | ||||
| 	return template.HTML(template.JSEscapeString(s)) | ||||
| } | ||||
| 
 | ||||
| func RenderEmojiPlain(s any) any { | ||||
| 	switch v := s.(type) { | ||||
| 	case string: | ||||
|  |  | |||
|  | @ -52,3 +52,7 @@ func TestSubjectBodySeparator(t *testing.T) { | |||
| 		"", | ||||
| 		"Insuficient\n--\nSeparators") | ||||
| } | ||||
| 
 | ||||
| func TestJSEscapeSafe(t *testing.T) { | ||||
| 	assert.EqualValues(t, `\u0026\u003C\u003E\'\"`, JSEscapeSafe(`&<>'"`)) | ||||
| } | ||||
|  |  | |||
|  | @ -8,7 +8,7 @@ | |||
| // | ||||
| //	Schemes: https, http | ||||
| //	BasePath: /api/v1 | ||||
| //	Version: {{AppVer | JSEscape | Safe}} | ||||
| //	Version: {{AppVer | JSEscape}} | ||||
| //	License: MIT http://opensource.org/licenses/MIT | ||||
| // | ||||
| //	Consumes: | ||||
|  |  | |||
|  | @ -579,16 +579,8 @@ func GrantApplicationOAuth(ctx *context.Context) { | |||
| 
 | ||||
| // OIDCWellKnown generates JSON so OIDC clients know Gitea's capabilities | ||||
| 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 | ||||
| 	if err = t.Execute(ctx.Resp, ctx.Data); err != nil { | ||||
| 		ctx.ServerError("unable to execute template", err) | ||||
| 	} | ||||
| 	ctx.JSONTemplate("user/auth/oidc_wellknown") | ||||
| } | ||||
| 
 | ||||
| // OIDCKeys generates the JSON Web Key Set | ||||
|  |  | |||
|  | @ -4,6 +4,8 @@ | |||
| package user | ||||
| 
 | ||||
| import ( | ||||
| 	"net/url" | ||||
| 
 | ||||
| 	"code.gitea.io/gitea/models/db" | ||||
| 	"code.gitea.io/gitea/models/organization" | ||||
| 	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["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["UserLocationMapURL"] = setting.Service.UserLocationMapURL | ||||
| 	ctx.Data["ContextUserLocationMapURL"] = setting.Service.UserLocationMapURL + url.QueryEscape(ctx.ContextUser.Location) | ||||
| 
 | ||||
| 	// Show OpenID URIs | ||||
| 	openIDs, err := user_model.GetUserOpenIDs(ctx, ctx.ContextUser.ID) | ||||
|  |  | |||
|  | @ -4,22 +4,10 @@ | |||
| package web | ||||
| 
 | ||||
| import ( | ||||
| 	"code.gitea.io/gitea/modules/base" | ||||
| 	"code.gitea.io/gitea/modules/context" | ||||
| ) | ||||
| 
 | ||||
| // tplSwaggerV1Json swagger v1 json template | ||||
| const tplSwaggerV1Json base.TplName = "swagger/v1_json" | ||||
| 
 | ||||
| // SwaggerV1Json render swagger v1 json | ||||
| func SwaggerV1Json(ctx *context.Context) { | ||||
| 	t, err := ctx.Render.TemplateLookup(string(tplSwaggerV1Json), 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) | ||||
| 	} | ||||
| 	ctx.JSONTemplate("swagger/v1_json") | ||||
| } | ||||
|  |  | |||
|  | @ -31,9 +31,8 @@ | |||
| 				<li> | ||||
| 					{{svg "octicon-location"}} | ||||
| 					<span class="gt-f1">{{.ContextUser.Location}}</span> | ||||
| 					{{if .UserLocationMapURL}} | ||||
| 						{{/* We presume that the UserLocationMapURL is safe, as it is provided by the site administrator. */}} | ||||
| 						<a href="{{.UserLocationMapURL | Safe}}{{.ContextUser.Location | QueryEscape}}" rel="nofollow noreferrer" data-tooltip-content="{{ctx.Locale.Tr "user.show_on_map"}}"> | ||||
| 					{{if .ContextUserLocationMapURL}} | ||||
| 						<a href="{{.ContextUserLocationMapURL}}" rel="nofollow noreferrer" data-tooltip-content="{{ctx.Locale.Tr "user.show_on_map"}}"> | ||||
| 							{{svg "octicon-link-external"}} | ||||
| 						</a> | ||||
| 					{{end}} | ||||
|  |  | |||
							
								
								
									
										4
									
								
								templates/swagger/v1_json.tmpl
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										4
									
								
								templates/swagger/v1_json.tmpl
									
										
									
										generated
									
									
									
								
							|  | @ -19,9 +19,9 @@ | |||
|       "name": "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": { | ||||
|     "/activitypub/user-id/{user-id}": { | ||||
|       "get": { | ||||
|  |  | |||
|  | @ -1,16 +1,16 @@ | |||
| { | ||||
|     "issuer": "{{AppUrl | JSEscape | Safe}}", | ||||
|     "authorization_endpoint": "{{AppUrl | JSEscape | Safe}}login/oauth/authorize", | ||||
|     "token_endpoint": "{{AppUrl | JSEscape | Safe}}login/oauth/access_token", | ||||
|     "jwks_uri": "{{AppUrl | JSEscape | Safe}}login/oauth/keys", | ||||
|     "userinfo_endpoint": "{{AppUrl | JSEscape | Safe}}login/oauth/userinfo", | ||||
|     "introspection_endpoint": "{{AppUrl | JSEscape | Safe}}login/oauth/introspect", | ||||
|     "issuer": "{{AppUrl | JSEscape}}", | ||||
|     "authorization_endpoint": "{{AppUrl | JSEscape}}login/oauth/authorize", | ||||
|     "token_endpoint": "{{AppUrl | JSEscape}}login/oauth/access_token", | ||||
|     "jwks_uri": "{{AppUrl | JSEscape}}login/oauth/keys", | ||||
|     "userinfo_endpoint": "{{AppUrl | JSEscape}}login/oauth/userinfo", | ||||
|     "introspection_endpoint": "{{AppUrl | JSEscape}}login/oauth/introspect", | ||||
|     "response_types_supported": [ | ||||
|         "code", | ||||
|         "id_token" | ||||
|     ], | ||||
|     "id_token_signing_alg_values_supported": [ | ||||
|         "{{.SigningKey.SigningMethod.Alg | JSEscape | Safe}}" | ||||
|         "{{.SigningKey.SigningMethod.Alg | JSEscape}}" | ||||
|     ], | ||||
|     "subject_types_supported": [ | ||||
|         "public" | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 wxiaoguang
				wxiaoguang