loading up the forgejo repo on tangled to test page performance
at forgejo 172 lines 5.1 kB view raw
1// Copyright 2014 The Gogs Authors. All rights reserved. 2// Copyright 2019 The Gitea Authors. All rights reserved. 3// SPDX-License-Identifier: MIT 4 5package middleware 6 7import ( 8 "reflect" 9 "strings" 10 11 "forgejo.org/modules/setting" 12 "forgejo.org/modules/translation" 13 "forgejo.org/modules/util" 14 "forgejo.org/modules/validation" 15 16 "code.forgejo.org/go-chi/binding" 17) 18 19// Form form binding interface 20type Form interface { 21 binding.Validator 22} 23 24func init() { 25 binding.SetNameMapper(util.ToSnakeCase) 26} 27 28// AssignForm assign form values back to the template data. 29func AssignForm(form any, data map[string]any) { 30 typ := reflect.TypeOf(form) 31 val := reflect.ValueOf(form) 32 33 for typ.Kind() == reflect.Ptr { 34 typ = typ.Elem() 35 val = val.Elem() 36 } 37 38 for i := 0; i < typ.NumField(); i++ { 39 field := typ.Field(i) 40 41 fieldName := field.Tag.Get("form") 42 // Allow ignored fields in the struct 43 if fieldName == "-" { 44 continue 45 } else if len(fieldName) == 0 { 46 fieldName = util.ToSnakeCase(field.Name) 47 } 48 49 data[fieldName] = val.Field(i).Interface() 50 } 51} 52 53func getRuleBody(field reflect.StructField, prefix string) string { 54 for _, rule := range strings.Split(field.Tag.Get("binding"), ";") { 55 if strings.HasPrefix(rule, prefix) { 56 return rule[len(prefix) : len(rule)-1] 57 } 58 } 59 return "" 60} 61 62// GetSize get size int form tag 63func GetSize(field reflect.StructField) string { 64 return getRuleBody(field, "Size(") 65} 66 67// GetMinSize get minimal size in form tag 68func GetMinSize(field reflect.StructField) string { 69 return getRuleBody(field, "MinSize(") 70} 71 72// GetMaxSize get max size in form tag 73func GetMaxSize(field reflect.StructField) string { 74 return getRuleBody(field, "MaxSize(") 75} 76 77// GetInclude get include in form tag 78func GetInclude(field reflect.StructField) string { 79 return getRuleBody(field, "Include(") 80} 81 82func GetRange(field reflect.StructField) (string, string) { 83 min, max, _ := strings.Cut(getRuleBody(field, "Range("), ",") 84 return min, max 85} 86 87// Validate populates the data with validation error (if any). 88func Validate(errs binding.Errors, data map[string]any, f any, l translation.Locale) binding.Errors { 89 if errs.Len() == 0 { 90 return errs 91 } 92 93 data["HasError"] = true 94 // If the field with name errs[0].FieldNames[0] is not found in form 95 // somehow, some code later on will panic on Data["ErrorMsg"].(string). 96 // So initialize it to some default. 97 data["ErrorMsg"] = l.Tr("form.unknown_error") 98 AssignForm(f, data) 99 100 typ := reflect.TypeOf(f) 101 102 if typ.Kind() == reflect.Ptr { 103 typ = typ.Elem() 104 } 105 106 if field, ok := typ.FieldByName(errs[0].FieldNames[0]); ok { 107 fieldName := field.Tag.Get("form") 108 if fieldName != "-" { 109 data["Err_"+field.Name] = true 110 111 trName := field.Tag.Get("locale") 112 if len(trName) == 0 { 113 trName = l.TrString("form." + field.Name) 114 } else { 115 trName = l.TrString(trName) 116 } 117 118 switch errs[0].Classification { 119 case binding.ERR_REQUIRED: 120 data["ErrorMsg"] = trName + l.TrString("form.require_error") 121 case binding.ERR_ALPHA_DASH: 122 data["ErrorMsg"] = trName + l.TrString("form.alpha_dash_error") 123 case binding.ERR_ALPHA_DASH_DOT: 124 data["ErrorMsg"] = trName + l.TrString("form.alpha_dash_dot_error") 125 case validation.ErrGitRefName: 126 data["ErrorMsg"] = trName + l.TrString("form.git_ref_name_error") 127 case binding.ERR_SIZE: 128 data["ErrorMsg"] = trName + l.TrString("form.size_error", GetSize(field)) 129 case binding.ERR_MIN_SIZE: 130 data["ErrorMsg"] = trName + l.TrString("form.min_size_error", GetMinSize(field)) 131 case binding.ERR_MAX_SIZE: 132 data["ErrorMsg"] = trName + l.TrString("form.max_size_error", GetMaxSize(field)) 133 case binding.ERR_EMAIL: 134 data["ErrorMsg"] = trName + l.TrString("form.email_error") 135 case binding.ERR_URL: 136 data["ErrorMsg"] = trName + l.TrString("form.url_error", errs[0].Message) 137 case binding.ERR_INCLUDE: 138 data["ErrorMsg"] = trName + l.TrString("form.include_error", GetInclude(field)) 139 case binding.ERR_RANGE: 140 min, max := GetRange(field) 141 data["ErrorMsg"] = trName + l.TrString("alert.range_error", l.PrettyNumber(min), l.PrettyNumber(max)) 142 case validation.ErrGlobPattern: 143 data["ErrorMsg"] = trName + l.TrString("form.glob_pattern_error", errs[0].Message) 144 case validation.ErrRegexPattern: 145 data["ErrorMsg"] = trName + l.TrString("form.regex_pattern_error", errs[0].Message) 146 case validation.ErrUsername: 147 if setting.Service.AllowDotsInUsernames { 148 data["ErrorMsg"] = trName + l.TrString("form.username_error") 149 } else { 150 data["ErrorMsg"] = trName + l.TrString("form.username_error_no_dots") 151 } 152 case validation.ErrInvalidGroupTeamMap: 153 data["ErrorMsg"] = trName + l.TrString("form.invalid_group_team_map_error", errs[0].Message) 154 case validation.ErrEmail: 155 data["ErrorMsg"] = trName + l.TrString("form.email_error") 156 default: 157 msg := errs[0].Classification 158 if msg != "" && errs[0].Message != "" { 159 msg += ": " 160 } 161 162 msg += errs[0].Message 163 if msg == "" { 164 msg = l.TrString("form.unknown_error") 165 } 166 data["ErrorMsg"] = trName + ": " + msg 167 } 168 return errs 169 } 170 } 171 return errs 172}