diff options
Diffstat (limited to 'modules/base/tool.go')
-rw-r--r-- | modules/base/tool.go | 529 |
1 files changed, 529 insertions, 0 deletions
diff --git a/modules/base/tool.go b/modules/base/tool.go new file mode 100644 index 0000000000..0dec7aa8dd --- /dev/null +++ b/modules/base/tool.go @@ -0,0 +1,529 @@ +// Copyright 2014 The Gogs Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package base + +import ( + "bytes" + "crypto/md5" + "crypto/rand" + "crypto/sha1" + "encoding/hex" + "encoding/json" + "fmt" + "math" + "strconv" + "strings" + "time" +) + +// Encode string to md5 hex value +func EncodeMd5(str string) string { + m := md5.New() + m.Write([]byte(str)) + return hex.EncodeToString(m.Sum(nil)) +} + +// GetRandomString generate random string by specify chars. +func GetRandomString(n int, alphabets ...byte) string { + const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + var bytes = make([]byte, n) + rand.Read(bytes) + for i, b := range bytes { + if len(alphabets) == 0 { + bytes[i] = alphanum[b%byte(len(alphanum))] + } else { + bytes[i] = alphabets[b%byte(len(alphabets))] + } + } + return string(bytes) +} + +// verify time limit code +func VerifyTimeLimitCode(data string, minutes int, code string) bool { + if len(code) <= 18 { + return false + } + + // split code + start := code[:12] + lives := code[12:18] + if d, err := StrTo(lives).Int(); err == nil { + minutes = d + } + + // right active code + retCode := CreateTimeLimitCode(data, minutes, start) + if retCode == code && minutes > 0 { + // check time is expired or not + before, _ := DateParse(start, "YmdHi") + now := time.Now() + if before.Add(time.Minute*time.Duration(minutes)).Unix() > now.Unix() { + return true + } + } + + return false +} + +const TimeLimitCodeLength = 12 + 6 + 40 + +// create a time limit code +// code format: 12 length date time string + 6 minutes string + 40 sha1 encoded string +func CreateTimeLimitCode(data string, minutes int, startInf interface{}) string { + format := "YmdHi" + + var start, end time.Time + var startStr, endStr string + + if startInf == nil { + // Use now time create code + start = time.Now() + startStr = DateFormat(start, format) + } else { + // use start string create code + startStr = startInf.(string) + start, _ = DateParse(startStr, format) + startStr = DateFormat(start, format) + } + + end = start.Add(time.Minute * time.Duration(minutes)) + endStr = DateFormat(end, format) + + // create sha1 encode string + sh := sha1.New() + sh.Write([]byte(data + SecretKey + startStr + endStr + ToStr(minutes))) + encoded := hex.EncodeToString(sh.Sum(nil)) + + code := fmt.Sprintf("%s%06d%s", startStr, minutes, encoded) + return code +} + +// AvatarLink returns avatar link by given e-mail. +func AvatarLink(email string) string { + return "/avatar/" + EncodeMd5(email) +} + +// Seconds-based time units +const ( + Minute = 60 + Hour = 60 * Minute + Day = 24 * Hour + Week = 7 * Day + Month = 30 * Day + Year = 12 * Month +) + +func computeTimeDiff(diff int64) (int64, string) { + diffStr := "" + switch { + case diff <= 0: + diff = 0 + diffStr = "now" + case diff < 2: + diff = 0 + diffStr = "1 second" + case diff < 1*Minute: + diffStr = fmt.Sprintf("%d seconds", diff) + diff = 0 + + case diff < 2*Minute: + diff -= 1 * Minute + diffStr = "1 minute" + case diff < 1*Hour: + diffStr = fmt.Sprintf("%d minutes", diff/Minute) + diff -= diff / Minute * Minute + + case diff < 2*Hour: + diff -= 1 * Hour + diffStr = "1 hour" + case diff < 1*Day: + diffStr = fmt.Sprintf("%d hours", diff/Hour) + diff -= diff / Hour * Hour + + case diff < 2*Day: + diff -= 1 * Day + diffStr = "1 day" + case diff < 1*Week: + diffStr = fmt.Sprintf("%d days", diff/Day) + diff -= diff / Day * Day + + case diff < 2*Week: + diff -= 1 * Week + diffStr = "1 week" + case diff < 1*Month: + diffStr = fmt.Sprintf("%d weeks", diff/Week) + diff -= diff / Week * Week + + case diff < 2*Month: + diff -= 1 * Month + diffStr = "1 month" + case diff < 1*Year: + diffStr = fmt.Sprintf("%d months", diff/Month) + diff -= diff / Month * Month + + case diff < 2*Year: + diff -= 1 * Year + diffStr = "1 year" + default: + diffStr = fmt.Sprintf("%d years", diff/Year) + diff = 0 + } + return diff, diffStr +} + +// TimeSincePro calculates the time interval and generate full user-friendly string. +func TimeSincePro(then time.Time) string { + now := time.Now() + diff := now.Unix() - then.Unix() + + if then.After(now) { + return "future" + } + + var timeStr, diffStr string + for { + if diff == 0 { + break + } + + diff, diffStr = computeTimeDiff(diff) + timeStr += ", " + diffStr + } + return strings.TrimPrefix(timeStr, ", ") +} + +// TimeSince calculates the time interval and generate user-friendly string. +func TimeSince(then time.Time) string { + now := time.Now() + + lbl := "ago" + diff := now.Unix() - then.Unix() + if then.After(now) { + lbl = "from now" + diff = then.Unix() - now.Unix() + } + + switch { + case diff <= 0: + return "now" + case diff <= 2: + return fmt.Sprintf("1 second %s", lbl) + case diff < 1*Minute: + return fmt.Sprintf("%d seconds %s", diff, lbl) + + case diff < 2*Minute: + return fmt.Sprintf("1 minute %s", lbl) + case diff < 1*Hour: + return fmt.Sprintf("%d minutes %s", diff/Minute, lbl) + + case diff < 2*Hour: + return fmt.Sprintf("1 hour %s", lbl) + case diff < 1*Day: + return fmt.Sprintf("%d hours %s", diff/Hour, lbl) + + case diff < 2*Day: + return fmt.Sprintf("1 day %s", lbl) + case diff < 1*Week: + return fmt.Sprintf("%d days %s", diff/Day, lbl) + + case diff < 2*Week: + return fmt.Sprintf("1 week %s", lbl) + case diff < 1*Month: + return fmt.Sprintf("%d weeks %s", diff/Week, lbl) + + case diff < 2*Month: + return fmt.Sprintf("1 month %s", lbl) + case diff < 1*Year: + return fmt.Sprintf("%d months %s", diff/Month, lbl) + + case diff < 2*Year: + return fmt.Sprintf("1 year %s", lbl) + default: + return fmt.Sprintf("%d years %s", diff/Year, lbl) + } + return then.String() +} + +const ( + Byte = 1 + KByte = Byte * 1024 + MByte = KByte * 1024 + GByte = MByte * 1024 + TByte = GByte * 1024 + PByte = TByte * 1024 + EByte = PByte * 1024 +) + +var bytesSizeTable = map[string]uint64{ + "b": Byte, + "kb": KByte, + "mb": MByte, + "gb": GByte, + "tb": TByte, + "pb": PByte, + "eb": EByte, +} + +func logn(n, b float64) float64 { + return math.Log(n) / math.Log(b) +} + +func humanateBytes(s uint64, base float64, sizes []string) string { + if s < 10 { + return fmt.Sprintf("%dB", s) + } + e := math.Floor(logn(float64(s), base)) + suffix := sizes[int(e)] + val := float64(s) / math.Pow(base, math.Floor(e)) + f := "%.0f" + if val < 10 { + f = "%.1f" + } + + return fmt.Sprintf(f+"%s", val, suffix) +} + +// FileSize calculates the file size and generate user-friendly string. +func FileSize(s int64) string { + sizes := []string{"B", "KB", "MB", "GB", "TB", "PB", "EB"} + return humanateBytes(uint64(s), 1024, sizes) +} + +// Subtract deals with subtraction of all types of number. +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)) + } +} + +// DateFormat pattern rules. +var datePatterns = []string{ + // year + "Y", "2006", // A full numeric representation of a year, 4 digits Examples: 1999 or 2003 + "y", "06", //A two digit representation of a year Examples: 99 or 03 + + // month + "m", "01", // Numeric representation of a month, with leading zeros 01 through 12 + "n", "1", // Numeric representation of a month, without leading zeros 1 through 12 + "M", "Jan", // A short textual representation of a month, three letters Jan through Dec + "F", "January", // A full textual representation of a month, such as January or March January through December + + // day + "d", "02", // Day of the month, 2 digits with leading zeros 01 to 31 + "j", "2", // Day of the month without leading zeros 1 to 31 + + // week + "D", "Mon", // A textual representation of a day, three letters Mon through Sun + "l", "Monday", // A full textual representation of the day of the week Sunday through Saturday + + // time + "g", "3", // 12-hour format of an hour without leading zeros 1 through 12 + "G", "15", // 24-hour format of an hour without leading zeros 0 through 23 + "h", "03", // 12-hour format of an hour with leading zeros 01 through 12 + "H", "15", // 24-hour format of an hour with leading zeros 00 through 23 + + "a", "pm", // Lowercase Ante meridiem and Post meridiem am or pm + "A", "PM", // Uppercase Ante meridiem and Post meridiem AM or PM + + "i", "04", // Minutes with leading zeros 00 to 59 + "s", "05", // Seconds, with leading zeros 00 through 59 + + // time zone + "T", "MST", + "P", "-07:00", + "O", "-0700", + + // RFC 2822 + "r", time.RFC1123Z, +} + +// Parse Date use PHP time format. +func DateParse(dateString, format string) (time.Time, error) { + replacer := strings.NewReplacer(datePatterns...) + format = replacer.Replace(format) + return time.ParseInLocation(format, dateString, time.Local) +} + +// Date takes a PHP like date func to Go's time format. +func DateFormat(t time.Time, format string) string { + replacer := strings.NewReplacer(datePatterns...) + format = replacer.Replace(format) + return t.Format(format) +} + +// convert string to specify type + +type StrTo string + +func (f StrTo) Exist() bool { + return string(f) != string(0x1E) +} + +func (f StrTo) Int() (int, error) { + v, err := strconv.ParseInt(f.String(), 10, 32) + return int(v), err +} + +func (f StrTo) String() string { + if f.Exist() { + return string(f) + } + return "" +} + +// convert any type to string +func ToStr(value interface{}, args ...int) (s string) { + switch v := value.(type) { + case bool: + s = strconv.FormatBool(v) + case float32: + s = strconv.FormatFloat(float64(v), 'f', argInt(args).Get(0, -1), argInt(args).Get(1, 32)) + case float64: + s = strconv.FormatFloat(v, 'f', argInt(args).Get(0, -1), argInt(args).Get(1, 64)) + case int: + s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10)) + case int8: + s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10)) + case int16: + s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10)) + case int32: + s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10)) + case int64: + s = strconv.FormatInt(v, argInt(args).Get(0, 10)) + case uint: + s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10)) + case uint8: + s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10)) + case uint16: + s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10)) + case uint32: + s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10)) + case uint64: + s = strconv.FormatUint(v, argInt(args).Get(0, 10)) + case string: + s = v + case []byte: + s = string(v) + default: + s = fmt.Sprintf("%v", v) + } + return s +} + +type argInt []int + +func (a argInt) Get(i int, args ...int) (r int) { + if i >= 0 && i < len(a) { + r = a[i] + } + if len(args) > 0 { + r = args[0] + } + return +} + +type Actioner interface { + GetOpType() int + GetActUserName() string + GetRepoName() string + GetBranch() string + GetContent() string +} + +// ActionIcon accepts a int that represents action operation type +// and returns a icon class name. +func ActionIcon(opType int) string { + switch opType { + case 1: // Create repository. + return "plus-circle" + case 5: // Commit repository. + return "arrow-circle-o-right" + default: + return "invalid type" + } +} + +const ( + TPL_CREATE_REPO = `<a href="/user/%s">%s</a> created repository <a href="/%s/%s">%s</a>` + TPL_COMMIT_REPO = `<a href="/user/%s">%s</a> pushed to <a href="/%s/%s/tree/%s">%s</a> at <a href="/%s/%s">%s/%s</a>%s` + TPL_COMMIT_REPO_LI = `<div><img id="gogs-user-avatar-commit" src="%s?s=16" alt="user-avatar" title="username"/> <a href="/%s/%s/commit/%s">%s</a> %s</div>` +) + +type PushCommits struct { + Len int + Commits [][]string +} + +// ActionDesc accepts int that represents action operation type +// and returns the description. +func ActionDesc(act Actioner, avatarLink string) string { + actUserName := act.GetActUserName() + repoName := act.GetRepoName() + branch := act.GetBranch() + content := act.GetContent() + switch act.GetOpType() { + case 1: // Create repository. + return fmt.Sprintf(TPL_CREATE_REPO, actUserName, actUserName, actUserName, repoName, repoName) + case 5: // Commit repository. + var push *PushCommits + if err := json.Unmarshal([]byte(content), &push); err != nil { + return err.Error() + } + buf := bytes.NewBuffer([]byte("\n")) + for _, commit := range push.Commits { + buf.WriteString(fmt.Sprintf(TPL_COMMIT_REPO_LI, avatarLink, actUserName, repoName, commit[0], commit[0][:7], commit[1]) + "\n") + } + if push.Len > 3 { + buf.WriteString(fmt.Sprintf(`<div><a href="/%s/%s/commits/%s">%d other commits >></a></div>`, actUserName, repoName, branch, push.Len)) + } + return fmt.Sprintf(TPL_COMMIT_REPO, actUserName, actUserName, actUserName, repoName, branch, branch, actUserName, repoName, actUserName, repoName, + buf.String()) + default: + return "invalid type" + } +} |