mirror of
				https://github.com/go-gitea/gitea
				synced 2025-10-31 03:18:24 +00:00 
			
		
		
		
	Make template Iif exactly match if (#31322)
				
					
				
			This commit is contained in:
		| @@ -239,7 +239,7 @@ func DotEscape(raw string) string { | |||||||
| // Iif is an "inline-if", similar util.Iif[T] but templates need the non-generic version, | // Iif is an "inline-if", similar util.Iif[T] but templates need the non-generic version, | ||||||
| // and it could be simply used as "{{Iif expr trueVal}}" (omit the falseVal). | // and it could be simply used as "{{Iif expr trueVal}}" (omit the falseVal). | ||||||
| func Iif(condition any, vals ...any) any { | func Iif(condition any, vals ...any) any { | ||||||
| 	if IsTruthy(condition) { | 	if isTemplateTruthy(condition) { | ||||||
| 		return vals[0] | 		return vals[0] | ||||||
| 	} else if len(vals) > 1 { | 	} else if len(vals) > 1 { | ||||||
| 		return vals[1] | 		return vals[1] | ||||||
| @@ -247,7 +247,7 @@ func Iif(condition any, vals ...any) any { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func IsTruthy(v any) bool { | func isTemplateTruthy(v any) bool { | ||||||
| 	if v == nil { | 	if v == nil { | ||||||
| 		return false | 		return false | ||||||
| 	} | 	} | ||||||
| @@ -256,20 +256,20 @@ func IsTruthy(v any) bool { | |||||||
| 	switch rv.Kind() { | 	switch rv.Kind() { | ||||||
| 	case reflect.Bool: | 	case reflect.Bool: | ||||||
| 		return rv.Bool() | 		return rv.Bool() | ||||||
| 	case reflect.String: |  | ||||||
| 		return rv.String() != "" |  | ||||||
| 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | ||||||
| 		return rv.Int() != 0 | 		return rv.Int() != 0 | ||||||
| 	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: | 	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: | ||||||
| 		return rv.Uint() != 0 | 		return rv.Uint() != 0 | ||||||
| 	case reflect.Float32, reflect.Float64: | 	case reflect.Float32, reflect.Float64: | ||||||
| 		return rv.Float() != 0 | 		return rv.Float() != 0 | ||||||
| 	case reflect.Slice, reflect.Array, reflect.Map: | 	case reflect.Complex64, reflect.Complex128: | ||||||
|  | 		return rv.Complex() != 0 | ||||||
|  | 	case reflect.String, reflect.Slice, reflect.Array, reflect.Map: | ||||||
| 		return rv.Len() > 0 | 		return rv.Len() > 0 | ||||||
| 	case reflect.Ptr: | 	case reflect.Struct: | ||||||
| 		return !rv.IsNil() && IsTruthy(reflect.Indirect(rv).Interface()) | 		return true | ||||||
| 	default: | 	default: | ||||||
| 		return rv.Kind() == reflect.Struct && !rv.IsNil() | 		return !rv.IsNil() | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -5,8 +5,11 @@ package templates | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"html/template" | 	"html/template" | ||||||
|  | 	"strings" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	"code.gitea.io/gitea/modules/util" | ||||||
|  |  | ||||||
| 	"github.com/stretchr/testify/assert" | 	"github.com/stretchr/testify/assert" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -66,17 +69,40 @@ func TestSanitizeHTML(t *testing.T) { | |||||||
| 	assert.Equal(t, template.HTML(`<a href="/" rel="nofollow">link</a> xss <div>inline</div>`), SanitizeHTML(`<a href="/">link</a> <a href="javascript:">xss</a> <div style="dangerous">inline</div>`)) | 	assert.Equal(t, template.HTML(`<a href="/" rel="nofollow">link</a> xss <div>inline</div>`), SanitizeHTML(`<a href="/">link</a> <a href="javascript:">xss</a> <div style="dangerous">inline</div>`)) | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestIsTruthy(t *testing.T) { | func TestTemplateTruthy(t *testing.T) { | ||||||
| 	var test any | 	tmpl := template.New("test") | ||||||
| 	assert.Equal(t, false, IsTruthy(test)) | 	tmpl.Funcs(template.FuncMap{"Iif": Iif}) | ||||||
| 	assert.Equal(t, false, IsTruthy(nil)) | 	template.Must(tmpl.Parse(`{{if .Value}}true{{else}}false{{end}}:{{Iif .Value "true" "false"}}`)) | ||||||
| 	assert.Equal(t, false, IsTruthy("")) |  | ||||||
| 	assert.Equal(t, true, IsTruthy("non-empty")) | 	cases := []any{ | ||||||
| 	assert.Equal(t, true, IsTruthy(-1)) | 		nil, false, true, "", "string", 0, 1, | ||||||
| 	assert.Equal(t, false, IsTruthy(0)) | 		byte(0), byte(1), int64(0), int64(1), float64(0), float64(1), | ||||||
| 	assert.Equal(t, true, IsTruthy(42)) | 		complex(0, 0), complex(1, 0), | ||||||
| 	assert.Equal(t, false, IsTruthy(0.0)) | 		(chan int)(nil), make(chan int), | ||||||
| 	assert.Equal(t, true, IsTruthy(3.14)) | 		(func())(nil), func() {}, | ||||||
| 	assert.Equal(t, false, IsTruthy([]int{})) | 		util.ToPointer(0), util.ToPointer(util.ToPointer(0)), | ||||||
| 	assert.Equal(t, true, IsTruthy([]int{1})) | 		util.ToPointer(1), util.ToPointer(util.ToPointer(1)), | ||||||
|  | 		[0]int{}, | ||||||
|  | 		[1]int{0}, | ||||||
|  | 		[]int(nil), | ||||||
|  | 		[]int{}, | ||||||
|  | 		[]int{0}, | ||||||
|  | 		map[any]any(nil), | ||||||
|  | 		map[any]any{}, | ||||||
|  | 		map[any]any{"k": "v"}, | ||||||
|  | 		(*struct{})(nil), | ||||||
|  | 		struct{}{}, | ||||||
|  | 		util.ToPointer(struct{}{}), | ||||||
|  | 	} | ||||||
|  | 	w := &strings.Builder{} | ||||||
|  | 	truthyCount := 0 | ||||||
|  | 	for i, v := range cases { | ||||||
|  | 		w.Reset() | ||||||
|  | 		assert.NoError(t, tmpl.Execute(w, struct{ Value any }{v}), "case %d (%T) %#v fails", i, v, v) | ||||||
|  | 		out := w.String() | ||||||
|  | 		truthyCount += util.Iif(out == "true:true", 1, 0) | ||||||
|  | 		truthyMatches := out == "true:true" || out == "false:false" | ||||||
|  | 		assert.True(t, truthyMatches, "case %d (%T) %#v fail: %s", i, v, v, out) | ||||||
|  | 	} | ||||||
|  | 	assert.True(t, truthyCount != 0 && truthyCount != len(cases)) | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user