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 14 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
11 years ago
11 years ago
11 years ago
11 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  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. if Service.EnableCacheAvatar {
  90. return "/avatar/" + EncodeMd5(email)
  91. }
  92. return "http://1.gravatar.com/avatar/" + EncodeMd5(email)
  93. }
  94. // Seconds-based time units
  95. const (
  96. Minute = 60
  97. Hour = 60 * Minute
  98. Day = 24 * Hour
  99. Week = 7 * Day
  100. Month = 30 * Day
  101. Year = 12 * Month
  102. )
  103. func computeTimeDiff(diff int64) (int64, string) {
  104. diffStr := ""
  105. switch {
  106. case diff <= 0:
  107. diff = 0
  108. diffStr = "now"
  109. case diff < 2:
  110. diff = 0
  111. diffStr = "1 second"
  112. case diff < 1*Minute:
  113. diffStr = fmt.Sprintf("%d seconds", diff)
  114. diff = 0
  115. case diff < 2*Minute:
  116. diff -= 1 * Minute
  117. diffStr = "1 minute"
  118. case diff < 1*Hour:
  119. diffStr = fmt.Sprintf("%d minutes", diff/Minute)
  120. diff -= diff / Minute * Minute
  121. case diff < 2*Hour:
  122. diff -= 1 * Hour
  123. diffStr = "1 hour"
  124. case diff < 1*Day:
  125. diffStr = fmt.Sprintf("%d hours", diff/Hour)
  126. diff -= diff / Hour * Hour
  127. case diff < 2*Day:
  128. diff -= 1 * Day
  129. diffStr = "1 day"
  130. case diff < 1*Week:
  131. diffStr = fmt.Sprintf("%d days", diff/Day)
  132. diff -= diff / Day * Day
  133. case diff < 2*Week:
  134. diff -= 1 * Week
  135. diffStr = "1 week"
  136. case diff < 1*Month:
  137. diffStr = fmt.Sprintf("%d weeks", diff/Week)
  138. diff -= diff / Week * Week
  139. case diff < 2*Month:
  140. diff -= 1 * Month
  141. diffStr = "1 month"
  142. case diff < 1*Year:
  143. diffStr = fmt.Sprintf("%d months", diff/Month)
  144. diff -= diff / Month * Month
  145. case diff < 2*Year:
  146. diff -= 1 * Year
  147. diffStr = "1 year"
  148. default:
  149. diffStr = fmt.Sprintf("%d years", diff/Year)
  150. diff = 0
  151. }
  152. return diff, diffStr
  153. }
  154. // TimeSincePro calculates the time interval and generate full user-friendly string.
  155. func TimeSincePro(then time.Time) string {
  156. now := time.Now()
  157. diff := now.Unix() - then.Unix()
  158. if then.After(now) {
  159. return "future"
  160. }
  161. var timeStr, diffStr string
  162. for {
  163. if diff == 0 {
  164. break
  165. }
  166. diff, diffStr = computeTimeDiff(diff)
  167. timeStr += ", " + diffStr
  168. }
  169. return strings.TrimPrefix(timeStr, ", ")
  170. }
  171. // TimeSince calculates the time interval and generate user-friendly string.
  172. func TimeSince(then time.Time) string {
  173. now := time.Now()
  174. lbl := "ago"
  175. diff := now.Unix() - then.Unix()
  176. if then.After(now) {
  177. lbl = "from now"
  178. diff = then.Unix() - now.Unix()
  179. }
  180. switch {
  181. case diff <= 0:
  182. return "now"
  183. case diff <= 2:
  184. return fmt.Sprintf("1 second %s", lbl)
  185. case diff < 1*Minute:
  186. return fmt.Sprintf("%d seconds %s", diff, lbl)
  187. case diff < 2*Minute:
  188. return fmt.Sprintf("1 minute %s", lbl)
  189. case diff < 1*Hour:
  190. return fmt.Sprintf("%d minutes %s", diff/Minute, lbl)
  191. case diff < 2*Hour:
  192. return fmt.Sprintf("1 hour %s", lbl)
  193. case diff < 1*Day:
  194. return fmt.Sprintf("%d hours %s", diff/Hour, lbl)
  195. case diff < 2*Day:
  196. return fmt.Sprintf("1 day %s", lbl)
  197. case diff < 1*Week:
  198. return fmt.Sprintf("%d days %s", diff/Day, lbl)
  199. case diff < 2*Week:
  200. return fmt.Sprintf("1 week %s", lbl)
  201. case diff < 1*Month:
  202. return fmt.Sprintf("%d weeks %s", diff/Week, lbl)
  203. case diff < 2*Month:
  204. return fmt.Sprintf("1 month %s", lbl)
  205. case diff < 1*Year:
  206. return fmt.Sprintf("%d months %s", diff/Month, lbl)
  207. case diff < 2*Year:
  208. return fmt.Sprintf("1 year %s", lbl)
  209. default:
  210. return fmt.Sprintf("%d years %s", diff/Year, lbl)
  211. }
  212. return then.String()
  213. }
  214. const (
  215. Byte = 1
  216. KByte = Byte * 1024
  217. MByte = KByte * 1024
  218. GByte = MByte * 1024
  219. TByte = GByte * 1024
  220. PByte = TByte * 1024
  221. EByte = PByte * 1024
  222. )
  223. var bytesSizeTable = map[string]uint64{
  224. "b": Byte,
  225. "kb": KByte,
  226. "mb": MByte,
  227. "gb": GByte,
  228. "tb": TByte,
  229. "pb": PByte,
  230. "eb": EByte,
  231. }
  232. func logn(n, b float64) float64 {
  233. return math.Log(n) / math.Log(b)
  234. }
  235. func humanateBytes(s uint64, base float64, sizes []string) string {
  236. if s < 10 {
  237. return fmt.Sprintf("%dB", s)
  238. }
  239. e := math.Floor(logn(float64(s), base))
  240. suffix := sizes[int(e)]
  241. val := float64(s) / math.Pow(base, math.Floor(e))
  242. f := "%.0f"
  243. if val < 10 {
  244. f = "%.1f"
  245. }
  246. return fmt.Sprintf(f+"%s", val, suffix)
  247. }
  248. // FileSize calculates the file size and generate user-friendly string.
  249. func FileSize(s int64) string {
  250. sizes := []string{"B", "KB", "MB", "GB", "TB", "PB", "EB"}
  251. return humanateBytes(uint64(s), 1024, sizes)
  252. }
  253. // Subtract deals with subtraction of all types of number.
  254. func Subtract(left interface{}, right interface{}) interface{} {
  255. var rleft, rright int64
  256. var fleft, fright float64
  257. var isInt bool = true
  258. switch left.(type) {
  259. case int:
  260. rleft = int64(left.(int))
  261. case int8:
  262. rleft = int64(left.(int8))
  263. case int16:
  264. rleft = int64(left.(int16))
  265. case int32:
  266. rleft = int64(left.(int32))
  267. case int64:
  268. rleft = left.(int64)
  269. case float32:
  270. fleft = float64(left.(float32))
  271. isInt = false
  272. case float64:
  273. fleft = left.(float64)
  274. isInt = false
  275. }
  276. switch right.(type) {
  277. case int:
  278. rright = int64(right.(int))
  279. case int8:
  280. rright = int64(right.(int8))
  281. case int16:
  282. rright = int64(right.(int16))
  283. case int32:
  284. rright = int64(right.(int32))
  285. case int64:
  286. rright = right.(int64)
  287. case float32:
  288. fright = float64(left.(float32))
  289. isInt = false
  290. case float64:
  291. fleft = left.(float64)
  292. isInt = false
  293. }
  294. if isInt {
  295. return rleft - rright
  296. } else {
  297. return fleft + float64(rleft) - (fright + float64(rright))
  298. }
  299. }
  300. // DateFormat pattern rules.
  301. var datePatterns = []string{
  302. // year
  303. "Y", "2006", // A full numeric representation of a year, 4 digits Examples: 1999 or 2003
  304. "y", "06", //A two digit representation of a year Examples: 99 or 03
  305. // month
  306. "m", "01", // Numeric representation of a month, with leading zeros 01 through 12
  307. "n", "1", // Numeric representation of a month, without leading zeros 1 through 12
  308. "M", "Jan", // A short textual representation of a month, three letters Jan through Dec
  309. "F", "January", // A full textual representation of a month, such as January or March January through December
  310. // day
  311. "d", "02", // Day of the month, 2 digits with leading zeros 01 to 31
  312. "j", "2", // Day of the month without leading zeros 1 to 31
  313. // week
  314. "D", "Mon", // A textual representation of a day, three letters Mon through Sun
  315. "l", "Monday", // A full textual representation of the day of the week Sunday through Saturday
  316. // time
  317. "g", "3", // 12-hour format of an hour without leading zeros 1 through 12
  318. "G", "15", // 24-hour format of an hour without leading zeros 0 through 23
  319. "h", "03", // 12-hour format of an hour with leading zeros 01 through 12
  320. "H", "15", // 24-hour format of an hour with leading zeros 00 through 23
  321. "a", "pm", // Lowercase Ante meridiem and Post meridiem am or pm
  322. "A", "PM", // Uppercase Ante meridiem and Post meridiem AM or PM
  323. "i", "04", // Minutes with leading zeros 00 to 59
  324. "s", "05", // Seconds, with leading zeros 00 through 59
  325. // time zone
  326. "T", "MST",
  327. "P", "-07:00",
  328. "O", "-0700",
  329. // RFC 2822
  330. "r", time.RFC1123Z,
  331. }
  332. // Parse Date use PHP time format.
  333. func DateParse(dateString, format string) (time.Time, error) {
  334. replacer := strings.NewReplacer(datePatterns...)
  335. format = replacer.Replace(format)
  336. return time.ParseInLocation(format, dateString, time.Local)
  337. }
  338. // Date takes a PHP like date func to Go's time format.
  339. func DateFormat(t time.Time, format string) string {
  340. replacer := strings.NewReplacer(datePatterns...)
  341. format = replacer.Replace(format)
  342. return t.Format(format)
  343. }
  344. // convert string to specify type
  345. type StrTo string
  346. func (f StrTo) Exist() bool {
  347. return string(f) != string(0x1E)
  348. }
  349. func (f StrTo) Int() (int, error) {
  350. v, err := strconv.ParseInt(f.String(), 10, 32)
  351. return int(v), err
  352. }
  353. func (f StrTo) Int64() (int64, error) {
  354. v, err := strconv.ParseInt(f.String(), 10, 64)
  355. return int64(v), err
  356. }
  357. func (f StrTo) String() string {
  358. if f.Exist() {
  359. return string(f)
  360. }
  361. return ""
  362. }
  363. // convert any type to string
  364. func ToStr(value interface{}, args ...int) (s string) {
  365. switch v := value.(type) {
  366. case bool:
  367. s = strconv.FormatBool(v)
  368. case float32:
  369. s = strconv.FormatFloat(float64(v), 'f', argInt(args).Get(0, -1), argInt(args).Get(1, 32))
  370. case float64:
  371. s = strconv.FormatFloat(v, 'f', argInt(args).Get(0, -1), argInt(args).Get(1, 64))
  372. case int:
  373. s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
  374. case int8:
  375. s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
  376. case int16:
  377. s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
  378. case int32:
  379. s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
  380. case int64:
  381. s = strconv.FormatInt(v, argInt(args).Get(0, 10))
  382. case uint:
  383. s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
  384. case uint8:
  385. s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
  386. case uint16:
  387. s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
  388. case uint32:
  389. s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
  390. case uint64:
  391. s = strconv.FormatUint(v, argInt(args).Get(0, 10))
  392. case string:
  393. s = v
  394. case []byte:
  395. s = string(v)
  396. default:
  397. s = fmt.Sprintf("%v", v)
  398. }
  399. return s
  400. }
  401. type argInt []int
  402. func (a argInt) Get(i int, args ...int) (r int) {
  403. if i >= 0 && i < len(a) {
  404. r = a[i]
  405. }
  406. if len(args) > 0 {
  407. r = args[0]
  408. }
  409. return
  410. }
  411. type Actioner interface {
  412. GetOpType() int
  413. GetActUserName() string
  414. GetRepoName() string
  415. GetBranch() string
  416. GetContent() string
  417. }
  418. // ActionIcon accepts a int that represents action operation type
  419. // and returns a icon class name.
  420. func ActionIcon(opType int) string {
  421. switch opType {
  422. case 1: // Create repository.
  423. return "plus-circle"
  424. case 5: // Commit repository.
  425. return "arrow-circle-o-right"
  426. case 6: // Create issue.
  427. return "exclamation-circle"
  428. default:
  429. return "invalid type"
  430. }
  431. }
  432. const (
  433. TPL_CREATE_REPO = `<a href="/user/%s">%s</a> created repository <a href="/%s">%s</a>`
  434. TPL_COMMIT_REPO = `<a href="/user/%s">%s</a> pushed to <a href="/%s/src/%s">%s</a> at <a href="/%s">%s</a>%s`
  435. TPL_COMMIT_REPO_LI = `<div><img src="%s?s=16" alt="user-avatar"/> <a href="/%s/commit/%s">%s</a> %s</div>`
  436. TPL_CREATE_Issue = `<a href="/user/%s">%s</a> opened issue <a href="/%s/issues/%s">%s#%s</a>
  437. <div><img src="%s?s=16" alt="user-avatar"/> %s</div>`
  438. )
  439. type PushCommits struct {
  440. Len int
  441. Commits [][]string
  442. }
  443. // ActionDesc accepts int that represents action operation type
  444. // and returns the description.
  445. func ActionDesc(act Actioner, avatarLink string) string {
  446. actUserName := act.GetActUserName()
  447. repoName := act.GetRepoName()
  448. repoLink := actUserName + "/" + repoName
  449. branch := act.GetBranch()
  450. content := act.GetContent()
  451. switch act.GetOpType() {
  452. case 1: // Create repository.
  453. return fmt.Sprintf(TPL_CREATE_REPO, actUserName, actUserName, repoLink, repoName)
  454. case 5: // Commit repository.
  455. var push *PushCommits
  456. if err := json.Unmarshal([]byte(content), &push); err != nil {
  457. return err.Error()
  458. }
  459. buf := bytes.NewBuffer([]byte("\n"))
  460. for _, commit := range push.Commits {
  461. buf.WriteString(fmt.Sprintf(TPL_COMMIT_REPO_LI, avatarLink, repoLink, commit[0], commit[0][:7], commit[1]) + "\n")
  462. }
  463. if push.Len > 3 {
  464. buf.WriteString(fmt.Sprintf(`<div><a href="/%s/%s/commits/%s">%d other commits >></a></div>`, actUserName, repoName, branch, push.Len))
  465. }
  466. return fmt.Sprintf(TPL_COMMIT_REPO, actUserName, actUserName, repoLink, branch, branch, repoLink, repoLink,
  467. buf.String())
  468. case 6: // Create issue.
  469. infos := strings.SplitN(content, "|", 2)
  470. return fmt.Sprintf(TPL_CREATE_Issue, actUserName, actUserName, repoLink, infos[0], repoLink, infos[0],
  471. avatarLink, infos[1])
  472. default:
  473. return "invalid type"
  474. }
  475. }
  476. func DiffTypeToStr(diffType int) string {
  477. diffTypes := map[int]string{
  478. 1: "add", 2: "modify", 3: "del",
  479. }
  480. return diffTypes[diffType]
  481. }
  482. func DiffLineTypeToStr(diffType int) string {
  483. switch diffType {
  484. case 2:
  485. return "add"
  486. case 3:
  487. return "del"
  488. case 4:
  489. return "tag"
  490. }
  491. return "same"
  492. }