package types import ( "fmt" "html" "html/template" "strings" "github.com/GoAdminGroup/go-admin/context" "github.com/GoAdminGroup/go-admin/modules/config" "github.com/GoAdminGroup/go-admin/template/types/form" "golang.org/x/text/cases" "golang.org/x/text/language" ) type DisplayFnGenerator interface { Get(ctx *context.Context, args ...interface{}) FieldFilterFn JS() template.HTML HTML() template.HTML } type BaseDisplayFnGenerator struct{} func (base *BaseDisplayFnGenerator) JS() template.HTML { return "" } func (base *BaseDisplayFnGenerator) HTML() template.HTML { return "" } var displayFnGens = make(map[string]DisplayFnGenerator) func RegisterDisplayFnGenerator(key string, gen DisplayFnGenerator) { if _, ok := displayFnGens[key]; ok { panic("display function generator has been registered") } displayFnGens[key] = gen } type FieldDisplay struct { Display FieldFilterFn DisplayProcessChains DisplayProcessFnChains } func (f FieldDisplay) ToDisplay(value FieldModel) interface{} { val := f.Display(value) if len(f.DisplayProcessChains) > 0 && f.IsNotSelectRes(val) { valStr := fmt.Sprintf("%v", val) for _, process := range f.DisplayProcessChains { valStr = fmt.Sprintf("%v", process(FieldModel{ Row: value.Row, Value: valStr, ID: value.ID, })) } return valStr } return val } func (f FieldDisplay) IsNotSelectRes(v interface{}) bool { switch v.(type) { case template.HTML: return false case []string: return false case [][]string: return false default: return true } } func (f FieldDisplay) ToDisplayHTML(value FieldModel) template.HTML { v := f.ToDisplay(value) if h, ok := v.(template.HTML); ok { return h } else if s, ok := v.(string); ok { return template.HTML(s) } else if arr, ok := v.([]string); ok && len(arr) > 0 { return template.HTML(arr[0]) } else if arr, ok := v.([]template.HTML); ok && len(arr) > 0 { return arr[0] } else if v != nil { return template.HTML(fmt.Sprintf("%v", v)) } else { return "" } } func (f FieldDisplay) ToDisplayString(value FieldModel) string { v := f.ToDisplay(value) if h, ok := v.(template.HTML); ok { return string(h) } else if s, ok := v.(string); ok { return s } else if arr, ok := v.([]string); ok && len(arr) > 0 { return arr[0] } else if arr, ok := v.([]template.HTML); ok && len(arr) > 0 { return string(arr[0]) } else if v != nil { return fmt.Sprintf("%v", v) } else { return "" } } func (f FieldDisplay) ToDisplayStringArray(value FieldModel) []string { v := f.ToDisplay(value) if h, ok := v.(template.HTML); ok { return []string{string(h)} } else if s, ok := v.(string); ok { return []string{s} } else if arr, ok := v.([]string); ok && len(arr) > 0 { return arr } else if arr, ok := v.([]template.HTML); ok && len(arr) > 0 { ss := make([]string, len(arr)) for k, a := range arr { ss[k] = string(a) } return ss } else if v != nil { return []string{fmt.Sprintf("%v", v)} } else { return []string{} } } func (f FieldDisplay) ToDisplayStringArrayArray(value FieldModel) [][]string { v := f.ToDisplay(value) if h, ok := v.(template.HTML); ok { return [][]string{{string(h)}} } else if s, ok := v.(string); ok { return [][]string{{s}} } else if arr, ok := v.([]string); ok && len(arr) > 0 { return [][]string{arr} } else if arr, ok := v.([][]string); ok && len(arr) > 0 { return arr } else if arr, ok := v.([]template.HTML); ok && len(arr) > 0 { ss := make([]string, len(arr)) for k, a := range arr { ss[k] = string(a) } return [][]string{ss} } else if v != nil { return [][]string{{fmt.Sprintf("%v", v)}} } else { return [][]string{} } } func (f FieldDisplay) AddLimit(limit int) DisplayProcessFnChains { return f.DisplayProcessChains.Add(func(value FieldModel) interface{} { if limit > len(value.Value) { return value.Value } else if limit < 0 { return "" } else { return value.Value[:limit] } }) } func (f FieldDisplay) AddTrimSpace() DisplayProcessFnChains { return f.DisplayProcessChains.Add(func(value FieldModel) interface{} { return strings.TrimSpace(value.Value) }) } func (f FieldDisplay) AddSubstr(start int, end int) DisplayProcessFnChains { return f.DisplayProcessChains.Add(func(value FieldModel) interface{} { if start > end || start > len(value.Value) || end < 0 { return "" } if start < 0 { start = 0 } if end > len(value.Value) { end = len(value.Value) } return value.Value[start:end] }) } func (f FieldDisplay) AddToTitle() DisplayProcessFnChains { return f.DisplayProcessChains.Add(func(value FieldModel) interface{} { return cases.Title(language.Und).String(value.Value) }) } func (f FieldDisplay) AddToUpper() DisplayProcessFnChains { return f.DisplayProcessChains.Add(func(value FieldModel) interface{} { return strings.ToUpper(value.Value) }) } func (f FieldDisplay) AddToLower() DisplayProcessFnChains { return f.DisplayProcessChains.Add(func(value FieldModel) interface{} { return strings.ToLower(value.Value) }) } type DisplayProcessFnChains []FieldFilterFn func (d DisplayProcessFnChains) Valid() bool { return len(d) > 0 } func (d DisplayProcessFnChains) Add(f FieldFilterFn) DisplayProcessFnChains { return append(d, f) } func (d DisplayProcessFnChains) Append(f DisplayProcessFnChains) DisplayProcessFnChains { return append(d, f...) } func (d DisplayProcessFnChains) Copy() DisplayProcessFnChains { if len(d) == 0 { return make(DisplayProcessFnChains, 0) } else { var newDisplayProcessFnChains = make(DisplayProcessFnChains, len(d)) copy(newDisplayProcessFnChains, d) return newDisplayProcessFnChains } } func chooseDisplayProcessChains(internal DisplayProcessFnChains) DisplayProcessFnChains { if len(internal) > 0 { return internal } return globalDisplayProcessChains.Copy() } var globalDisplayProcessChains = make(DisplayProcessFnChains, 0) func AddGlobalDisplayProcessFn(f FieldFilterFn) { globalDisplayProcessChains = globalDisplayProcessChains.Add(f) } func AddLimit(limit int) DisplayProcessFnChains { return addLimit(limit, globalDisplayProcessChains) } func AddTrimSpace() DisplayProcessFnChains { return addTrimSpace(globalDisplayProcessChains) } func AddSubstr(start int, end int) DisplayProcessFnChains { return addSubstr(start, end, globalDisplayProcessChains) } func AddToTitle() DisplayProcessFnChains { return addToTitle(globalDisplayProcessChains) } func AddToUpper() DisplayProcessFnChains { return addToUpper(globalDisplayProcessChains) } func AddToLower() DisplayProcessFnChains { return addToLower(globalDisplayProcessChains) } func AddXssFilter() DisplayProcessFnChains { return addXssFilter(globalDisplayProcessChains) } func AddXssJsFilter() DisplayProcessFnChains { return addXssJsFilter(globalDisplayProcessChains) } func addLimit(limit int, chains DisplayProcessFnChains) DisplayProcessFnChains { chains = chains.Add(func(value FieldModel) interface{} { if limit > len(value.Value) { return value } else if limit < 0 { return "" } else { return value.Value[:limit] } }) return chains } func addTrimSpace(chains DisplayProcessFnChains) DisplayProcessFnChains { chains = chains.Add(func(value FieldModel) interface{} { return strings.TrimSpace(value.Value) }) return chains } func addSubstr(start int, end int, chains DisplayProcessFnChains) DisplayProcessFnChains { chains = chains.Add(func(value FieldModel) interface{} { if start > end || start > len(value.Value) || end < 0 { return "" } if start < 0 { start = 0 } if end > len(value.Value) { end = len(value.Value) } return value.Value[start:end] }) return chains } func addToTitle(chains DisplayProcessFnChains) DisplayProcessFnChains { chains = chains.Add(func(value FieldModel) interface{} { return cases.Title(language.Und).String(value.Value) }) return chains } func addToUpper(chains DisplayProcessFnChains) DisplayProcessFnChains { chains = chains.Add(func(value FieldModel) interface{} { return strings.ToUpper(value.Value) }) return chains } func addToLower(chains DisplayProcessFnChains) DisplayProcessFnChains { chains = chains.Add(func(value FieldModel) interface{} { return strings.ToLower(value.Value) }) return chains } func addXssFilter(chains DisplayProcessFnChains) DisplayProcessFnChains { chains = chains.Add(func(value FieldModel) interface{} { return html.EscapeString(value.Value) }) return chains } func addXssJsFilter(chains DisplayProcessFnChains) DisplayProcessFnChains { chains = chains.Add(func(value FieldModel) interface{} { replacer := strings.NewReplacer("", "</script>") return replacer.Replace(value.Value) }) return chains } func setDefaultDisplayFnOfFormType(f *FormPanel, typ form.Type) { if typ.IsMultiFile() { f.FieldList[f.curFieldListIndex].Display = func(value FieldModel) interface{} { if value.Value == "" { return "" } arr := strings.Split(value.Value, ",") res := "[" for i, item := range arr { if i == len(arr)-1 { res += "'" + config.GetStore().URL(item) + "']" } else { res += "'" + config.GetStore().URL(item) + "'," } } return res } } if typ.IsSelect() { f.FieldList[f.curFieldListIndex].Display = func(value FieldModel) interface{} { return strings.Split(value.Value, ",") } } }