diff --git a/controller.go b/controller.go index 78e6c34f..261de3df 100644 --- a/controller.go +++ b/controller.go @@ -3,6 +3,7 @@ package beego import ( "bytes" "crypto/hmac" + "crypto/rand" "crypto/sha1" "encoding/base64" "errors" @@ -370,7 +371,7 @@ func (c *Controller) XsrfToken() string { } else { expire = int64(XSRFExpire) } - token = GetRandomString(15) + token = getRandomString(15) c.SetSecureCookie(XSRFKEY, "_xsrf", token, expire) } c._xsrf_token = token @@ -405,3 +406,14 @@ func (c *Controller) GoToFunc(funcname string) { } c.gotofunc = funcname } + +//utils func for controller internal +func getRandomString(n int) string { + const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + var bytes = make([]byte, n) + rand.Read(bytes) + for i, b := range bytes { + bytes[i] = alphanum[b%byte(len(alphanum))] + } + return string(bytes) +} diff --git a/router.go b/router.go index 0cd668a0..c36db4fb 100644 --- a/router.go +++ b/router.go @@ -15,6 +15,7 @@ import ( beecontext "github.com/astaxie/beego/context" "github.com/astaxie/beego/middleware" "github.com/astaxie/beego/toolbox" + "github.com/astaxie/beego/utils" ) const ( @@ -159,7 +160,7 @@ func (p *ControllerRegistor) Add(pattern string, c ControllerInterface, mappingM } comma := strings.Split(colon[0], ",") for _, m := range comma { - if m == "*" || inSlice(strings.ToLower(m), HTTPMETHOD) { + if m == "*" || utils.InSlice(strings.ToLower(m), HTTPMETHOD) { if val := reflectVal.MethodByName(colon[1]); val.IsValid() { methods[strings.ToLower(m)] = colon[1] } else { @@ -272,7 +273,7 @@ func (p *ControllerRegistor) UrlFor(endpoint string, values ...string) string { for _, route := range p.fixrouters { if route.controllerType.Name() == controllName { var finded bool - if inSlice(strings.ToLower(methodName), HTTPMETHOD) { + if utils.InSlice(strings.ToLower(methodName), HTTPMETHOD) { if route.hasMethod { if m, ok := route.methods[strings.ToLower(methodName)]; ok && m != methodName { finded = false @@ -303,7 +304,7 @@ func (p *ControllerRegistor) UrlFor(endpoint string, values ...string) string { for _, route := range p.routers { if route.controllerType.Name() == controllName { var finded bool - if inSlice(strings.ToLower(methodName), HTTPMETHOD) { + if utils.InSlice(strings.ToLower(methodName), HTTPMETHOD) { if route.hasMethod { if m, ok := route.methods[strings.ToLower(methodName)]; ok && m != methodName { finded = false @@ -419,7 +420,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) context.Output = beecontext.NewOutput(rw) } - if !inSlice(strings.ToLower(r.Method), HTTPMETHOD) { + if !utils.InSlice(strings.ToLower(r.Method), HTTPMETHOD) { http.Error(w, "Method Not Allowed", 405) goto Admin } diff --git a/template.go b/template.go index 5c56da74..31d34a75 100644 --- a/template.go +++ b/template.go @@ -9,9 +9,10 @@ import ( "io/ioutil" "os" "path/filepath" - "reflect" "regexp" "strings" + + "github.com/astaxie/beego/utils" ) var ( @@ -144,7 +145,7 @@ func getTplDeep(root, file, parent string, t *template.Template) (*template.Temp } else { fileabspath = filepath.Join(root, file) } - if e, _ := FileExists(fileabspath); !e { + if e := utils.FileExists(fileabspath); !e { panic("can't find template file" + file) } data, err := ioutil.ReadFile(fileabspath) @@ -238,156 +239,3 @@ func _getTemplate(t0 *template.Template, root string, submods [][]string, others } return } - -// go1.2 added template funcs. begin -var ( - errBadComparisonType = errors.New("invalid type for comparison") - errBadComparison = errors.New("incompatible types for comparison") - errNoComparison = errors.New("missing argument for comparison") -) - -type kind int - -const ( - invalidKind kind = iota - boolKind - complexKind - intKind - floatKind - integerKind - stringKind - uintKind -) - -func basicKind(v reflect.Value) (kind, error) { - switch v.Kind() { - case reflect.Bool: - return boolKind, nil - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return intKind, nil - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - return uintKind, nil - case reflect.Float32, reflect.Float64: - return floatKind, nil - case reflect.Complex64, reflect.Complex128: - return complexKind, nil - case reflect.String: - return stringKind, nil - } - return invalidKind, errBadComparisonType -} - -// eq evaluates the comparison a == b || a == c || ... -func eq(arg1 interface{}, arg2 ...interface{}) (bool, error) { - v1 := reflect.ValueOf(arg1) - k1, err := basicKind(v1) - if err != nil { - return false, err - } - if len(arg2) == 0 { - return false, errNoComparison - } - for _, arg := range arg2 { - v2 := reflect.ValueOf(arg) - k2, err := basicKind(v2) - if err != nil { - return false, err - } - if k1 != k2 { - return false, errBadComparison - } - truth := false - switch k1 { - case boolKind: - truth = v1.Bool() == v2.Bool() - case complexKind: - truth = v1.Complex() == v2.Complex() - case floatKind: - truth = v1.Float() == v2.Float() - case intKind: - truth = v1.Int() == v2.Int() - case stringKind: - truth = v1.String() == v2.String() - case uintKind: - truth = v1.Uint() == v2.Uint() - default: - panic("invalid kind") - } - if truth { - return true, nil - } - } - return false, nil -} - -// ne evaluates the comparison a != b. -func ne(arg1, arg2 interface{}) (bool, error) { - // != is the inverse of ==. - equal, err := eq(arg1, arg2) - return !equal, err -} - -// lt evaluates the comparison a < b. -func lt(arg1, arg2 interface{}) (bool, error) { - v1 := reflect.ValueOf(arg1) - k1, err := basicKind(v1) - if err != nil { - return false, err - } - v2 := reflect.ValueOf(arg2) - k2, err := basicKind(v2) - if err != nil { - return false, err - } - if k1 != k2 { - return false, errBadComparison - } - truth := false - switch k1 { - case boolKind, complexKind: - return false, errBadComparisonType - case floatKind: - truth = v1.Float() < v2.Float() - case intKind: - truth = v1.Int() < v2.Int() - case stringKind: - truth = v1.String() < v2.String() - case uintKind: - truth = v1.Uint() < v2.Uint() - default: - panic("invalid kind") - } - return truth, nil -} - -// le evaluates the comparison <= b. -func le(arg1, arg2 interface{}) (bool, error) { - // <= is < or ==. - lessThan, err := lt(arg1, arg2) - if lessThan || err != nil { - return lessThan, err - } - return eq(arg1, arg2) -} - -// gt evaluates the comparison a > b. -func gt(arg1, arg2 interface{}) (bool, error) { - // > is the inverse of <=. - lessOrEqual, err := le(arg1, arg2) - if err != nil { - return false, err - } - return !lessOrEqual, nil -} - -// ge evaluates the comparison a >= b. -func ge(arg1, arg2 interface{}) (bool, error) { - // >= is the inverse of <. - lessThan, err := lt(arg1, arg2) - if err != nil { - return false, err - } - return !lessThan, nil -} - -// go1.2 added template funcs. end diff --git a/utils.go b/templatefunc.go similarity index 72% rename from utils.go rename to templatefunc.go index fafe4190..88acbb3f 100644 --- a/utils.go +++ b/templatefunc.go @@ -1,409 +1,509 @@ -package beego - -import ( - "crypto/rand" - "fmt" - "html/template" - "net/url" - "os" - "reflect" - "regexp" - "strconv" - "strings" - "time" -) - -func webTime(t time.Time) string { - ftime := t.Format(time.RFC1123) - if strings.HasSuffix(ftime, "UTC") { - ftime = ftime[0:len(ftime)-3] + "GMT" - } - return ftime -} - -func Substr(s string, start, length int) string { - bt := []rune(s) - if start < 0 { - start = 0 - } - var end int - if (start + length) > (len(bt) - 1) { - end = len(bt) - } else { - end = start + length - } - return string(bt[start:end]) -} - -// Html2str() returns escaping text convert from html -func Html2str(html string) string { - src := string(html) - - //将HTML标签全转换成小写 - re, _ := regexp.Compile("\\<[\\S\\s]+?\\>") - src = re.ReplaceAllStringFunc(src, strings.ToLower) - - //去除STYLE - re, _ = regexp.Compile("\\") - src = re.ReplaceAllString(src, "") - - //去除SCRIPT - re, _ = regexp.Compile("\\") - src = re.ReplaceAllString(src, "") - - //去除所有尖括号内的HTML代码,并换成换行符 - re, _ = regexp.Compile("\\<[\\S\\s]+?\\>") - src = re.ReplaceAllString(src, "\n") - - //去除连续的换行符 - re, _ = regexp.Compile("\\s{2,}") - src = re.ReplaceAllString(src, "\n") - - return strings.TrimSpace(src) -} - -// DateFormat takes a time and a layout string and returns a string with the formatted date. Used by the template parser as "dateformat" -func DateFormat(t time.Time, layout string) (datestring string) { - datestring = t.Format(layout) - return -} - -var DatePatterns = []string{ - // year - "Y", "2006", // A full numeric representation of a year, 4 digits Examples: 1999 or 2003 - "y", "06", //A two digit representation of a year Examples: 99 or 03 - - // month - "m", "01", // Numeric representation of a month, with leading zeros 01 through 12 - "n", "1", // Numeric representation of a month, without leading zeros 1 through 12 - "M", "Jan", // A short textual representation of a month, three letters Jan through Dec - "F", "January", // A full textual representation of a month, such as January or March January through December - - // day - "d", "02", // Day of the month, 2 digits with leading zeros 01 to 31 - "j", "2", // Day of the month without leading zeros 1 to 31 - - // week - "D", "Mon", // A textual representation of a day, three letters Mon through Sun - "l", "Monday", // A full textual representation of the day of the week Sunday through Saturday - - // time - "g", "3", // 12-hour format of an hour without leading zeros 1 through 12 - "G", "15", // 24-hour format of an hour without leading zeros 0 through 23 - "h", "03", // 12-hour format of an hour with leading zeros 01 through 12 - "H", "15", // 24-hour format of an hour with leading zeros 00 through 23 - - "a", "pm", // Lowercase Ante meridiem and Post meridiem am or pm - "A", "PM", // Uppercase Ante meridiem and Post meridiem AM or PM - - "i", "04", // Minutes with leading zeros 00 to 59 - "s", "05", // Seconds, with leading zeros 00 through 59 - - // time zone - "T", "MST", - "P", "-07:00", - "O", "-0700", - - // RFC 2822 - "r", time.RFC1123Z, -} - -// Parse Date use PHP time format -func DateParse(dateString, format string) (time.Time, error) { - replacer := strings.NewReplacer(DatePatterns...) - format = replacer.Replace(format) - return time.ParseInLocation(format, dateString, time.Local) -} - -// Date takes a PHP like date func to Go's time format -func Date(t time.Time, format string) string { - replacer := strings.NewReplacer(DatePatterns...) - format = replacer.Replace(format) - return t.Format(format) -} - -// Compare is a quick and dirty comparison function. It will convert whatever you give it to strings and see if the two values are equal. -// Whitespace is trimmed. Used by the template parser as "eq" -func Compare(a, b interface{}) (equal bool) { - equal = false - if strings.TrimSpace(fmt.Sprintf("%v", a)) == strings.TrimSpace(fmt.Sprintf("%v", b)) { - equal = true - } - return -} - -func Str2html(raw string) template.HTML { - return template.HTML(raw) -} - -func Htmlquote(src string) string { - //HTML编码为实体符号 - /* - Encodes `text` for raw use in HTML. - >>> htmlquote("<'&\\">") - '<'&">' - */ - - text := string(src) - - text = strings.Replace(text, "&", "&", -1) // Must be done first! - text = strings.Replace(text, "<", "<", -1) - text = strings.Replace(text, ">", ">", -1) - text = strings.Replace(text, "'", "'", -1) - text = strings.Replace(text, "\"", """, -1) - text = strings.Replace(text, "“", "“", -1) - text = strings.Replace(text, "”", "”", -1) - text = strings.Replace(text, " ", " ", -1) - - return strings.TrimSpace(text) -} - -func Htmlunquote(src string) string { - //实体符号解释为HTML - /* - Decodes `text` that's HTML quoted. - >>> htmlunquote('<'&">') - '<\\'&">' - */ - - // strings.Replace(s, old, new, n) - // 在s字符串中,把old字符串替换为new字符串,n表示替换的次数,小于0表示全部替换 - - text := string(src) - text = strings.Replace(text, " ", " ", -1) - text = strings.Replace(text, "”", "”", -1) - text = strings.Replace(text, "“", "“", -1) - text = strings.Replace(text, """, "\"", -1) - text = strings.Replace(text, "'", "'", -1) - text = strings.Replace(text, ">", ">", -1) - text = strings.Replace(text, "<", "<", -1) - text = strings.Replace(text, "&", "&", -1) // Must be done last! - - return strings.TrimSpace(text) -} - -func inSlice(v string, sl []string) bool { - for _, vv := range sl { - if vv == v { - return true - } - } - return false -} - -// parse form values to struct via tag -func ParseForm(form url.Values, obj interface{}) error { - objT := reflect.TypeOf(obj) - objV := reflect.ValueOf(obj) - if !isStructPtr(objT) { - return fmt.Errorf("%v must be a struct pointer", obj) - } - objT = objT.Elem() - objV = objV.Elem() - - for i := 0; i < objT.NumField(); i++ { - fieldV := objV.Field(i) - if !fieldV.CanSet() { - continue - } - - fieldT := objT.Field(i) - tags := strings.Split(fieldT.Tag.Get("form"), ",") - var tag string - if len(tags) == 0 || len(tags[0]) == 0 { - tag = fieldT.Name - } else if tags[0] == "-" { - continue - } else { - tag = tags[0] - } - - value := form.Get(tag) - if len(value) == 0 { - continue - } - - switch fieldT.Type.Kind() { - case reflect.Bool: - b, err := strconv.ParseBool(value) - if err != nil { - return err - } - fieldV.SetBool(b) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - x, err := strconv.ParseInt(value, 10, 64) - if err != nil { - return err - } - fieldV.SetInt(x) - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - x, err := strconv.ParseUint(value, 10, 64) - if err != nil { - return err - } - fieldV.SetUint(x) - case reflect.Float32, reflect.Float64: - x, err := strconv.ParseFloat(value, 64) - if err != nil { - return err - } - fieldV.SetFloat(x) - case reflect.Interface: - fieldV.Set(reflect.ValueOf(value)) - case reflect.String: - fieldV.SetString(value) - } - } - return nil -} - -// form types for RenderForm function -var FormType = map[string]bool{ - "text": true, - "textarea": true, - "hidden": true, - "password": true, -} - -var unKind = map[reflect.Kind]bool{ - reflect.Uintptr: true, - reflect.Complex64: true, - reflect.Complex128: true, - reflect.Array: true, - reflect.Chan: true, - reflect.Func: true, - reflect.Map: true, - reflect.Ptr: true, - reflect.Slice: true, - reflect.Struct: true, - reflect.UnsafePointer: true, -} - -// obj must be a struct pointer -func RenderForm(obj interface{}) template.HTML { - objT := reflect.TypeOf(obj) - objV := reflect.ValueOf(obj) - if !isStructPtr(objT) { - return template.HTML("") - } - objT = objT.Elem() - objV = objV.Elem() - - var raw []string - for i := 0; i < objT.NumField(); i++ { - fieldV := objV.Field(i) - if !fieldV.CanSet() || unKind[fieldV.Kind()] { - continue - } - - fieldT := objT.Field(i) - tags := strings.Split(fieldT.Tag.Get("form"), ",") - label := fieldT.Name + ": " - name := fieldT.Name - fType := "text" - - switch len(tags) { - case 1: - if tags[0] == "-" { - continue - } - if len(tags[0]) > 0 { - name = tags[0] - } - case 2: - if len(tags[0]) > 0 { - name = tags[0] - } - if len(tags[1]) > 0 { - fType = tags[1] - } - case 3: - if len(tags[0]) > 0 { - name = tags[0] - } - if len(tags[1]) > 0 { - fType = tags[1] - } - if len(tags[2]) > 0 { - label = tags[2] - } - } - - raw = append(raw, fmt.Sprintf(`%v`, - label, name, fType, fieldV.Interface())) - } - return template.HTML(strings.Join(raw, "
")) -} - -func isStructPtr(t reflect.Type) bool { - return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct -} - -func stringsToJson(str string) string { - rs := []rune(str) - jsons := "" - for _, r := range rs { - rint := int(r) - if rint < 128 { - jsons += string(r) - } else { - jsons += "\\u" + strconv.FormatInt(int64(rint), 16) // json - } - } - return jsons -} - -func FileExists(path string) (bool, error) { - _, err := os.Stat(path) - if err == nil { - return true, nil - } - if os.IsNotExist(err) { - return false, nil - } - return false, err -} - -func GetRandomString(n int) string { - const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" - var bytes = make([]byte, n) - rand.Read(bytes) - for i, b := range bytes { - bytes[i] = alphanum[b%byte(len(alphanum))] - } - return string(bytes) -} - -// This will reference the index function local to the current blueprint: -// UrlFor(".index") -// ... print UrlFor("index") -// ... print UrlFor("login") -// ... print UrlFor("login", "next","/"") -// ... print UrlFor("profile", "username","John Doe") -// ... -// / -// /login -// /login?next=/ -// /user/John%20Doe -func UrlFor(endpoint string, values ...string) string { - return BeeApp.UrlFor(endpoint, values...) -} - - -//This can be changed to a better name -func AssetsJs(src string) template.HTML { - text := string(src) - - text = "" - - return template.HTML(text) -} - -//This can be changed to a better name -func AssetsCss(src string) template.HTML { - text := string(src) - - text = "" - - return template.HTML(text) -} +package beego + +import ( + "errors" + "fmt" + "html/template" + "net/url" + "reflect" + "regexp" + "strconv" + "strings" + "time" +) + +// Substr() return the substr from start to length +func Substr(s string, start, length int) string { + bt := []rune(s) + if start < 0 { + start = 0 + } + var end int + if (start + length) > (len(bt) - 1) { + end = len(bt) + } else { + end = start + length + } + return string(bt[start:end]) +} + +// Html2str() returns escaping text convert from html +func Html2str(html string) string { + src := string(html) + + //将HTML标签全转换成小写 + re, _ := regexp.Compile("\\<[\\S\\s]+?\\>") + src = re.ReplaceAllStringFunc(src, strings.ToLower) + + //去除STYLE + re, _ = regexp.Compile("\\") + src = re.ReplaceAllString(src, "") + + //去除SCRIPT + re, _ = regexp.Compile("\\") + src = re.ReplaceAllString(src, "") + + //去除所有尖括号内的HTML代码,并换成换行符 + re, _ = regexp.Compile("\\<[\\S\\s]+?\\>") + src = re.ReplaceAllString(src, "\n") + + //去除连续的换行符 + re, _ = regexp.Compile("\\s{2,}") + src = re.ReplaceAllString(src, "\n") + + return strings.TrimSpace(src) +} + +// DateFormat takes a time and a layout string and returns a string with the formatted date. Used by the template parser as "dateformat" +func DateFormat(t time.Time, layout string) (datestring string) { + datestring = t.Format(layout) + return +} + +var DatePatterns = []string{ + // year + "Y", "2006", // A full numeric representation of a year, 4 digits Examples: 1999 or 2003 + "y", "06", //A two digit representation of a year Examples: 99 or 03 + + // month + "m", "01", // Numeric representation of a month, with leading zeros 01 through 12 + "n", "1", // Numeric representation of a month, without leading zeros 1 through 12 + "M", "Jan", // A short textual representation of a month, three letters Jan through Dec + "F", "January", // A full textual representation of a month, such as January or March January through December + + // day + "d", "02", // Day of the month, 2 digits with leading zeros 01 to 31 + "j", "2", // Day of the month without leading zeros 1 to 31 + + // week + "D", "Mon", // A textual representation of a day, three letters Mon through Sun + "l", "Monday", // A full textual representation of the day of the week Sunday through Saturday + + // time + "g", "3", // 12-hour format of an hour without leading zeros 1 through 12 + "G", "15", // 24-hour format of an hour without leading zeros 0 through 23 + "h", "03", // 12-hour format of an hour with leading zeros 01 through 12 + "H", "15", // 24-hour format of an hour with leading zeros 00 through 23 + + "a", "pm", // Lowercase Ante meridiem and Post meridiem am or pm + "A", "PM", // Uppercase Ante meridiem and Post meridiem AM or PM + + "i", "04", // Minutes with leading zeros 00 to 59 + "s", "05", // Seconds, with leading zeros 00 through 59 + + // time zone + "T", "MST", + "P", "-07:00", + "O", "-0700", + + // RFC 2822 + "r", time.RFC1123Z, +} + +// Parse Date use PHP time format +func DateParse(dateString, format string) (time.Time, error) { + replacer := strings.NewReplacer(DatePatterns...) + format = replacer.Replace(format) + return time.ParseInLocation(format, dateString, time.Local) +} + +// Date takes a PHP like date func to Go's time format +func Date(t time.Time, format string) string { + replacer := strings.NewReplacer(DatePatterns...) + format = replacer.Replace(format) + return t.Format(format) +} + +// Compare is a quick and dirty comparison function. It will convert whatever you give it to strings and see if the two values are equal. +// Whitespace is trimmed. Used by the template parser as "eq" +func Compare(a, b interface{}) (equal bool) { + equal = false + if strings.TrimSpace(fmt.Sprintf("%v", a)) == strings.TrimSpace(fmt.Sprintf("%v", b)) { + equal = true + } + return +} + +func Str2html(raw string) template.HTML { + return template.HTML(raw) +} + +func Htmlquote(src string) string { + //HTML编码为实体符号 + /* + Encodes `text` for raw use in HTML. + >>> htmlquote("<'&\\">") + '<'&">' + */ + + text := string(src) + + text = strings.Replace(text, "&", "&", -1) // Must be done first! + text = strings.Replace(text, "<", "<", -1) + text = strings.Replace(text, ">", ">", -1) + text = strings.Replace(text, "'", "'", -1) + text = strings.Replace(text, "\"", """, -1) + text = strings.Replace(text, "“", "“", -1) + text = strings.Replace(text, "”", "”", -1) + text = strings.Replace(text, " ", " ", -1) + + return strings.TrimSpace(text) +} + +func Htmlunquote(src string) string { + //实体符号解释为HTML + /* + Decodes `text` that's HTML quoted. + >>> htmlunquote('<'&">') + '<\\'&">' + */ + + // strings.Replace(s, old, new, n) + // 在s字符串中,把old字符串替换为new字符串,n表示替换的次数,小于0表示全部替换 + + text := string(src) + text = strings.Replace(text, " ", " ", -1) + text = strings.Replace(text, "”", "”", -1) + text = strings.Replace(text, "“", "“", -1) + text = strings.Replace(text, """, "\"", -1) + text = strings.Replace(text, "'", "'", -1) + text = strings.Replace(text, ">", ">", -1) + text = strings.Replace(text, "<", "<", -1) + text = strings.Replace(text, "&", "&", -1) // Must be done last! + + return strings.TrimSpace(text) +} + +// This will reference the index function local to the current blueprint: +// UrlFor(".index") +// ... print UrlFor("index") +// ... print UrlFor("login") +// ... print UrlFor("login", "next","/"") +// ... print UrlFor("profile", "username","John Doe") +// ... +// / +// /login +// /login?next=/ +// /user/John%20Doe +func UrlFor(endpoint string, values ...string) string { + return BeeApp.UrlFor(endpoint, values...) +} + +//This can be changed to a better name +func AssetsJs(src string) template.HTML { + text := string(src) + + text = "" + + return template.HTML(text) +} + +//This can be changed to a better name +func AssetsCss(src string) template.HTML { + text := string(src) + + text = "" + + return template.HTML(text) +} + +// parse form values to struct via tag +func ParseForm(form url.Values, obj interface{}) error { + objT := reflect.TypeOf(obj) + objV := reflect.ValueOf(obj) + if !isStructPtr(objT) { + return fmt.Errorf("%v must be a struct pointer", obj) + } + objT = objT.Elem() + objV = objV.Elem() + + for i := 0; i < objT.NumField(); i++ { + fieldV := objV.Field(i) + if !fieldV.CanSet() { + continue + } + + fieldT := objT.Field(i) + tags := strings.Split(fieldT.Tag.Get("form"), ",") + var tag string + if len(tags) == 0 || len(tags[0]) == 0 { + tag = fieldT.Name + } else if tags[0] == "-" { + continue + } else { + tag = tags[0] + } + + value := form.Get(tag) + if len(value) == 0 { + continue + } + + switch fieldT.Type.Kind() { + case reflect.Bool: + b, err := strconv.ParseBool(value) + if err != nil { + return err + } + fieldV.SetBool(b) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + x, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return err + } + fieldV.SetInt(x) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + x, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return err + } + fieldV.SetUint(x) + case reflect.Float32, reflect.Float64: + x, err := strconv.ParseFloat(value, 64) + if err != nil { + return err + } + fieldV.SetFloat(x) + case reflect.Interface: + fieldV.Set(reflect.ValueOf(value)) + case reflect.String: + fieldV.SetString(value) + } + } + return nil +} + +// form types for RenderForm function +var FormType = map[string]bool{ + "text": true, + "textarea": true, + "hidden": true, + "password": true, +} + +var unKind = map[reflect.Kind]bool{ + reflect.Uintptr: true, + reflect.Complex64: true, + reflect.Complex128: true, + reflect.Array: true, + reflect.Chan: true, + reflect.Func: true, + reflect.Map: true, + reflect.Ptr: true, + reflect.Slice: true, + reflect.Struct: true, + reflect.UnsafePointer: true, +} + +// obj must be a struct pointer +func RenderForm(obj interface{}) template.HTML { + objT := reflect.TypeOf(obj) + objV := reflect.ValueOf(obj) + if !isStructPtr(objT) { + return template.HTML("") + } + objT = objT.Elem() + objV = objV.Elem() + + var raw []string + for i := 0; i < objT.NumField(); i++ { + fieldV := objV.Field(i) + if !fieldV.CanSet() || unKind[fieldV.Kind()] { + continue + } + + fieldT := objT.Field(i) + tags := strings.Split(fieldT.Tag.Get("form"), ",") + label := fieldT.Name + ": " + name := fieldT.Name + fType := "text" + + switch len(tags) { + case 1: + if tags[0] == "-" { + continue + } + if len(tags[0]) > 0 { + name = tags[0] + } + case 2: + if len(tags[0]) > 0 { + name = tags[0] + } + if len(tags[1]) > 0 { + fType = tags[1] + } + case 3: + if len(tags[0]) > 0 { + name = tags[0] + } + if len(tags[1]) > 0 { + fType = tags[1] + } + if len(tags[2]) > 0 { + label = tags[2] + } + } + + raw = append(raw, fmt.Sprintf(`%v`, + label, name, fType, fieldV.Interface())) + } + return template.HTML(strings.Join(raw, "
")) +} + +func isStructPtr(t reflect.Type) bool { + return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct +} + +// go1.2 added template funcs. begin +var ( + errBadComparisonType = errors.New("invalid type for comparison") + errBadComparison = errors.New("incompatible types for comparison") + errNoComparison = errors.New("missing argument for comparison") +) + +type kind int + +const ( + invalidKind kind = iota + boolKind + complexKind + intKind + floatKind + integerKind + stringKind + uintKind +) + +func basicKind(v reflect.Value) (kind, error) { + switch v.Kind() { + case reflect.Bool: + return boolKind, nil + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return intKind, nil + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return uintKind, nil + case reflect.Float32, reflect.Float64: + return floatKind, nil + case reflect.Complex64, reflect.Complex128: + return complexKind, nil + case reflect.String: + return stringKind, nil + } + return invalidKind, errBadComparisonType +} + +// eq evaluates the comparison a == b || a == c || ... +func eq(arg1 interface{}, arg2 ...interface{}) (bool, error) { + v1 := reflect.ValueOf(arg1) + k1, err := basicKind(v1) + if err != nil { + return false, err + } + if len(arg2) == 0 { + return false, errNoComparison + } + for _, arg := range arg2 { + v2 := reflect.ValueOf(arg) + k2, err := basicKind(v2) + if err != nil { + return false, err + } + if k1 != k2 { + return false, errBadComparison + } + truth := false + switch k1 { + case boolKind: + truth = v1.Bool() == v2.Bool() + case complexKind: + truth = v1.Complex() == v2.Complex() + case floatKind: + truth = v1.Float() == v2.Float() + case intKind: + truth = v1.Int() == v2.Int() + case stringKind: + truth = v1.String() == v2.String() + case uintKind: + truth = v1.Uint() == v2.Uint() + default: + panic("invalid kind") + } + if truth { + return true, nil + } + } + return false, nil +} + +// ne evaluates the comparison a != b. +func ne(arg1, arg2 interface{}) (bool, error) { + // != is the inverse of ==. + equal, err := eq(arg1, arg2) + return !equal, err +} + +// lt evaluates the comparison a < b. +func lt(arg1, arg2 interface{}) (bool, error) { + v1 := reflect.ValueOf(arg1) + k1, err := basicKind(v1) + if err != nil { + return false, err + } + v2 := reflect.ValueOf(arg2) + k2, err := basicKind(v2) + if err != nil { + return false, err + } + if k1 != k2 { + return false, errBadComparison + } + truth := false + switch k1 { + case boolKind, complexKind: + return false, errBadComparisonType + case floatKind: + truth = v1.Float() < v2.Float() + case intKind: + truth = v1.Int() < v2.Int() + case stringKind: + truth = v1.String() < v2.String() + case uintKind: + truth = v1.Uint() < v2.Uint() + default: + panic("invalid kind") + } + return truth, nil +} + +// le evaluates the comparison <= b. +func le(arg1, arg2 interface{}) (bool, error) { + // <= is < or ==. + lessThan, err := lt(arg1, arg2) + if lessThan || err != nil { + return lessThan, err + } + return eq(arg1, arg2) +} + +// gt evaluates the comparison a > b. +func gt(arg1, arg2 interface{}) (bool, error) { + // > is the inverse of <=. + lessOrEqual, err := le(arg1, arg2) + if err != nil { + return false, err + } + return !lessOrEqual, nil +} + +// ge evaluates the comparison a >= b. +func ge(arg1, arg2 interface{}) (bool, error) { + // >= is the inverse of <. + lessThan, err := lt(arg1, arg2) + if err != nil { + return false, err + } + return !lessThan, nil +} + +// go1.2 added template funcs. end diff --git a/utils_test.go b/templatefunc_test.go similarity index 88% rename from utils_test.go rename to templatefunc_test.go index b6a5bc58..726ce2d2 100644 --- a/utils_test.go +++ b/templatefunc_test.go @@ -7,18 +7,6 @@ import ( "time" ) -func TestWebTime(t *testing.T) { - ts := "Fri, 26 Jul 2013 12:27:42 CST" - l, _ := time.LoadLocation("GST") - tt, _ := time.ParseInLocation(time.RFC1123, ts, l) - if ts != webTime(tt) { - t.Error("should be equal") - } - if "Fri, 26 Jul 2013 12:27:42 GMT" != webTime(tt.UTC()) { - t.Error("should be equal") - } -} - func TestSubstr(t *testing.T) { s := `012345` if Substr(s, 0, 2) != "01" { @@ -92,16 +80,6 @@ func TestHtmlunquote(t *testing.T) { } } -func TestInSlice(t *testing.T) { - sl := []string{"A", "b"} - if !inSlice("A", sl) { - t.Error("should be true") - } - if inSlice("B", sl) { - t.Error("should be false") - } -} - func TestParseForm(t *testing.T) { type user struct { Id int `form:"-"` diff --git a/utils/slice.go b/utils/slice.go new file mode 100644 index 00000000..fe6555f1 --- /dev/null +++ b/utils/slice.go @@ -0,0 +1,10 @@ +package utils + +func InSlice(v string, sl []string) bool { + for _, vv := range sl { + if vv == v { + return true + } + } + return false +} diff --git a/utils/slice_test.go b/utils/slice_test.go new file mode 100644 index 00000000..d06a94a6 --- /dev/null +++ b/utils/slice_test.go @@ -0,0 +1,15 @@ +package utils + +import ( + "testing" +) + +func TestInSlice(t *testing.T) { + sl := []string{"A", "b"} + if !InSlice("A", sl) { + t.Error("should be true") + } + if InSlice("B", sl) { + t.Error("should be false") + } +}