7 "github.com/xeonx/timeago"
16 // The functions available for use in the templates.
17 TemplateFuncs = map[string]interface{}{
19 "set": func(viewArgs map[string]interface{}, key string, value interface{}) template.JS {
21 return template.JS("")
23 "append": func(viewArgs map[string]interface{}, key string, value interface{}) template.JS {
24 if viewArgs[key] == nil {
25 viewArgs[key] = []interface{}{value}
27 viewArgs[key] = append(viewArgs[key].([]interface{}), value)
29 return template.JS("")
32 "firstof": func(args ...interface{}) interface{} {
33 for _, val := range args {
48 "option": func(f *Field, val interface{}, label string) template.HTML {
50 if f.Flash() == val || (f.Flash() == "" && f.Value() == val) {
51 selected = " selected"
54 return template.HTML(fmt.Sprintf(`<option value="%s"%s>%s</option>`,
55 html.EscapeString(fmt.Sprintf("%v", val)), selected, html.EscapeString(label)))
57 "radio": func(f *Field, val string) template.HTML {
62 return template.HTML(fmt.Sprintf(`<input type="radio" name="%s" value="%s"%s>`,
63 html.EscapeString(f.Name), html.EscapeString(val), checked))
65 "checkbox": func(f *Field, val string) template.HTML {
70 return template.HTML(fmt.Sprintf(`<input type="checkbox" name="%s" value="%s"%s>`,
71 html.EscapeString(f.Name), html.EscapeString(val), checked))
73 // Pads the given string with 's up to the given width.
74 "pad": func(str string, width int) template.HTML {
75 if len(str) >= width {
76 return template.HTML(html.EscapeString(str))
78 return template.HTML(html.EscapeString(str) + strings.Repeat(" ", width-len(str)))
81 "errorClass": func(name string, viewArgs map[string]interface{}) template.HTML {
82 errorMap, ok := viewArgs["errors"].(map[string]*ValidationError)
83 if !ok || errorMap == nil {
84 templateLog.Warn("errorClass: Called 'errorClass' without 'errors' in the view args.")
85 return template.HTML("")
87 valError, ok := errorMap[name]
88 if !ok || valError == nil {
89 return template.HTML("")
91 return template.HTML(ErrorCSSClass)
94 "msg": func(viewArgs map[string]interface{}, message string, args ...interface{}) template.HTML {
95 str, ok := viewArgs[CurrentLocaleViewArg].(string)
99 return template.HTML(MessageFunc(str, message, args...))
102 // Replaces newlines with <br>
103 "nl2br": func(text string) template.HTML {
104 return template.HTML(strings.Replace(template.HTMLEscapeString(text), "\n", "<br>", -1))
107 // Skips sanitation on the parameter. Do not use with dynamic data.
108 "raw": func(text string) template.HTML {
109 return template.HTML(text)
112 // Pluralize, a helper for pluralizing words to correspond to data of dynamic length.
113 // items - a slice of items, or an integer indicating how many items there are.
114 // pluralOverrides - optional arguments specifying the output in the
115 // singular and plural cases. by default "" and "s"
116 "pluralize": func(items interface{}, pluralOverrides ...string) string {
117 singular, plural := "", "s"
118 if len(pluralOverrides) >= 1 {
119 singular = pluralOverrides[0]
120 if len(pluralOverrides) == 2 {
121 plural = pluralOverrides[1]
125 switch v := reflect.ValueOf(items); v.Kind() {
127 if items.(int) != 1 {
135 templateLog.Error("pluralize: unexpected type: ", "value", v)
140 // Format a date according to the application's default date(time) format.
141 "date": func(date time.Time) string {
142 return date.Format(DateFormat)
144 "datetime": func(date time.Time) string {
145 return date.Format(DateTimeFormat)
147 // Fetch an object from the session.
148 "session": func(key string, viewArgs map[string]interface{}) interface{} {
150 if c, found := viewArgs["_controller"]; found {
151 if v, err := c.(*Controller).Session.Get(key); err == nil {
154 templateLog.Errorf("template.session, key %s error %v", key, err)
157 templateLog.Warnf("template.session, key %s requested without controller", key)
160 templateLog.Warnf("template.session, key %s requested passing in view args", key)
166 "even": func(a int) bool { return (a % 2) == 0 },
168 // Using https://github.com/xeonx/timeago
170 "i18ntemplate": func(args ...interface{}) (template.HTML, error) {
171 templateName, lang := "", ""
172 var viewArgs interface{}
175 templateLog.Error("i18ntemplate: No arguments passed to template call")
177 // Assume only the template name is passed in
178 templateName = args[0].(string)
180 // Assume template name and viewArgs is passed in
181 templateName = args[0].(string)
183 // Try to extract language from the view args
184 if viewargsmap, ok := viewArgs.(map[string]interface{}); ok {
185 lang, _ = viewargsmap[CurrentLocaleViewArg].(string)
188 // Assume third argument is the region
189 templateName = args[0].(string)
191 lang, _ = args[2].(string)
193 templateLog.Error("i18ntemplate: Received more parameters then needed for", "template", templateName)
199 tmpl, err := MainTemplateLoader.TemplateLang(templateName, lang)
201 err = tmpl.Render(&buf, viewArgs)
203 templateLog.Error("i18ntemplate: Failed to render i18ntemplate ", "name", templateName, "error", err)
205 return template.HTML(buf.String()), err
210 /////////////////////
211 // Template functions
212 /////////////////////
214 // ReverseURL returns a url capable of invoking a given controller method:
215 // "Application.ShowApp 123" => "/app/123"
216 func ReverseURL(args ...interface{}) (template.URL, error) {
218 return "", errors.New("no arguments provided to reverse route")
221 action := args[0].(string)
222 if action == "Root" {
223 return template.URL(AppRoot), nil
226 pathData, found := splitActionPath(nil, action, true)
229 return "", fmt.Errorf("reversing '%s', expected 'Controller.Action'", action)
232 // Look up the types.
234 if pathData.TypeOfController == nil {
235 return "", fmt.Errorf("Failed reversing %s: controller not found %#v", action, pathData)
238 // Note method name is case insensitive search
239 methodType := pathData.TypeOfController.Method(pathData.MethodName)
240 if methodType == nil {
241 return "", errors.New("revel/controller: In " + action + " failed to find function " + pathData.MethodName)
244 if len(methodType.Args) < len(args)-1 {
245 return "", fmt.Errorf("reversing %s: route defines %d args, but received %d",
246 action, len(methodType.Args), len(args)-1)
248 // Unbind the arguments.
249 argsByName := make(map[string]string)
250 // Bind any static args first
251 fixedParams := len(pathData.FixedParamsByName)
253 for i, argValue := range args[1:] {
254 Unbind(argsByName, methodType.Args[i+fixedParams].Name, argValue)
257 return template.URL(MainRouter.Reverse(args[0].(string), argsByName).URL), nil
260 func Slug(text string) string {
262 text = strings.ToLower(text)
263 text = invalidSlugPattern.ReplaceAllString(text, "")
264 text = whiteSpacePattern.ReplaceAllString(text, separator)
265 text = strings.Trim(text, separator)
269 var timeAgoLangs = map[string]timeago.Config{}
271 func TimeAgo(args ...interface{}) string {
273 datetime := time.Now()
275 var viewArgs interface{}
278 templateLog.Error("TimeAgo: No arguments passed to timeago")
280 // only the time is passed in
281 datetime = args[0].(time.Time)
283 // time and region is passed in
284 datetime = args[0].(time.Time)
285 switch v := reflect.ValueOf(args[1]); v.Kind() {
287 // second params type string equals region
288 lang, _ = args[1].(string)
290 // second params type map equals viewArgs
292 if viewargsmap, ok := viewArgs.(map[string]interface{}); ok {
293 lang, _ = viewargsmap[CurrentLocaleViewArg].(string)
296 templateLog.Error("TimeAgo: unexpected type: ", "value", v)
299 // Assume third argument is the region
300 datetime = args[0].(time.Time)
301 if reflect.ValueOf(args[1]).Kind() != reflect.Map {
302 templateLog.Error("TimeAgo: unexpected type", "value", args[1])
304 if reflect.ValueOf(args[2]).Kind() != reflect.String {
305 templateLog.Error("TimeAgo: unexpected type: ", "value", args[2])
308 lang, _ = args[2].(string)
310 templateLog.Error("TimeAgo: Received more parameters then needed for timeago")
314 lang, _ = Config.String(defaultLanguageOption)
316 timeAgoLangs[lang] = timeago.English
319 _, ok := timeAgoLangs[lang]
321 timeAgoLangs[lang] = timeago.Config{
323 PastSuffix: " " + MessageFunc(lang, "ago"),
324 FuturePrefix: MessageFunc(lang, "in") + " ",
326 Periods: []timeago.FormatPeriod{
327 {time.Second, MessageFunc(lang, "about a second"), MessageFunc(lang, "%d seconds")},
328 {time.Minute, MessageFunc(lang, "about a minute"), MessageFunc(lang, "%d minutes")},
329 {time.Hour, MessageFunc(lang, "about an hour"), MessageFunc(lang, "%d hours")},
330 {timeago.Day, MessageFunc(lang, "one day"), MessageFunc(lang, "%d days")},
331 {timeago.Month, MessageFunc(lang, "one month"), MessageFunc(lang, "%d months")},
332 {timeago.Year, MessageFunc(lang, "one year"), MessageFunc(lang, "%d years")},
334 Zero: MessageFunc(lang, "about a second"),
336 DefaultLayout: "2006-01-02",
340 return timeAgoLangs[lang].Format(datetime)