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
}