You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

tool.go 13 kB

11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package base
  5. import (
  6. "bytes"
  7. "crypto/md5"
  8. "crypto/rand"
  9. "crypto/sha1"
  10. "encoding/hex"
  11. "encoding/json"
  12. "fmt"
  13. "math"
  14. "strconv"
  15. "strings"
  16. "time"
  17. )
  18. // Encode string to md5 hex value
  19. func EncodeMd5(str string) string {
  20. m := md5.New()
  21. m.Write([]byte(str))
  22. return hex.EncodeToString(m.Sum(nil))
  23. }
  24. // GetRandomString generate random string by specify chars.
  25. func GetRandomString(n int, alphabets ...byte) string {
  26. const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
  27. var bytes = make([]byte, n)
  28. rand.Read(bytes)
  29. for i, b := range bytes {
  30. if len(alphabets) == 0 {
  31. bytes[i] = alphanum[b%byte(len(alphanum))]
  32. } else {
  33. bytes[i] = alphabets[b%byte(len(alphabets))]
  34. }
  35. }
  36. return string(bytes)
  37. }
  38. // verify time limit code
  39. func VerifyTimeLimitCode(data string, minutes int, code string) bool {
  40. if len(code) <= 18 {
  41. return false
  42. }
  43. // split code
  44. start := code[:12]
  45. lives := code[12:18]
  46. if d, err := StrTo(lives).Int(); err == nil {
  47. minutes = d
  48. }
  49. // right active code
  50. retCode := CreateTimeLimitCode(data, minutes, start)
  51. if retCode == code && minutes > 0 {
  52. // check time is expired or not
  53. before, _ := DateParse(start, "YmdHi")
  54. now := time.Now()
  55. if before.Add(time.Minute*time.Duration(minutes)).Unix() > now.Unix() {
  56. return true
  57. }
  58. }
  59. return false
  60. }
  61. const TimeLimitCodeLength = 12 + 6 + 40
  62. // create a time limit code
  63. // code format: 12 length date time string + 6 minutes string + 40 sha1 encoded string
  64. func CreateTimeLimitCode(data string, minutes int, startInf interface{}) string {
  65. format := "YmdHi"
  66. var start, end time.Time
  67. var startStr, endStr string
  68. if startInf == nil {
  69. // Use now time create code
  70. start = time.Now()
  71. startStr = DateFormat(start, format)
  72. } else {
  73. // use start string create code
  74. startStr = startInf.(string)
  75. start, _ = DateParse(startStr, format)
  76. startStr = DateFormat(start, format)
  77. }
  78. end = start.Add(time.Minute * time.Duration(minutes))
  79. endStr = DateFormat(end, format)
  80. // create sha1 encode string
  81. sh := sha1.New()
  82. sh.Write([]byte(data + SecretKey + startStr + endStr + ToStr(minutes)))
  83. encoded := hex.EncodeToString(sh.Sum(nil))
  84. code := fmt.Sprintf("%s%06d%s", startStr, minutes, encoded)
  85. return code
  86. }
  87. // AvatarLink returns avatar link by given e-mail.
  88. func AvatarLink(email string) string {
  89. return "http://1.gravatar.com/avatar/" + EncodeMd5(email)
  90. }
  91. // Seconds-based time units
  92. const (
  93. Minute = 60
  94. Hour = 60 * Minute
  95. Day = 24 * Hour
  96. Week = 7 * Day
  97. Month = 30 * Day
  98. Year = 12 * Month
  99. )
  100. func computeTimeDiff(diff int64) (int64, string) {
  101. diffStr := ""
  102. switch {
  103. case diff <= 0:
  104. diff = 0
  105. diffStr = "now"
  106. case diff < 2:
  107. diff = 0
  108. diffStr = "1 second"
  109. case diff < 1*Minute:
  110. diffStr = fmt.Sprintf("%d seconds", diff)
  111. diff = 0
  112. case diff < 2*Minute:
  113. diff -= 1 * Minute
  114. diffStr = "1 minute"
  115. case diff < 1*Hour:
  116. diffStr = fmt.Sprintf("%d minutes", diff/Minute)
  117. diff -= diff / Minute * Minute
  118. case diff < 2*Hour:
  119. diff -= 1 * Hour
  120. diffStr = "1 hour"
  121. case diff < 1*Day:
  122. diffStr = fmt.Sprintf("%d hours", diff/Hour)
  123. diff -= diff / Hour * Hour
  124. case diff < 2*Day:
  125. diff -= 1 * Day
  126. diffStr = "1 day"
  127. case diff < 1*Week:
  128. diffStr = fmt.Sprintf("%d days", diff/Day)
  129. diff -= diff / Day * Day
  130. case diff < 2*Week:
  131. diff -= 1 * Week
  132. diffStr = "1 week"
  133. case diff < 1*Month:
  134. diffStr = fmt.Sprintf("%d weeks", diff/Week)
  135. diff -= diff / Week * Week
  136. case diff < 2*Month:
  137. diff -= 1 * Month
  138. diffStr = "1 month"
  139. case diff < 1*Year:
  140. diffStr = fmt.Sprintf("%d months", diff/Month)
  141. diff -= diff / Month * Month
  142. case diff < 2*Year:
  143. diff -= 1 * Year
  144. diffStr = "1 year"
  145. default:
  146. diffStr = fmt.Sprintf("%d years", diff/Year)
  147. diff = 0
  148. }
  149. return diff, diffStr
  150. }
  151. // TimeSincePro calculates the time interval and generate full user-friendly string.
  152. func TimeSincePro(then time.Time) string {
  153. now := time.Now()
  154. diff := now.Unix() - then.Unix()
  155. if then.After(now) {
  156. return "future"
  157. }
  158. var timeStr, diffStr string
  159. for {
  160. if diff == 0 {
  161. break
  162. }
  163. diff, diffStr = computeTimeDiff(diff)
  164. timeStr += ", " + diffStr
  165. }
  166. return strings.TrimPrefix(timeStr, ", ")
  167. }
  168. // TimeSince calculates the time interval and generate user-friendly string.
  169. func TimeSince(then time.Time) string {
  170. now := time.Now()
  171. lbl := "ago"
  172. diff := now.Unix() - then.Unix()
  173. if then.After(now) {
  174. lbl = "from now"
  175. diff = then.Unix() - now.Unix()
  176. }
  177. switch {
  178. case diff <= 0:
  179. return "now"
  180. case diff <= 2:
  181. return fmt.Sprintf("1 second %s", lbl)
  182. case diff < 1*Minute:
  183. return fmt.Sprintf("%d seconds %s", diff, lbl)
  184. case diff < 2*Minute:
  185. return fmt.Sprintf("1 minute %s", lbl)
  186. case diff < 1*Hour:
  187. return fmt.Sprintf("%d minutes %s", diff/Minute, lbl)
  188. case diff < 2*Hour:
  189. return fmt.Sprintf("1 hour %s", lbl)
  190. case diff < 1*Day:
  191. return fmt.Sprintf("%d hours %s", diff/Hour, lbl)
  192. case diff < 2*Day:
  193. return fmt.Sprintf("1 day %s", lbl)
  194. case diff < 1*Week:
  195. return fmt.Sprintf("%d days %s", diff/Day, lbl)
  196. case diff < 2*Week:
  197. return fmt.Sprintf("1 week %s", lbl)
  198. case diff < 1*Month:
  199. return fmt.Sprintf("%d weeks %s", diff/Week, lbl)
  200. case diff < 2*Month:
  201. return fmt.Sprintf("1 month %s", lbl)
  202. case diff < 1*Year:
  203. return fmt.Sprintf("%d months %s", diff/Month, lbl)
  204. case diff < 2*Year:
  205. return fmt.Sprintf("1 year %s", lbl)
  206. default:
  207. return fmt.Sprintf("%d years %s", diff/Year, lbl)
  208. }
  209. return then.String()
  210. }
  211. const (
  212. Byte = 1
  213. KByte = Byte * 1024
  214. MByte = KByte * 1024
  215. GByte = MByte * 1024
  216. TByte = GByte * 1024
  217. PByte = TByte * 1024
  218. EByte = PByte * 1024
  219. )
  220. var bytesSizeTable = map[string]uint64{
  221. "b": Byte,
  222. "kb": KByte,
  223. "mb": MByte,
  224. "gb": GByte,
  225. "tb": TByte,
  226. "pb": PByte,
  227. "eb": EByte,
  228. }
  229. func logn(n, b float64) float64 {
  230. return math.Log(n) / math.Log(b)
  231. }
  232. func humanateBytes(s uint64, base float64, sizes []string) string {
  233. if s < 10 {
  234. return fmt.Sprintf("%dB", s)
  235. }
  236. e := math.Floor(logn(float64(s), base))
  237. suffix := sizes[int(e)]
  238. val := float64(s) / math.Pow(base, math.Floor(e))
  239. f := "%.0f"
  240. if val < 10 {
  241. f = "%.1f"
  242. }
  243. return fmt.Sprintf(f+"%s", val, suffix)
  244. }
  245. // FileSize calculates the file size and generate user-friendly string.
  246. func FileSize(s int64) string {
  247. sizes := []string{"B", "KB", "MB", "GB", "TB", "PB", "EB"}
  248. return humanateBytes(uint64(s), 1024, sizes)
  249. }
  250. // Subtract deals with subtraction of all types of number.
  251. func Subtract(left interface{}, right interface{}) interface{} {
  252. var rleft, rright int64
  253. var fleft, fright float64
  254. var isInt bool = true
  255. switch left.(type) {
  256. case int:
  257. rleft = int64(left.(int))
  258. case int8:
  259. rleft = int64(left.(int8))
  260. case int16:
  261. rleft = int64(left.(int16))
  262. case int32:
  263. rleft = int64(left.(int32))
  264. case int64:
  265. rleft = left.(int64)
  266. case float32:
  267. fleft = float64(left.(float32))
  268. isInt = false
  269. case float64:
  270. fleft = left.(float64)
  271. isInt = false
  272. }
  273. switch right.(type) {
  274. case int:
  275. rright = int64(right.(int))
  276. case int8:
  277. rright = int64(right.(int8))
  278. case int16:
  279. rright = int64(right.(int16))
  280. case int32:
  281. rright = int64(right.(int32))
  282. case int64:
  283. rright = right.(int64)
  284. case float32:
  285. fright = float64(left.(float32))
  286. isInt = false
  287. case float64:
  288. fleft = left.(float64)
  289. isInt = false
  290. }
  291. if isInt {
  292. return rleft - rright
  293. } else {
  294. return fleft + float64(rleft) - (fright + float64(rright))
  295. }
  296. }
  297. // DateFormat pattern rules.
  298. var datePatterns = []string{
  299. // year
  300. "Y", "2006", // A full numeric representation of a year, 4 digits Examples: 1999 or 2003
  301. "y", "06", //A two digit representation of a year Examples: 99 or 03
  302. // month
  303. "m", "01", // Numeric representation of a month, with leading zeros 01 through 12
  304. "n", "1", // Numeric representation of a month, without leading zeros 1 through 12
  305. "M", "Jan", // A short textual representation of a month, three letters Jan through Dec
  306. "F", "January", // A full textual representation of a month, such as January or March January through December
  307. // day
  308. "d", "02", // Day of the month, 2 digits with leading zeros 01 to 31
  309. "j", "2", // Day of the month without leading zeros 1 to 31
  310. // week
  311. "D", "Mon", // A textual representation of a day, three letters Mon through Sun
  312. "l", "Monday", // A full textual representation of the day of the week Sunday through Saturday
  313. // time
  314. "g", "3", // 12-hour format of an hour without leading zeros 1 through 12
  315. "G", "15", // 24-hour format of an hour without leading zeros 0 through 23
  316. "h", "03", // 12-hour format of an hour with leading zeros 01 through 12
  317. "H", "15", // 24-hour format of an hour with leading zeros 00 through 23
  318. "a", "pm", // Lowercase Ante meridiem and Post meridiem am or pm
  319. "A", "PM", // Uppercase Ante meridiem and Post meridiem AM or PM
  320. "i", "04", // Minutes with leading zeros 00 to 59
  321. "s", "05", // Seconds, with leading zeros 00 through 59
  322. // time zone
  323. "T", "MST",
  324. "P", "-07:00",
  325. "O", "-0700",
  326. // RFC 2822
  327. "r", time.RFC1123Z,
  328. }
  329. // Parse Date use PHP time format.
  330. func DateParse(dateString, format string) (time.Time, error) {
  331. replacer := strings.NewReplacer(datePatterns...)
  332. format = replacer.Replace(format)
  333. return time.ParseInLocation(format, dateString, time.Local)
  334. }
  335. // Date takes a PHP like date func to Go's time format.
  336. func DateFormat(t time.Time, format string) string {
  337. replacer := strings.NewReplacer(datePatterns...)
  338. format = replacer.Replace(format)
  339. return t.Format(format)
  340. }
  341. // convert string to specify type
  342. type StrTo string
  343. func (f StrTo) Exist() bool {
  344. return string(f) != string(0x1E)
  345. }
  346. func (f StrTo) Int() (int, error) {
  347. v, err := strconv.ParseInt(f.String(), 10, 32)
  348. return int(v), err
  349. }
  350. func (f StrTo) String() string {
  351. if f.Exist() {
  352. return string(f)
  353. }
  354. return ""
  355. }
  356. // convert any type to string
  357. func ToStr(value interface{}, args ...int) (s string) {
  358. switch v := value.(type) {
  359. case bool:
  360. s = strconv.FormatBool(v)
  361. case float32:
  362. s = strconv.FormatFloat(float64(v), 'f', argInt(args).Get(0, -1), argInt(args).Get(1, 32))
  363. case float64:
  364. s = strconv.FormatFloat(v, 'f', argInt(args).Get(0, -1), argInt(args).Get(1, 64))
  365. case int:
  366. s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
  367. case int8:
  368. s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
  369. case int16:
  370. s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
  371. case int32:
  372. s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
  373. case int64:
  374. s = strconv.FormatInt(v, argInt(args).Get(0, 10))
  375. case uint:
  376. s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
  377. case uint8:
  378. s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
  379. case uint16:
  380. s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
  381. case uint32:
  382. s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
  383. case uint64:
  384. s = strconv.FormatUint(v, argInt(args).Get(0, 10))
  385. case string:
  386. s = v
  387. case []byte:
  388. s = string(v)
  389. default:
  390. s = fmt.Sprintf("%v", v)
  391. }
  392. return s
  393. }
  394. type argInt []int
  395. func (a argInt) Get(i int, args ...int) (r int) {
  396. if i >= 0 && i < len(a) {
  397. r = a[i]
  398. }
  399. if len(args) > 0 {
  400. r = args[0]
  401. }
  402. return
  403. }
  404. type Actioner interface {
  405. GetOpType() int
  406. GetActUserName() string
  407. GetRepoName() string
  408. GetBranch() string
  409. GetContent() string
  410. }
  411. // ActionIcon accepts a int that represents action operation type
  412. // and returns a icon class name.
  413. func ActionIcon(opType int) string {
  414. switch opType {
  415. case 1: // Create repository.
  416. return "plus-circle"
  417. case 5: // Commit repository.
  418. return "arrow-circle-o-right"
  419. default:
  420. return "invalid type"
  421. }
  422. }
  423. const (
  424. TPL_CREATE_REPO = `<a href="/user/%s">%s</a> created repository <a href="/%s/%s">%s</a>`
  425. 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`
  426. 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>`
  427. )
  428. type PushCommits struct {
  429. Len int
  430. Commits [][]string
  431. }
  432. // ActionDesc accepts int that represents action operation type
  433. // and returns the description.
  434. func ActionDesc(act Actioner, avatarLink string) string {
  435. actUserName := act.GetActUserName()
  436. repoName := act.GetRepoName()
  437. branch := act.GetBranch()
  438. content := act.GetContent()
  439. switch act.GetOpType() {
  440. case 1: // Create repository.
  441. return fmt.Sprintf(TPL_CREATE_REPO, actUserName, actUserName, actUserName, repoName, repoName)
  442. case 5: // Commit repository.
  443. var push *PushCommits
  444. if err := json.Unmarshal([]byte(content), &push); err != nil {
  445. return err.Error()
  446. }
  447. buf := bytes.NewBuffer([]byte("\n"))
  448. max := 3
  449. count := len(push.Commits)
  450. if count < max {
  451. max = count
  452. }
  453. for _, commit := range push.Commits[:max] {
  454. buf.WriteString(fmt.Sprintf(TPL_COMMIT_REPO_LI, avatarLink, actUserName, repoName, commit[0], commit[0][:7], commit[1]) + "\n")
  455. }
  456. if count > max {
  457. buf.WriteString(fmt.Sprintf(`<div><a href="/%s/%s/commits">%d other commits >></a></div>`, actUserName, repoName, count-max))
  458. }
  459. return fmt.Sprintf(TPL_COMMIT_REPO, actUserName, actUserName, actUserName, repoName, branch, branch, actUserName, repoName, actUserName, repoName,
  460. buf.String())
  461. default:
  462. return "invalid type"
  463. }
  464. }