package xweb import ( "fmt" "html/template" "io/ioutil" "log" "os" "path/filepath" "reflect" "regexp" "strings" "sync" "time" "app.yhyue.com/moapp/jybase/date" "app.yhyue.com/moapp/jybase/encrypt" "github.com/howeyc/fsnotify" ) func IsNil(a interface{}) bool { if a == nil { return true } aa := reflect.ValueOf(a) return !aa.IsValid() || (aa.Type().Kind() == reflect.Ptr && aa.IsNil()) } //替换函数 func Replace(s interface{}, os, ns string, n int) string { return strings.Replace(fmt.Sprintf("%s", s), os, ns, n) } //正则 func Regexp(s interface{}, os, ns string) string { return regexp.MustCompile(os).ReplaceAllString(fmt.Sprintf("%s", s), ns) } //long转日期 func LongToDate(s interface{}, n int) string { if n == 0 { return date.FormatDateWithObj(&s, date.Date_Short_Layout) } else { return date.FormatDateWithObj(&s, date.Date_Full_Layout) } } //文本加密 func ConEncode(con interface{}) string { if con == nil { return "" } log.Println(con) se := encrypt.SimpleEncrypt{Key: "#topnet@con$temp#"} return se.EncodeString(fmt.Sprint(con)) } //文本加密 func ConDecode(con interface{}) string { if con == nil { return "" } se := encrypt.SimpleEncrypt{Key: "#topnet@con$temp#"} return se.DecodeString(fmt.Sprint(con)) } func Add(left interface{}, right interface{}) interface{} { var rleft, rright int64 var fleft, fright float64 var isInt bool = true switch left.(type) { case int: rleft = int64(left.(int)) case int8: rleft = int64(left.(int8)) case int16: rleft = int64(left.(int16)) case int32: rleft = int64(left.(int32)) case int64: rleft = left.(int64) case float32: fleft = float64(left.(float32)) isInt = false case float64: fleft = left.(float64) isInt = false } switch right.(type) { case int: rright = int64(right.(int)) case int8: rright = int64(right.(int8)) case int16: rright = int64(right.(int16)) case int32: rright = int64(right.(int32)) case int64: rright = right.(int64) case float32: fright = float64(left.(float32)) isInt = false case float64: fleft = left.(float64) isInt = false } var intSum int64 = rleft + rright if isInt { return intSum } else { return fleft + fright + float64(intSum) } } func Subtract(left interface{}, right interface{}) interface{} { var rleft, rright int64 var fleft, fright float64 var isInt bool = true switch left.(type) { case int: rleft = int64(left.(int)) case int8: rleft = int64(left.(int8)) case int16: rleft = int64(left.(int16)) case int32: rleft = int64(left.(int32)) case int64: rleft = left.(int64) case float32: fleft = float64(left.(float32)) isInt = false case float64: fleft = left.(float64) isInt = false } switch right.(type) { case int: rright = int64(right.(int)) case int8: rright = int64(right.(int8)) case int16: rright = int64(right.(int16)) case int32: rright = int64(right.(int32)) case int64: rright = right.(int64) case float32: fright = float64(left.(float32)) isInt = false case float64: fleft = left.(float64) isInt = false } if isInt { return rleft - rright } else { return fleft + float64(rleft) - (fright + float64(rright)) } } func Now() time.Time { return time.Now() } func FormatDate(t time.Time, format string) string { return t.Format(format) } func Eq(left interface{}, right interface{}) bool { leftIsNil := (left == nil) rightIsNil := (right == nil) if leftIsNil || rightIsNil { if leftIsNil && rightIsNil { return true } return false } return fmt.Sprintf("%v", left) == fmt.Sprintf("%v", right) } func Html(raw string) template.HTML { return template.HTML(raw) } func Js(raw string) template.JS { return template.JS(raw) } //Usage:UrlFor("main:root:/user/login") or UrlFor("root:/user/login") or UrlFor("/user/login") or UrlFor() func UrlFor(args ...string) string { s := [3]string{"main", "root", ""} var u []string size := len(args) if size > 0 { u = strings.Split(args[0], ":") } else { u = []string{""} } var appUrl string = "" switch len(u) { case 1: s[2] = u[0] case 2: s[1] = u[0] s[2] = u[1] default: s[0] = u[0] s[1] = u[1] s[2] = u[2] } var url, prefix, suffix string if server, ok := Servers[s[0]]; ok { url += server.Config.Url prefix = server.Config.UrlPrefix suffix = server.Config.UrlSuffix if appPath, ok := server.AppsNamePath[s[1]]; ok { appUrl = appPath } } url = strings.TrimRight(url, "/") + "/" if size == 0 { return url } if appUrl != "/" { appUrl = strings.TrimLeft(appUrl, "/") if length := len(appUrl); length > 0 && appUrl[length-1] != '/' { appUrl = appUrl + "/" } } else { appUrl = "" } url += prefix + appUrl if s[2] == "" { return url } url += strings.TrimLeft(s[2], "/") + suffix return url } var ( defaultFuncs template.FuncMap = template.FuncMap{ "Now": Now, "Eq": Eq, "FormatDate": FormatDate, "Html": Html, "Add": Add, "Subtract": Subtract, "IsNil": IsNil, "UrlFor": UrlFor, "Js": Js, "Replace": Replace, "Regexp": Regexp, "LongToDate": LongToDate, "ConDecode": ConDecode, //文本解密 "ConEncode": ConEncode, //文本加密 } ) type TemplateMgr struct { Caches map[string][]byte mutex *sync.Mutex RootDir string Ignores map[string]bool IsReload bool app *App Preprocessor func([]byte) []byte } func (self *TemplateMgr) Moniter(rootDir string) error { watcher, err := fsnotify.NewWatcher() if err != nil { return err } done := make(chan bool) go func() { for { select { case ev := <-watcher.Event: if ev == nil { break } if _, ok := self.Ignores[filepath.Base(ev.Name)]; ok { break } d, err := os.Stat(ev.Name) if err != nil { break } if ev.IsCreate() { if d.IsDir() { watcher.Watch(ev.Name) } else { tmpl := ev.Name[len(self.RootDir)+1:] content, err := ioutil.ReadFile(ev.Name) if err != nil { self.app.Errorf("loaded template %v failed: %v", tmpl, err) break } self.app.Infof("loaded template file %v success", tmpl) self.CacheTemplate(tmpl, content) } } else if ev.IsDelete() { if d.IsDir() { watcher.RemoveWatch(ev.Name) } else { tmpl := ev.Name[len(self.RootDir)+1:] self.CacheDelete(tmpl) } } else if ev.IsModify() { if d.IsDir() { } else { tmpl := ev.Name[len(self.RootDir)+1:] content, err := ioutil.ReadFile(ev.Name) if err != nil { self.app.Errorf("reloaded template %v failed: %v", tmpl, err) break } content = newIncludeIntmpl(rootDir, content) self.CacheTemplate(tmpl, content) self.app.Infof("reloaded template %v success", tmpl) } } else if ev.IsRename() { if d.IsDir() { watcher.RemoveWatch(ev.Name) } else { tmpl := ev.Name[len(self.RootDir)+1:] self.CacheDelete(tmpl) } } case err := <-watcher.Error: self.app.Error("error:", err) } } }() err = filepath.Walk(self.RootDir, func(f string, info os.FileInfo, err error) error { if info.IsDir() { return watcher.Watch(f) } return nil }) if err != nil { self.app.Error(err.Error()) return err } <-done watcher.Close() return nil } func newIncludeIntmpl(rootDir string, content []byte) []byte { for i := 0; i < 4; i++ { b := false newcontent := regInclude.ReplaceAllFunc(content, func(m []byte) []byte { b = true tpl := regInclude.FindSubmatch(m)[1] fpath := filepath.Join(rootDir, string(tpl)) if strings.Contains(string(m), "OUTSIDE") { fpath = string(tpl) } c, err := ioutil.ReadFile(fpath) if err != nil { return []byte{} } //c, _ := c.getTemplate(tpl) return c }) if !b { break } content = newcontent } return content } func (self *TemplateMgr) CacheAll(rootDir string) error { self.mutex.Lock() defer self.mutex.Unlock() //fmt.Print("Reading the contents of the template files, please wait... ") err := filepath.Walk(rootDir, func(f string, info os.FileInfo, err error) error { if info.IsDir() { return nil } tmpl := f[len(rootDir)+1:] tmpl = strings.Replace(tmpl, "\\", "/", -1) //[SWH|+]fix windows env if _, ok := self.Ignores[filepath.Base(tmpl)]; !ok { fpath := filepath.Join(self.RootDir, tmpl) content, err := ioutil.ReadFile(fpath) if err != nil { self.app.Debugf("load template %s error: %v", fpath, err) return err } content = newIncludeIntmpl(rootDir, content) self.app.Debug("loaded template", fpath) self.Caches[tmpl] = content } return nil }) //fmt.Println("Complete.") return err } func (self *TemplateMgr) Init(app *App, rootDir string, reload bool) error { self.RootDir = rootDir self.Caches = make(map[string][]byte) self.Ignores = make(map[string]bool) self.mutex = &sync.Mutex{} self.app = app if dirExists(rootDir) { self.CacheAll(rootDir) if reload { go self.Moniter(rootDir) } } if len(self.Ignores) == 0 { self.Ignores["*.tmp"] = false } return nil } func (self *TemplateMgr) GetTemplate(tmpl string) ([]byte, error) { self.mutex.Lock() defer self.mutex.Unlock() if content, ok := self.Caches[tmpl]; ok { self.app.Debugf("load template %v from cache", tmpl) return content, nil } content, err := ioutil.ReadFile(filepath.Join(self.RootDir, tmpl)) if err == nil { content = newIncludeIntmpl(self.RootDir, content) self.app.Debugf("load template %v from the file:", tmpl) self.Caches[tmpl] = content } return content, err } func (self *TemplateMgr) CacheTemplate(tmpl string, content []byte) { if self.Preprocessor != nil { content = self.Preprocessor(content) } self.mutex.Lock() defer self.mutex.Unlock() tmpl = strings.Replace(tmpl, "\\", "/", -1) self.app.Debugf("update template %v on cache", tmpl) self.Caches[tmpl] = content return } func (self *TemplateMgr) CacheDelete(tmpl string) { self.mutex.Lock() defer self.mutex.Unlock() tmpl = strings.Replace(tmpl, "\\", "/", -1) self.app.Debugf("delete template %v from cache", tmpl) delete(self.Caches, tmpl) return }