package renderer import ( "fmt" "html/template" "io/ioutil" "net/url" "os" "reflect" "strconv" "strings" "time" "git.andreafazzi.eu/andrea/oef/errors" "git.andreafazzi.eu/andrea/oef/i18n" "github.com/dchest/captcha" jwt "github.com/dgrijalva/jwt-go" "github.com/jinzhu/inflection" yml "gopkg.in/yaml.v2" "github.com/microcosm-cc/bluemonday" "gopkg.in/russross/blackfriday.v2" ) const ( MaxTextLength = 20 ) var ( funcMap = template.FuncMap{ "env": env, "genCaptcha": genCaptcha, "markdown": markdown, "version": version, "toInt": toInt, "isResponseIn": isResponseIn, "query": query, "convertDate": convertDate, "convertTime": convertTime, "prettyDate": prettyDate, "prettyTime": prettyTime, "prettyDateTime": prettyDateTime, "zeroTime": zeroTime, "seconds": seconds, "modelPath": modelPath, "dict": dict, "yaml": yaml, "create": create, "update": update, "delete": delete, "show": show, "all": all, "execute": execute, "isSlice": isSlice, "toSlice": toSlice, "string": callString, "incr": incr, "mod2": mod2, "toLower": toLower, "anchor": anchor, "alertLink": alertLink, "html": html, "field": field, "modelName": modelName, "active": active, "pluralize": pluralize, "lower": lower, "trim": trim, "username": username, "isAdmin": isAdmin, "isSchool": isSchool, "modelId": modelId, "isParticipant": isParticipant, "isSubscriber": isSubscriber, "attr": attr, } ) func env(key string) string { return os.Getenv(key) } func genCaptcha() string { return captcha.New() } func markdown(text string) string { sanitized := strings.Replace(text, "\r\n", "\n", -1) unsafe := blackfriday.Run([]byte(sanitized)) return string(bluemonday.UGCPolicy().SanitizeBytes(unsafe)) } func version() string { version, err := ioutil.ReadFile("./VERSION") if err != nil { return "" } return strings.TrimSpace(string(version)) } func toInt(value float64) int { return int(value) } func isResponseIn(id uint, answersIDs string) (bool, error) { if answersIDs != "" { ids := make([]uint, 0) srIDs := strings.Split(answersIDs, " ") for _, srID := range srIDs { id, err := strconv.Atoi(srID) if err != nil { return false, err } ids = append(ids, uint(id)) } for _, v := range ids { if v == id { return true, nil } } } return false, nil } func username(claims jwt.MapClaims) string { return claims["username"].(string) } func modelId(claims jwt.MapClaims) (uint, error) { id, err := strconv.Atoi(claims["model_id"].(string)) if err != nil { return 0, err } return uint(id), nil } func getRole(claims jwt.MapClaims) (string, error) { role, ok := claims["role"] if !ok { return "", errors.NotAuthorized } return role.(string), nil } func isAdmin(claims jwt.MapClaims) (bool, error) { role, err := getRole(claims) if err != nil { return false, err } return role == "administrator", nil } func isParticipant(claims jwt.MapClaims) (bool, error) { role, err := getRole(claims) if err != nil { return false, err } return role == "participant", nil } func isSchool(claims jwt.MapClaims) (bool, error) { role, err := getRole(claims) if err != nil { return false, err } return role == "school", nil } func isSubscriber(claims jwt.MapClaims) (bool, error) { role, err := getRole(claims) if err != nil { return false, err } return role == "subscriber", nil } func trim(text string) string { if len(text) > MaxTextLength { return text[0:MaxTextLength] + "…" } return text } func modelName(value interface{}) string { t := reflect.TypeOf(value) switch t.Kind() { case reflect.Ptr: return t.Elem().Name() case reflect.Slice: return strings.Replace(t.Elem().String(), "*orm.", "", -1) default: return t.Name() } } func lower(text string) string { return strings.ToLower(text) } func pluralize(text string) string { return inflection.Plural(text) } func active(value string, options url.Values) string { if len(options["tpl_content"]) > 0 { model := strings.Title(inflection.Singular(strings.Split(options["tpl_content"][0], "_")[0])) if value == model { return "active" } } return "" } func field(name string, value interface{}) interface{} { if value != nil { s := reflect.ValueOf(value).Elem() return s.FieldByName(name).Interface() } else { return nil } } func html(content string) template.HTML { return template.HTML(content) } func attr(attr string) template.HTMLAttr { return template.HTMLAttr(attr) } func anchor(text, url string) template.HTML { return template.HTML(fmt.Sprintf("%s", url, text)) } func alertLink(text, url string) template.HTML { return template.HTML(fmt.Sprintf("%s", url, text)) } func toLower(text string) string { return strings.ToLower(text) } func mod2(value int) bool { return value%2 == 0 } func incr(value int) int { return value + 1 } func callString(value interface{}) string { if value != nil { switch reflect.ValueOf(value).Kind() { case reflect.String: return value.(string) case reflect.Bool: if value.(bool) { return i18n.Text["answerCorrect"]["it"] } return "false" default: return reflect.ValueOf(value).MethodByName("String").Interface().(func() string)() } } else { return "" } } func isSlice(value interface{}) bool { return reflect.TypeOf(value).Kind() == reflect.Slice } func yaml(content string) (interface{}, error) { var result interface{} err := yml.Unmarshal([]byte(content), &result) if err != nil { return nil, err } return result, nil } func dict(values ...interface{}) (map[string]interface{}, error) { if len(values)%2 != 0 { return nil, fmt.Errorf("Invalid dict call, numbers of arguments is %d but should be a multiple of two", len(values)) } dict := make(map[string]interface{}, len(values)/2) for i := 0; i < len(values); i += 2 { key, ok := values[i].(string) if !ok { return nil, fmt.Errorf("%s", "dict keys must be strings") } dict[key] = values[i+1] } return dict, nil } func toSlice(values ...string) interface{} { var result []string result = append(result, values...) return result } func getType(myvar interface{}) (res string) { t := reflect.TypeOf(myvar) for t.Kind() == reflect.Ptr { t = t.Elem() res += "*" } return res + t.Name() } func query(values ...string) template.URL { var ( urlValues url.Values format bool ) urlValues = make(url.Values) for i := 0; i < len(values); i += 2 { if values[i] == "format" { format = true } urlValues.Add(values[i], values[i+1]) } if !format { urlValues.Set("format", "html") } return template.URL(urlValues.Encode()) } func convertDate(value interface{}) string { t, ok := value.(time.Time) if !ok { return "" } return fmt.Sprintf("%d-%02d-%02d", t.Year(), t.Month(), t.Day()) } func prettyDate(value interface{}) string { t, ok := value.(time.Time) if !ok { return "" } if zeroTime(&t) { return i18n.Text["alwaysActiveContest"]["it"] } return fmt.Sprintf("%02d/%02d/%d", t.Day(), t.Month(), t.Year()) } func prettyDateTime(value interface{}) string { t, ok := value.(time.Time) if !ok { return "" } return fmt.Sprintf(i18n.Formats["dateTime"]["it"], t.Day(), t.Month(), t.Year(), t.Hour(), t.Minute()) } func convertTime(value interface{}) string { t, ok := value.(time.Time) if !ok { return "" } return fmt.Sprintf("%02d:%02d", t.Hour(), t.Minute()) } func prettyTime(value interface{}) string { return convertTime(value) } func zeroTime(t *time.Time) bool { return *t == time.Time{} } func seconds(d time.Duration) int { return int(d.Seconds()) } func modelPath(model string, action string, id uint) string { var q template.URL action = strings.ToLower(action) plural := inflection.Plural(strings.ToLower(model)) if action != "" { switch action { case "show": q = query("tpl_layout", "base", "tpl_content", fmt.Sprintf("%s_%s", plural, action)) return fmt.Sprintf("/%s/%d?%s", plural, id, q) case "update": q = query("tpl_layout", "base", "tpl_content", fmt.Sprintf("%s_add_update", plural), "update", "true") return fmt.Sprintf("/%s/%d/%s?%s", plural, id, action, q) case "create": q = query("tpl_layout", "base", "tpl_content", fmt.Sprintf("%s_add_update", plural)) return fmt.Sprintf("/%s/%s/?%s", plural, action, q) case "delete": q = query("tpl_layout", "base", "tpl_content", fmt.Sprintf("%s_%s", plural, action)) return fmt.Sprintf("/%s/%d/%s?%s", plural, id, action, q) } } q = query("tpl_layout", "base", "tpl_content", plural) return fmt.Sprintf("/%s?%s", plural, q) } func all(model string) string { plural := inflection.Plural(strings.ToLower(model)) q := query("tpl_layout", "base", "tpl_content", plural) return fmt.Sprintf("/%s?%s", plural, q) } func create(model string) string { action := "create" plural := inflection.Plural(strings.ToLower(model)) q := query("tpl_layout", "base", "tpl_content", fmt.Sprintf("%s_add_update", plural)) return fmt.Sprintf("/%s/%s/?%s", plural, action, q) } func show(model string, id uint, format ...string) string { action := "show" plural := inflection.Plural(strings.ToLower(model)) args := []string{"tpl_layout", "base", "tpl_content", fmt.Sprintf("%s_%s", plural, action)} if len(format) > 0 { args = append(args, []string{"format", format[0]}...) } // q := query("tpl_layout", "base", "tpl_content", fmt.Sprintf("%s_%s", plural, action)) q := query(args...) return fmt.Sprintf("/%s/%d?%s", plural, id, q) } func execute(model string, id uint) string { action := "execute" plural := inflection.Plural(strings.ToLower(model)) q := query("tpl_layout", "base", "tpl_content", fmt.Sprintf("%s_%s", plural, action)) return fmt.Sprintf("/%s/%d/%s?%s", plural, id, action, q) } func update(model string, id uint) string { action := "update" plural := inflection.Plural(strings.ToLower(model)) q := query("tpl_layout", "base", "tpl_content", fmt.Sprintf("%s_add_update", plural), "update", "true") return fmt.Sprintf("/%s/%d/%s?%s", plural, id, action, q) } func delete(model string, id uint) string { action := "delete" plural := inflection.Plural(strings.ToLower(model)) q := query("tpl_layout", "base", "tpl_content", fmt.Sprintf("%s_%s", plural, action)) return fmt.Sprintf("/%s/%d/%s?%s", plural, id, action, q) }