f593c1f3 by xiemengjun

init framwork

1 parent afebcd89
1 package beego
2
3 import (
4 "fmt"
5 "net/http"
6 "os"
7 "path"
8 )
9
10 var (
11 BeeApp *App
12 AppName string
13 AppPath string
14 StaticDir map[string]string
15 HttpAddr string
16 HttpPort int
17 RecoverPanic bool
18 AutoRender bool
19 ViewsPath string
20 RunMode string //"dev" or "prod"
21 AppConfig *Config
22 )
23
24 func init() {
25 BeeApp = NewApp()
26 AppPath, _ = os.Getwd()
27 StaticDir = make(map[string]string)
28 var err error
29 AppConfig, err = LoadConfig(path.Join(AppPath, "conf", "app.conf"))
30 if err != nil {
31 Trace("open Config err:", err)
32 HttpAddr = ""
33 HttpPort = 8080
34 AppName = "beego"
35 RunMode = "prod"
36 AutoRender = true
37 RecoverPanic = true
38 ViewsPath = "views"
39 } else {
40 HttpAddr = AppConfig.String("httpaddr")
41 if v, err := AppConfig.Int("httpport"); err != nil {
42 HttpPort = 8080
43 } else {
44 HttpPort = v
45 }
46 AppName = AppConfig.String("appname")
47 if runmode := AppConfig.String("runmode"); runmode != "" {
48 RunMode = runmode
49 } else {
50 RunMode = "prod"
51 }
52 if ar, err := AppConfig.Bool("autorender"); err != nil {
53 AutoRender = true
54 } else {
55 AutoRender = ar
56 }
57 if ar, err := AppConfig.Bool("autorecover"); err != nil {
58 RecoverPanic = true
59 } else {
60 RecoverPanic = ar
61 }
62 if views := AppConfig.String("viewspath"); views == "" {
63 ViewsPath = "views"
64 } else {
65 ViewsPath = views
66 }
67 }
68 StaticDir["/static"] = "static"
69
70 }
71
72 type App struct {
73 Handlers *ControllerRegistor
74 }
75
76 // New returns a new PatternServeMux.
77 func NewApp() *App {
78 cr := NewControllerRegistor()
79 app := &App{Handlers: cr}
80 return app
81 }
82
83 func (app *App) Run() {
84 addr := fmt.Sprintf("%s:%d", HttpAddr, HttpPort)
85 err := http.ListenAndServe(addr, app.Handlers)
86 if err != nil {
87 BeeLogger.Fatal("ListenAndServe: ", err)
88 }
89 }
90
91 func (app *App) RegisterController(path string, c ControllerInterface) *App {
92 app.Handlers.Add(path, c)
93 return app
94 }
95
96 func (app *App) SetViewsPath(path string) *App {
97 ViewsPath = path
98 return app
99 }
100
101 func (app *App) SetStaticPath(url string, path string) *App {
102 StaticDir[url] = path
103 return app
104 }
105
106 func (app *App) ErrorLog(ctx *Context) {
107 BeeLogger.Printf("[ERR] host: '%s', request: '%s %s', proto: '%s', ua: '%s', remote: '%s'\n", ctx.Request.Host, ctx.Request.Method, ctx.Request.URL.Path, ctx.Request.Proto, ctx.Request.UserAgent(), ctx.Request.RemoteAddr)
108 }
109
110 func (app *App) AccessLog(ctx *Context) {
111 BeeLogger.Printf("[ACC] host: '%s', request: '%s %s', proto: '%s', ua: %s'', remote: '%s'\n", ctx.Request.Host, ctx.Request.Method, ctx.Request.URL.Path, ctx.Request.Proto, ctx.Request.UserAgent(), ctx.Request.RemoteAddr)
112 }
1 package beego
2
3 import (
4 "bufio"
5 "bytes"
6 "errors"
7 "io"
8 "os"
9 "strconv"
10 "strings"
11 "sync"
12 "unicode"
13 )
14
15 var (
16 bComment = []byte{'#'}
17 bEmpty = []byte{}
18 bEqual = []byte{'='}
19 bDQuote = []byte{'"'}
20 )
21
22 // A Config represents the configuration.
23 type Config struct {
24 filename string
25 comment map[int][]string // id: []{comment, key...}; id 1 is for main comment.
26 data map[string]string // key: value
27 offset map[string]int64 // key: offset; for editing.
28 sync.RWMutex
29 }
30
31 // ParseFile creates a new Config and parses the file configuration from the
32 // named file.
33 func LoadConfig(name string) (*Config, error) {
34 file, err := os.Open(name)
35 if err != nil {
36 return nil, err
37 }
38
39 cfg := &Config{
40 file.Name(),
41 make(map[int][]string),
42 make(map[string]string),
43 make(map[string]int64),
44 sync.RWMutex{},
45 }
46 cfg.Lock()
47 defer cfg.Unlock()
48 defer file.Close()
49
50 var comment bytes.Buffer
51 buf := bufio.NewReader(file)
52
53 for nComment, off := 0, int64(1); ; {
54 line, _, err := buf.ReadLine()
55 if err == io.EOF {
56 break
57 }
58 if bytes.Equal(line, bEmpty) {
59 continue
60 }
61
62 off += int64(len(line))
63
64 if bytes.HasPrefix(line, bComment) {
65 line = bytes.TrimLeft(line, "#")
66 line = bytes.TrimLeftFunc(line, unicode.IsSpace)
67 comment.Write(line)
68 comment.WriteByte('\n')
69 continue
70 }
71 if comment.Len() != 0 {
72 cfg.comment[nComment] = []string{comment.String()}
73 comment.Reset()
74 nComment++
75 }
76
77 val := bytes.SplitN(line, bEqual, 2)
78 if bytes.HasPrefix(val[1], bDQuote) {
79 val[1] = bytes.Trim(val[1], `"`)
80 }
81
82 key := strings.TrimSpace(string(val[0]))
83 cfg.comment[nComment-1] = append(cfg.comment[nComment-1], key)
84 cfg.data[key] = strings.TrimSpace(string(val[1]))
85 cfg.offset[key] = off
86 }
87 return cfg, nil
88 }
89
90 // Bool returns the boolean value for a given key.
91 func (c *Config) Bool(key string) (bool, error) {
92 return strconv.ParseBool(c.data[key])
93 }
94
95 // Int returns the integer value for a given key.
96 func (c *Config) Int(key string) (int, error) {
97 return strconv.Atoi(c.data[key])
98 }
99
100 // Float returns the float value for a given key.
101 func (c *Config) Float(key string) (float64, error) {
102 return strconv.ParseFloat(c.data[key], 64)
103 }
104
105 // String returns the string value for a given key.
106 func (c *Config) String(key string) string {
107 return c.data[key]
108 }
109
110 // WriteValue writes a new value for key.
111 func (c *Config) SetValue(key, value string) error {
112 c.Lock()
113 defer c.Unlock()
114
115 if _, found := c.data[key]; !found {
116 return errors.New("key not found: " + key)
117 }
118
119 c.data[key] = value
120 return nil
121 }
1 package beego
2
3 import (
4 "fmt"
5 "mime"
6 "net/http"
7 "strings"
8 "time"
9 )
10
11 type Context struct {
12 ResponseWriter http.ResponseWriter
13 Request *http.Request
14 Params map[string]string
15 }
16
17 func (ctx *Context) WriteString(content string) {
18 ctx.ResponseWriter.Write([]byte(content))
19 }
20
21 func (ctx *Context) Abort(status int, body string) {
22 ctx.ResponseWriter.WriteHeader(status)
23 ctx.ResponseWriter.Write([]byte(body))
24 }
25
26 func (ctx *Context) Redirect(status int, url_ string) {
27 ctx.ResponseWriter.Header().Set("Location", url_)
28 ctx.ResponseWriter.WriteHeader(status)
29 ctx.ResponseWriter.Write([]byte("Redirecting to: " + url_))
30 }
31
32 func (ctx *Context) NotModified() {
33 ctx.ResponseWriter.WriteHeader(304)
34 }
35
36 func (ctx *Context) NotFound(message string) {
37 ctx.ResponseWriter.WriteHeader(404)
38 ctx.ResponseWriter.Write([]byte(message))
39 }
40
41 //Sets the content type by extension, as defined in the mime package.
42 //For example, ctx.ContentType("json") sets the content-type to "application/json"
43 func (ctx *Context) ContentType(ext string) {
44 if !strings.HasPrefix(ext, ".") {
45 ext = "." + ext
46 }
47 ctype := mime.TypeByExtension(ext)
48 if ctype != "" {
49 ctx.ResponseWriter.Header().Set("Content-Type", ctype)
50 }
51 }
52
53 func (ctx *Context) SetHeader(hdr string, val string, unique bool) {
54 if unique {
55 ctx.ResponseWriter.Header().Set(hdr, val)
56 } else {
57 ctx.ResponseWriter.Header().Add(hdr, val)
58 }
59 }
60
61 //Sets a cookie -- duration is the amount of time in seconds. 0 = forever
62 func (ctx *Context) SetCookie(name string, value string, age int64) {
63 var utctime time.Time
64 if age == 0 {
65 // 2^31 - 1 seconds (roughly 2038)
66 utctime = time.Unix(2147483647, 0)
67 } else {
68 utctime = time.Unix(time.Now().Unix()+age, 0)
69 }
70 cookie := fmt.Sprintf("%s=%s; expires=%s", name, value, webTime(utctime))
71 ctx.SetHeader("Set-Cookie", cookie, false)
72 }
1 package beego
2
3 import (
4 "bytes"
5 "encoding/json"
6 "encoding/xml"
7 "html/template"
8 "io/ioutil"
9 "net/http"
10 "path"
11 "strconv"
12 )
13
14 type Controller struct {
15 Ct *Context
16 Tpl *template.Template
17 Data map[interface{}]interface{}
18 ChildName string
19 TplNames string
20 Layout string
21 TplExt string
22 }
23
24 type ControllerInterface interface {
25 Init(ct *Context, cn string)
26 Prepare()
27 Get()
28 Post()
29 Delete()
30 Put()
31 Head()
32 Patch()
33 Options()
34 Finish()
35 Render() error
36 }
37
38 func (c *Controller) Init(ct *Context, cn string) {
39 c.Data = make(map[interface{}]interface{})
40 c.Tpl = template.New(cn + ct.Request.Method)
41 c.Tpl = c.Tpl.Funcs(beegoTplFuncMap)
42 c.Layout = ""
43 c.TplNames = ""
44 c.ChildName = cn
45 c.Ct = ct
46 c.TplExt = "tpl"
47
48 }
49
50 func (c *Controller) Prepare() {
51
52 }
53
54 func (c *Controller) Finish() {
55
56 }
57
58 func (c *Controller) Get() {
59 http.Error(c.Ct.ResponseWriter, "Method Not Allowed", 405)
60 }
61
62 func (c *Controller) Post() {
63 http.Error(c.Ct.ResponseWriter, "Method Not Allowed", 405)
64 }
65
66 func (c *Controller) Delete() {
67 http.Error(c.Ct.ResponseWriter, "Method Not Allowed", 405)
68 }
69
70 func (c *Controller) Put() {
71 http.Error(c.Ct.ResponseWriter, "Method Not Allowed", 405)
72 }
73
74 func (c *Controller) Head() {
75 http.Error(c.Ct.ResponseWriter, "Method Not Allowed", 405)
76 }
77
78 func (c *Controller) Patch() {
79 http.Error(c.Ct.ResponseWriter, "Method Not Allowed", 405)
80 }
81
82 func (c *Controller) Options() {
83 http.Error(c.Ct.ResponseWriter, "Method Not Allowed", 405)
84 }
85
86 func (c *Controller) Render() error {
87 //if the controller has set layout, then first get the tplname's content set the content to the layout
88 if c.Layout != "" {
89 if c.TplNames == "" {
90 c.TplNames = c.ChildName + "/" + c.Ct.Request.Method + "." + c.TplExt
91 }
92 t, err := c.Tpl.ParseFiles(path.Join(ViewsPath, c.TplNames), path.Join(ViewsPath, c.Layout))
93 if err != nil {
94 Trace("template ParseFiles err:", err)
95 }
96 _, file := path.Split(c.TplNames)
97 newbytes := bytes.NewBufferString("")
98 t.ExecuteTemplate(newbytes, file, c.Data)
99 tplcontent, _ := ioutil.ReadAll(newbytes)
100 c.Data["LayoutContent"] = template.HTML(string(tplcontent))
101 _, file = path.Split(c.Layout)
102 err = t.ExecuteTemplate(c.Ct.ResponseWriter, file, c.Data)
103 if err != nil {
104 Trace("template Execute err:", err)
105 }
106 } else {
107 if c.TplNames == "" {
108 c.TplNames = c.ChildName + "/" + c.Ct.Request.Method + "." + c.TplExt
109 }
110 t, err := c.Tpl.ParseFiles(path.Join(ViewsPath, c.TplNames))
111 if err != nil {
112 Trace("template ParseFiles err:", err)
113 }
114 _, file := path.Split(c.TplNames)
115 err = t.ExecuteTemplate(c.Ct.ResponseWriter, file, c.Data)
116 if err != nil {
117 Trace("template Execute err:", err)
118 }
119 }
120 return nil
121 }
122
123 func (c *Controller) Redirect(url string, code int) {
124 c.Ct.Redirect(code, url)
125 }
126
127 func (c *Controller) ServeJson() {
128 content, err := json.MarshalIndent(c.Data, "", " ")
129 if err != nil {
130 http.Error(c.Ct.ResponseWriter, err.Error(), http.StatusInternalServerError)
131 return
132 }
133 c.Ct.SetHeader("Content-Length", strconv.Itoa(len(content)), true)
134 c.Ct.ContentType("json")
135 c.Ct.ResponseWriter.Write(content)
136 }
137
138 func (c *Controller) ServeXml() {
139 content, err := xml.Marshal(c.Data)
140 if err != nil {
141 http.Error(c.Ct.ResponseWriter, err.Error(), http.StatusInternalServerError)
142 return
143 }
144 c.Ct.SetHeader("Content-Length", strconv.Itoa(len(content)), true)
145 c.Ct.ContentType("xml")
146 c.Ct.ResponseWriter.Write(content)
147 }
1 package beego
2
3 import (
4 "log"
5 "os"
6 )
7
8 //--------------------
9 // LOG LEVEL
10 //--------------------
11
12 // Log levels to control the logging output.
13 const (
14 LevelTrace = iota
15 LevelDebug
16 LevelInfo
17 LevelWarning
18 LevelError
19 LevelCritical
20 )
21
22 // logLevel controls the global log level used by the logger.
23 var level = LevelTrace
24
25 // LogLevel returns the global log level and can be used in
26 // own implementations of the logger interface.
27 func Level() int {
28 return level
29 }
30
31 // SetLogLevel sets the global log level used by the simple
32 // logger.
33 func SetLevel(l int) {
34 level = l
35 }
36
37 // logger references the used application logger.
38 var BeeLogger = log.New(os.Stdout, "", log.Ldate|log.Ltime)
39
40 // SetLogger sets a new logger.
41 func SetLogger(l *log.Logger) {
42 BeeLogger = l
43 }
44
45 // Trace logs a message at trace level.
46 func Trace(v ...interface{}) {
47 if level <= LevelTrace {
48 BeeLogger.Printf("[T] %v\n", v)
49 }
50 }
51
52 // Debug logs a message at debug level.
53 func Debug(v ...interface{}) {
54 if level <= LevelDebug {
55 BeeLogger.Printf("[D] %v\n", v)
56 }
57 }
58
59 // Info logs a message at info level.
60 func Info(v ...interface{}) {
61 if level <= LevelInfo {
62 BeeLogger.Printf("[I] %v\n", v)
63 }
64 }
65
66 // Warning logs a message at warning level.
67 func Warn(v ...interface{}) {
68 if level <= LevelWarning {
69 BeeLogger.Printf("[W] %v\n", v)
70 }
71 }
72
73 // Error logs a message at error level.
74 func Error(v ...interface{}) {
75 if level <= LevelError {
76 BeeLogger.Printf("[E] %v\n", v)
77 }
78 }
79
80 // Critical logs a message at critical level.
81 func Critical(v ...interface{}) {
82 if level <= LevelCritical {
83 BeeLogger.Printf("[C] %v\n", v)
84 }
85 }
1 package beego
...\ No newline at end of file ...\ No newline at end of file
1 package beego
2
3 import (
4 "net/http"
5 "net/url"
6 "reflect"
7 "regexp"
8 "runtime"
9 "strings"
10 )
11
12 type controllerInfo struct {
13 pattern string
14 regex *regexp.Regexp
15 params map[int]string
16 controllerType reflect.Type
17 }
18
19 type ControllerRegistor struct {
20 routers []*controllerInfo
21 fixrouters []*controllerInfo
22 filters []http.HandlerFunc
23 }
24
25 func NewControllerRegistor() *ControllerRegistor {
26 return &ControllerRegistor{routers: make([]*controllerInfo, 0)}
27 }
28
29 func (p *ControllerRegistor) Add(pattern string, c ControllerInterface) {
30 parts := strings.Split(pattern, "/")
31
32 j := 0
33 params := make(map[int]string)
34 for i, part := range parts {
35 if strings.HasPrefix(part, ":") {
36 expr := "([^/]+)"
37 //a user may choose to override the defult expression
38 // similar to expressjs: ‘/user/:id([0-9]+)’
39 if index := strings.Index(part, "("); index != -1 {
40 expr = part[index:]
41 part = part[:index]
42 }
43 params[j] = part
44 parts[i] = expr
45 j++
46 }
47 }
48 if j == 0 {
49 //now create the Route
50 t := reflect.Indirect(reflect.ValueOf(c)).Type()
51 route := &controllerInfo{}
52 route.pattern = pattern
53 route.controllerType = t
54
55 p.fixrouters = append(p.fixrouters, route)
56 } else { // add regexp routers
57 //recreate the url pattern, with parameters replaced
58 //by regular expressions. then compile the regex
59 pattern = strings.Join(parts, "/")
60 regex, regexErr := regexp.Compile(pattern)
61 if regexErr != nil {
62 //TODO add error handling here to avoid panic
63 panic(regexErr)
64 return
65 }
66
67 //now create the Route
68 t := reflect.Indirect(reflect.ValueOf(c)).Type()
69 route := &controllerInfo{}
70 route.regex = regex
71 route.params = params
72 route.controllerType = t
73
74 p.routers = append(p.routers, route)
75 }
76
77 }
78
79 // Filter adds the middleware filter.
80 func (p *ControllerRegistor) Filter(filter http.HandlerFunc) {
81 p.filters = append(p.filters, filter)
82 }
83
84 // FilterParam adds the middleware filter if the REST URL parameter exists.
85 func (p *ControllerRegistor) FilterParam(param string, filter http.HandlerFunc) {
86 if !strings.HasPrefix(param, ":") {
87 param = ":" + param
88 }
89
90 p.Filter(func(w http.ResponseWriter, r *http.Request) {
91 p := r.URL.Query().Get(param)
92 if len(p) > 0 {
93 filter(w, r)
94 }
95 })
96 }
97
98 // FilterPrefixPath adds the middleware filter if the prefix path exists.
99 func (p *ControllerRegistor) FilterPrefixPath(path string, filter http.HandlerFunc) {
100 p.Filter(func(w http.ResponseWriter, r *http.Request) {
101 if strings.HasPrefix(r.URL.Path, path) {
102 filter(w, r)
103 }
104 })
105 }
106
107 // AutoRoute
108 func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
109 defer func() {
110 if err := recover(); err != nil {
111 if !RecoverPanic {
112 // go back to panic
113 panic(err)
114 } else {
115 Critical("Handler crashed with error", err)
116 for i := 1; ; i += 1 {
117 _, file, line, ok := runtime.Caller(i)
118 if !ok {
119 break
120 }
121 Critical(file, line)
122 }
123 }
124 }
125 }()
126 w := &responseWriter{writer: rw}
127
128 var runrouter *controllerInfo
129 var findrouter bool
130
131 params := make(map[string]string)
132
133 //static file server
134 for prefix, staticDir := range StaticDir {
135 if strings.HasPrefix(r.URL.Path, prefix) {
136 file := staticDir + r.URL.Path[len(prefix):]
137 http.ServeFile(w, r, file)
138 w.started = true
139 return
140 }
141 }
142
143 requestPath := r.URL.Path
144
145 //first find path from the fixrouters to Improve Performance
146 for _, route := range p.fixrouters {
147 n := len(requestPath)
148 if (requestPath[n-1] != '/' && route.pattern == requestPath) ||
149 (len(route.pattern) >= n && requestPath[0:n] == route.pattern) {
150 runrouter = route
151 findrouter = true
152 break
153 }
154 }
155
156 if !findrouter {
157 //find a matching Route
158 for _, route := range p.routers {
159
160 //check if Route pattern matches url
161 if !route.regex.MatchString(requestPath) {
162 continue
163 }
164
165 //get submatches (params)
166 matches := route.regex.FindStringSubmatch(requestPath)
167
168 //double check that the Route matches the URL pattern.
169 if len(matches[0]) != len(requestPath) {
170 continue
171 }
172
173 if len(route.params) > 0 {
174 //add url parameters to the query param map
175 values := r.URL.Query()
176 for i, match := range matches[1:] {
177 values.Add(route.params[i], match)
178 params[route.params[i]] = match
179 }
180 //reassemble query params and add to RawQuery
181 r.URL.RawQuery = url.Values(values).Encode() + "&" + r.URL.RawQuery
182 //r.URL.RawQuery = url.Values(values).Encode()
183 }
184 runrouter = route
185 findrouter = true
186 break
187 }
188 }
189
190 if runrouter != nil {
191 //execute middleware filters
192 for _, filter := range p.filters {
193 filter(w, r)
194 if w.started {
195 return
196 }
197 }
198
199 //Invoke the request handler
200 vc := reflect.New(runrouter.controllerType)
201
202 //call the controller init function
203 init := vc.MethodByName("Init")
204 in := make([]reflect.Value, 2)
205 ct := &Context{ResponseWriter: w, Request: r, Params: params}
206 in[0] = reflect.ValueOf(ct)
207 in[1] = reflect.ValueOf(runrouter.controllerType.Name())
208 init.Call(in)
209 //call prepare function
210 in = make([]reflect.Value, 0)
211 method := vc.MethodByName("Prepare")
212 method.Call(in)
213
214 //if response has written,yes don't run next
215 if !w.started {
216 if r.Method == "GET" {
217 method = vc.MethodByName("Get")
218 method.Call(in)
219 } else if r.Method == "POST" {
220 method = vc.MethodByName("Post")
221 method.Call(in)
222 } else if r.Method == "HEAD" {
223 method = vc.MethodByName("Head")
224 method.Call(in)
225 } else if r.Method == "DELETE" {
226 method = vc.MethodByName("Delete")
227 method.Call(in)
228 } else if r.Method == "PUT" {
229 method = vc.MethodByName("Put")
230 method.Call(in)
231 } else if r.Method == "PATCH" {
232 method = vc.MethodByName("Patch")
233 method.Call(in)
234 } else if r.Method == "OPTIONS" {
235 method = vc.MethodByName("Options")
236 method.Call(in)
237 }
238 if !w.started {
239 if AutoRender {
240 method = vc.MethodByName("Render")
241 method.Call(in)
242 }
243 if !w.started {
244 method = vc.MethodByName("Finish")
245 method.Call(in)
246 }
247 }
248 }
249 }
250
251 //if no matches to url, throw a not found exception
252 if w.started == false {
253 http.NotFound(w, r)
254 }
255 }
256
257 //responseWriter is a wrapper for the http.ResponseWriter
258 //started set to true if response was written to then don't execute other handler
259 type responseWriter struct {
260 writer http.ResponseWriter
261 started bool
262 status int
263 }
264
265 // Header returns the header map that will be sent by WriteHeader.
266 func (w *responseWriter) Header() http.Header {
267 return w.writer.Header()
268 }
269
270 // Write writes the data to the connection as part of an HTTP reply,
271 // and sets `started` to true
272 func (w *responseWriter) Write(p []byte) (int, error) {
273 w.started = true
274 return w.writer.Write(p)
275 }
276
277 // WriteHeader sends an HTTP response header with status code,
278 // and sets `started` to true
279 func (w *responseWriter) WriteHeader(code int) {
280 w.status = code
281 w.started = true
282 w.writer.WriteHeader(code)
283 }
1 package beego
2
3 //@todo add template funcs
4
5 import (
6 //"fmt"
7 "errors"
8 "fmt"
9 "github.com/russross/blackfriday"
10 "html/template"
11 "strings"
12 "time"
13 )
14
15 var beegoTplFuncMap template.FuncMap
16
17 func init() {
18 beegoTplFuncMap = make(template.FuncMap)
19 beegoTplFuncMap["markdown"] = MarkDown
20 beegoTplFuncMap["dateformat"] = DateFormat
21 beegoTplFuncMap["compare"] = Compare
22 }
23
24 // MarkDown parses a string in MarkDown format and returns HTML. Used by the template parser as "markdown"
25 func MarkDown(raw string) (output template.HTML) {
26 input := []byte(raw)
27 bOutput := blackfriday.MarkdownBasic(input)
28 output = template.HTML(string(bOutput))
29 return
30 }
31
32 // DateFormat takes a time and a layout string and returns a string with the formatted date. Used by the template parser as "dateformat"
33 func DateFormat(t time.Time, layout string) (datestring string) {
34 datestring = t.Format(layout)
35 return
36 }
37
38 // 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.
39 // Whitespace is trimmed. Used by the template parser as "eq"
40 func Compare(a, b interface{}) (equal bool) {
41 equal = false
42 if strings.TrimSpace(fmt.Sprintf("%v", a)) == strings.TrimSpace(fmt.Sprintf("%v", b)) {
43 equal = true
44 }
45 return
46 }
47
48 // AddFuncMap let user to register a func in the template
49 func AddFuncMap(key string, funname interface{}) error {
50 if _, ok := beegoTplFuncMap["key"]; ok {
51 beegoTplFuncMap[key] = funname
52 return nil
53 }
54 return errors.New("funcmap already has the key")
55 }
1 package beego
2
3 import (
4 "strings"
5 "time"
6 )
7
8 func webTime(t time.Time) string {
9 ftime := t.Format(time.RFC1123)
10 if strings.HasSuffix(ftime, "UTC") {
11 ftime = ftime[0:len(ftime)-3] + "GMT"
12 }
13 return ftime
14 }
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!