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

пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 7 година
пре 10 година
пре 7 година
пре 10 година
пре 7 година
пре 7 година
пре 7 година
пре 10 година
пре 5 година
пре 7 година
пре 7 година
пре 7 година
пре 7 година
пре 10 година
пре 8 година
пре 7 година
пре 10 година
Better logging (#6038) (#6095) * Panic don't fatal on create new logger Fixes #5854 Signed-off-by: Andrew Thornton <art27@cantab.net> * partial broken * Update the logging infrastrcture Signed-off-by: Andrew Thornton <art27@cantab.net> * Reset the skip levels for Fatal and Error Signed-off-by: Andrew Thornton <art27@cantab.net> * broken ncsa * More log.Error fixes Signed-off-by: Andrew Thornton <art27@cantab.net> * Remove nal * set log-levels to lowercase * Make console_test test all levels * switch to lowercased levels * OK now working * Fix vetting issues * Fix lint * Fix tests * change default logging to match current gitea * Improve log testing Signed-off-by: Andrew Thornton <art27@cantab.net> * reset error skip levels to 0 * Update documentation and access logger configuration * Redirect the router log back to gitea if redirect macaron log but also allow setting the log level - i.e. TRACE * Fix broken level caching * Refactor the router log * Add Router logger * Add colorizing options * Adjust router colors * Only create logger if they will be used * update app.ini.sample * rename Attribute ColorAttribute * Change from white to green for function * Set fatal/error levels * Restore initial trace logger * Fix Trace arguments in modules/auth/auth.go * Properly handle XORMLogger * Improve admin/config page * fix fmt * Add auto-compression of old logs * Update error log levels * Remove the unnecessary skip argument from Error, Fatal and Critical * Add stacktrace support * Fix tests * Remove x/sync from vendors? * Add stderr option to console logger * Use filepath.ToSlash to protect against Windows in tests * Remove prefixed underscores from names in colors.go * Remove not implemented database logger This was removed from Gogs on 4 Mar 2016 but left in the configuration since then. * Ensure that log paths are relative to ROOT_PATH * use path.Join * rename jsonConfig to logConfig * Rename "config" to "jsonConfig" to make it clearer * Requested changes * Requested changes: XormLogger * Try to color the windows terminal If successful default to colorizing the console logs * fixup * Colorize initially too * update vendor * Colorize logs on default and remove if this is not a colorizing logger * Fix documentation * fix test * Use go-isatty to detect if on windows we are on msys or cygwin * Fix spelling mistake * Add missing vendors * More changes * Rationalise the ANSI writer protection * Adjust colors on advice from @0x5c * Make Flags a comma separated list * Move to use the windows constant for ENABLE_VIRTUAL_TERMINAL_PROCESSING * Ensure matching is done on the non-colored message - to simpify EXPRESSION
пре 5 година
Better logging (#6038) (#6095) * Panic don't fatal on create new logger Fixes #5854 Signed-off-by: Andrew Thornton <art27@cantab.net> * partial broken * Update the logging infrastrcture Signed-off-by: Andrew Thornton <art27@cantab.net> * Reset the skip levels for Fatal and Error Signed-off-by: Andrew Thornton <art27@cantab.net> * broken ncsa * More log.Error fixes Signed-off-by: Andrew Thornton <art27@cantab.net> * Remove nal * set log-levels to lowercase * Make console_test test all levels * switch to lowercased levels * OK now working * Fix vetting issues * Fix lint * Fix tests * change default logging to match current gitea * Improve log testing Signed-off-by: Andrew Thornton <art27@cantab.net> * reset error skip levels to 0 * Update documentation and access logger configuration * Redirect the router log back to gitea if redirect macaron log but also allow setting the log level - i.e. TRACE * Fix broken level caching * Refactor the router log * Add Router logger * Add colorizing options * Adjust router colors * Only create logger if they will be used * update app.ini.sample * rename Attribute ColorAttribute * Change from white to green for function * Set fatal/error levels * Restore initial trace logger * Fix Trace arguments in modules/auth/auth.go * Properly handle XORMLogger * Improve admin/config page * fix fmt * Add auto-compression of old logs * Update error log levels * Remove the unnecessary skip argument from Error, Fatal and Critical * Add stacktrace support * Fix tests * Remove x/sync from vendors? * Add stderr option to console logger * Use filepath.ToSlash to protect against Windows in tests * Remove prefixed underscores from names in colors.go * Remove not implemented database logger This was removed from Gogs on 4 Mar 2016 but left in the configuration since then. * Ensure that log paths are relative to ROOT_PATH * use path.Join * rename jsonConfig to logConfig * Rename "config" to "jsonConfig" to make it clearer * Requested changes * Requested changes: XormLogger * Try to color the windows terminal If successful default to colorizing the console logs * fixup * Colorize initially too * update vendor * Colorize logs on default and remove if this is not a colorizing logger * Fix documentation * fix test * Use go-isatty to detect if on windows we are on msys or cygwin * Fix spelling mistake * Add missing vendors * More changes * Rationalise the ANSI writer protection * Adjust colors on advice from @0x5c * Make Flags a comma separated list * Move to use the windows constant for ENABLE_VIRTUAL_TERMINAL_PROCESSING * Ensure matching is done on the non-colored message - to simpify EXPRESSION
пре 5 година
пре 7 година
пре 10 година
пре 7 година
пре 7 година
пре 10 година
пре 7 година
пре 7 година
пре 10 година
пре 7 година
пре 7 година
пре 8 година
пре 7 година
пре 7 година
пре 8 година
пре 7 година
пре 10 година
пре 7 година
пре 10 година
пре 10 година
пре 7 година
пре 10 година
пре 10 година
пре 7 година
пре 10 година
пре 7 година
пре 10 година
пре 7 година
пре 8 година
Better logging (#6038) (#6095) * Panic don't fatal on create new logger Fixes #5854 Signed-off-by: Andrew Thornton <art27@cantab.net> * partial broken * Update the logging infrastrcture Signed-off-by: Andrew Thornton <art27@cantab.net> * Reset the skip levels for Fatal and Error Signed-off-by: Andrew Thornton <art27@cantab.net> * broken ncsa * More log.Error fixes Signed-off-by: Andrew Thornton <art27@cantab.net> * Remove nal * set log-levels to lowercase * Make console_test test all levels * switch to lowercased levels * OK now working * Fix vetting issues * Fix lint * Fix tests * change default logging to match current gitea * Improve log testing Signed-off-by: Andrew Thornton <art27@cantab.net> * reset error skip levels to 0 * Update documentation and access logger configuration * Redirect the router log back to gitea if redirect macaron log but also allow setting the log level - i.e. TRACE * Fix broken level caching * Refactor the router log * Add Router logger * Add colorizing options * Adjust router colors * Only create logger if they will be used * update app.ini.sample * rename Attribute ColorAttribute * Change from white to green for function * Set fatal/error levels * Restore initial trace logger * Fix Trace arguments in modules/auth/auth.go * Properly handle XORMLogger * Improve admin/config page * fix fmt * Add auto-compression of old logs * Update error log levels * Remove the unnecessary skip argument from Error, Fatal and Critical * Add stacktrace support * Fix tests * Remove x/sync from vendors? * Add stderr option to console logger * Use filepath.ToSlash to protect against Windows in tests * Remove prefixed underscores from names in colors.go * Remove not implemented database logger This was removed from Gogs on 4 Mar 2016 but left in the configuration since then. * Ensure that log paths are relative to ROOT_PATH * use path.Join * rename jsonConfig to logConfig * Rename "config" to "jsonConfig" to make it clearer * Requested changes * Requested changes: XormLogger * Try to color the windows terminal If successful default to colorizing the console logs * fixup * Colorize initially too * update vendor * Colorize logs on default and remove if this is not a colorizing logger * Fix documentation * fix test * Use go-isatty to detect if on windows we are on msys or cygwin * Fix spelling mistake * Add missing vendors * More changes * Rationalise the ANSI writer protection * Adjust colors on advice from @0x5c * Make Flags a comma separated list * Move to use the windows constant for ENABLE_VIRTUAL_TERMINAL_PROCESSING * Ensure matching is done on the non-colored message - to simpify EXPRESSION
пре 5 година
пре 8 година
пре 8 година
пре 8 година
пре 7 година
пре 8 година
Better logging (#6038) (#6095) * Panic don't fatal on create new logger Fixes #5854 Signed-off-by: Andrew Thornton <art27@cantab.net> * partial broken * Update the logging infrastrcture Signed-off-by: Andrew Thornton <art27@cantab.net> * Reset the skip levels for Fatal and Error Signed-off-by: Andrew Thornton <art27@cantab.net> * broken ncsa * More log.Error fixes Signed-off-by: Andrew Thornton <art27@cantab.net> * Remove nal * set log-levels to lowercase * Make console_test test all levels * switch to lowercased levels * OK now working * Fix vetting issues * Fix lint * Fix tests * change default logging to match current gitea * Improve log testing Signed-off-by: Andrew Thornton <art27@cantab.net> * reset error skip levels to 0 * Update documentation and access logger configuration * Redirect the router log back to gitea if redirect macaron log but also allow setting the log level - i.e. TRACE * Fix broken level caching * Refactor the router log * Add Router logger * Add colorizing options * Adjust router colors * Only create logger if they will be used * update app.ini.sample * rename Attribute ColorAttribute * Change from white to green for function * Set fatal/error levels * Restore initial trace logger * Fix Trace arguments in modules/auth/auth.go * Properly handle XORMLogger * Improve admin/config page * fix fmt * Add auto-compression of old logs * Update error log levels * Remove the unnecessary skip argument from Error, Fatal and Critical * Add stacktrace support * Fix tests * Remove x/sync from vendors? * Add stderr option to console logger * Use filepath.ToSlash to protect against Windows in tests * Remove prefixed underscores from names in colors.go * Remove not implemented database logger This was removed from Gogs on 4 Mar 2016 but left in the configuration since then. * Ensure that log paths are relative to ROOT_PATH * use path.Join * rename jsonConfig to logConfig * Rename "config" to "jsonConfig" to make it clearer * Requested changes * Requested changes: XormLogger * Try to color the windows terminal If successful default to colorizing the console logs * fixup * Colorize initially too * update vendor * Colorize logs on default and remove if this is not a colorizing logger * Fix documentation * fix test * Use go-isatty to detect if on windows we are on msys or cygwin * Fix spelling mistake * Add missing vendors * More changes * Rationalise the ANSI writer protection * Adjust colors on advice from @0x5c * Make Flags a comma separated list * Move to use the windows constant for ENABLE_VIRTUAL_TERMINAL_PROCESSING * Ensure matching is done on the non-colored message - to simpify EXPRESSION
пре 5 година
пре 8 година
пре 8 година
пре 8 година
пре 8 година
пре 8 година
пре 7 година
пре 8 година
пре 8 година
пре 7 година
пре 8 година
пре 8 година
пре 8 година
пре 7 година
пре 8 година
пре 8 година
пре 7 година
пре 8 година
пре 7 година
пре 8 година
пре 8 година
пре 7 година
пре 8 година
Better logging (#6038) (#6095) * Panic don't fatal on create new logger Fixes #5854 Signed-off-by: Andrew Thornton <art27@cantab.net> * partial broken * Update the logging infrastrcture Signed-off-by: Andrew Thornton <art27@cantab.net> * Reset the skip levels for Fatal and Error Signed-off-by: Andrew Thornton <art27@cantab.net> * broken ncsa * More log.Error fixes Signed-off-by: Andrew Thornton <art27@cantab.net> * Remove nal * set log-levels to lowercase * Make console_test test all levels * switch to lowercased levels * OK now working * Fix vetting issues * Fix lint * Fix tests * change default logging to match current gitea * Improve log testing Signed-off-by: Andrew Thornton <art27@cantab.net> * reset error skip levels to 0 * Update documentation and access logger configuration * Redirect the router log back to gitea if redirect macaron log but also allow setting the log level - i.e. TRACE * Fix broken level caching * Refactor the router log * Add Router logger * Add colorizing options * Adjust router colors * Only create logger if they will be used * update app.ini.sample * rename Attribute ColorAttribute * Change from white to green for function * Set fatal/error levels * Restore initial trace logger * Fix Trace arguments in modules/auth/auth.go * Properly handle XORMLogger * Improve admin/config page * fix fmt * Add auto-compression of old logs * Update error log levels * Remove the unnecessary skip argument from Error, Fatal and Critical * Add stacktrace support * Fix tests * Remove x/sync from vendors? * Add stderr option to console logger * Use filepath.ToSlash to protect against Windows in tests * Remove prefixed underscores from names in colors.go * Remove not implemented database logger This was removed from Gogs on 4 Mar 2016 but left in the configuration since then. * Ensure that log paths are relative to ROOT_PATH * use path.Join * rename jsonConfig to logConfig * Rename "config" to "jsonConfig" to make it clearer * Requested changes * Requested changes: XormLogger * Try to color the windows terminal If successful default to colorizing the console logs * fixup * Colorize initially too * update vendor * Colorize logs on default and remove if this is not a colorizing logger * Fix documentation * fix test * Use go-isatty to detect if on windows we are on msys or cygwin * Fix spelling mistake * Add missing vendors * More changes * Rationalise the ANSI writer protection * Adjust colors on advice from @0x5c * Make Flags a comma separated list * Move to use the windows constant for ENABLE_VIRTUAL_TERMINAL_PROCESSING * Ensure matching is done on the non-colored message - to simpify EXPRESSION
пре 5 година
пре 8 година
пре 8 година
пре 8 година
пре 7 година
пре 8 година
пре 7 година
пре 8 година
пре 8 година
пре 10 година
пре 9 година
пре 9 година
пре 10 година
пре 6 година
пре 10 година
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Copyright 2019 The Gitea Authors. All rights reserved.
  3. // Use of this source code is governed by a MIT-style
  4. // license that can be found in the LICENSE file.
  5. package models
  6. import (
  7. "encoding/json"
  8. "fmt"
  9. "html"
  10. "path"
  11. "regexp"
  12. "strconv"
  13. "strings"
  14. "time"
  15. "unicode"
  16. "code.gitea.io/gitea/modules/base"
  17. "code.gitea.io/gitea/modules/git"
  18. "code.gitea.io/gitea/modules/log"
  19. "code.gitea.io/gitea/modules/setting"
  20. api "code.gitea.io/gitea/modules/structs"
  21. "code.gitea.io/gitea/modules/timeutil"
  22. "github.com/unknwon/com"
  23. "xorm.io/builder"
  24. )
  25. // ActionType represents the type of an action.
  26. type ActionType int
  27. // Possible action types.
  28. const (
  29. ActionCreateRepo ActionType = iota + 1 // 1
  30. ActionRenameRepo // 2
  31. ActionStarRepo // 3
  32. ActionWatchRepo // 4
  33. ActionCommitRepo // 5
  34. ActionCreateIssue // 6
  35. ActionCreatePullRequest // 7
  36. ActionTransferRepo // 8
  37. ActionPushTag // 9
  38. ActionCommentIssue // 10
  39. ActionMergePullRequest // 11
  40. ActionCloseIssue // 12
  41. ActionReopenIssue // 13
  42. ActionClosePullRequest // 14
  43. ActionReopenPullRequest // 15
  44. ActionDeleteTag // 16
  45. ActionDeleteBranch // 17
  46. ActionMirrorSyncPush // 18
  47. ActionMirrorSyncCreate // 19
  48. ActionMirrorSyncDelete // 20
  49. )
  50. var (
  51. // Same as GitHub. See
  52. // https://help.github.com/articles/closing-issues-via-commit-messages
  53. issueCloseKeywords = []string{"close", "closes", "closed", "fix", "fixes", "fixed", "resolve", "resolves", "resolved"}
  54. issueReopenKeywords = []string{"reopen", "reopens", "reopened"}
  55. issueCloseKeywordsPat, issueReopenKeywordsPat *regexp.Regexp
  56. issueReferenceKeywordsPat *regexp.Regexp
  57. )
  58. const issueRefRegexpStr = `(?:([0-9a-zA-Z-_\.]+)/([0-9a-zA-Z-_\.]+))?(#[0-9]+)+`
  59. const issueRefRegexpStrNoKeyword = `(?:\s|^|\(|\[)(?:([0-9a-zA-Z-_\.]+)/([0-9a-zA-Z-_\.]+))?(#[0-9]+)(?:\s|$|\)|\]|:|\.(\s|$))`
  60. func assembleKeywordsPattern(words []string) string {
  61. return fmt.Sprintf(`(?i)(?:%s)(?::?) %s`, strings.Join(words, "|"), issueRefRegexpStr)
  62. }
  63. func init() {
  64. issueCloseKeywordsPat = regexp.MustCompile(assembleKeywordsPattern(issueCloseKeywords))
  65. issueReopenKeywordsPat = regexp.MustCompile(assembleKeywordsPattern(issueReopenKeywords))
  66. issueReferenceKeywordsPat = regexp.MustCompile(issueRefRegexpStrNoKeyword)
  67. }
  68. // Action represents user operation type and other information to
  69. // repository. It implemented interface base.Actioner so that can be
  70. // used in template render.
  71. type Action struct {
  72. ID int64 `xorm:"pk autoincr"`
  73. UserID int64 `xorm:"INDEX"` // Receiver user id.
  74. OpType ActionType
  75. ActUserID int64 `xorm:"INDEX"` // Action user id.
  76. ActUser *User `xorm:"-"`
  77. RepoID int64 `xorm:"INDEX"`
  78. Repo *Repository `xorm:"-"`
  79. CommentID int64 `xorm:"INDEX"`
  80. Comment *Comment `xorm:"-"`
  81. IsDeleted bool `xorm:"INDEX NOT NULL DEFAULT false"`
  82. RefName string
  83. IsPrivate bool `xorm:"INDEX NOT NULL DEFAULT false"`
  84. Content string `xorm:"TEXT"`
  85. CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
  86. }
  87. // GetOpType gets the ActionType of this action.
  88. func (a *Action) GetOpType() ActionType {
  89. return a.OpType
  90. }
  91. func (a *Action) loadActUser() {
  92. if a.ActUser != nil {
  93. return
  94. }
  95. var err error
  96. a.ActUser, err = GetUserByID(a.ActUserID)
  97. if err == nil {
  98. return
  99. } else if IsErrUserNotExist(err) {
  100. a.ActUser = NewGhostUser()
  101. } else {
  102. log.Error("GetUserByID(%d): %v", a.ActUserID, err)
  103. }
  104. }
  105. func (a *Action) loadRepo() {
  106. if a.Repo != nil {
  107. return
  108. }
  109. var err error
  110. a.Repo, err = GetRepositoryByID(a.RepoID)
  111. if err != nil {
  112. log.Error("GetRepositoryByID(%d): %v", a.RepoID, err)
  113. }
  114. }
  115. // GetActFullName gets the action's user full name.
  116. func (a *Action) GetActFullName() string {
  117. a.loadActUser()
  118. return a.ActUser.FullName
  119. }
  120. // GetActUserName gets the action's user name.
  121. func (a *Action) GetActUserName() string {
  122. a.loadActUser()
  123. return a.ActUser.Name
  124. }
  125. // ShortActUserName gets the action's user name trimmed to max 20
  126. // chars.
  127. func (a *Action) ShortActUserName() string {
  128. return base.EllipsisString(a.GetActUserName(), 20)
  129. }
  130. // GetDisplayName gets the action's display name based on DEFAULT_SHOW_FULL_NAME
  131. func (a *Action) GetDisplayName() string {
  132. if setting.UI.DefaultShowFullName {
  133. return a.GetActFullName()
  134. }
  135. return a.ShortActUserName()
  136. }
  137. // GetDisplayNameTitle gets the action's display name used for the title (tooltip) based on DEFAULT_SHOW_FULL_NAME
  138. func (a *Action) GetDisplayNameTitle() string {
  139. if setting.UI.DefaultShowFullName {
  140. return a.ShortActUserName()
  141. }
  142. return a.GetActFullName()
  143. }
  144. // GetActAvatar the action's user's avatar link
  145. func (a *Action) GetActAvatar() string {
  146. a.loadActUser()
  147. return a.ActUser.RelAvatarLink()
  148. }
  149. // GetRepoUserName returns the name of the action repository owner.
  150. func (a *Action) GetRepoUserName() string {
  151. a.loadRepo()
  152. return a.Repo.MustOwner().Name
  153. }
  154. // ShortRepoUserName returns the name of the action repository owner
  155. // trimmed to max 20 chars.
  156. func (a *Action) ShortRepoUserName() string {
  157. return base.EllipsisString(a.GetRepoUserName(), 20)
  158. }
  159. // GetRepoName returns the name of the action repository.
  160. func (a *Action) GetRepoName() string {
  161. a.loadRepo()
  162. return a.Repo.Name
  163. }
  164. // ShortRepoName returns the name of the action repository
  165. // trimmed to max 33 chars.
  166. func (a *Action) ShortRepoName() string {
  167. return base.EllipsisString(a.GetRepoName(), 33)
  168. }
  169. // GetRepoPath returns the virtual path to the action repository.
  170. func (a *Action) GetRepoPath() string {
  171. return path.Join(a.GetRepoUserName(), a.GetRepoName())
  172. }
  173. // ShortRepoPath returns the virtual path to the action repository
  174. // trimmed to max 20 + 1 + 33 chars.
  175. func (a *Action) ShortRepoPath() string {
  176. return path.Join(a.ShortRepoUserName(), a.ShortRepoName())
  177. }
  178. // GetRepoLink returns relative link to action repository.
  179. func (a *Action) GetRepoLink() string {
  180. if len(setting.AppSubURL) > 0 {
  181. return path.Join(setting.AppSubURL, a.GetRepoPath())
  182. }
  183. return "/" + a.GetRepoPath()
  184. }
  185. // GetRepositoryFromMatch returns a *Repository from a username and repo strings
  186. func GetRepositoryFromMatch(ownerName string, repoName string) (*Repository, error) {
  187. var err error
  188. refRepo, err := GetRepositoryByOwnerAndName(ownerName, repoName)
  189. if err != nil {
  190. if IsErrRepoNotExist(err) {
  191. log.Warn("Repository referenced in commit but does not exist: %v", err)
  192. return nil, err
  193. }
  194. log.Error("GetRepositoryByOwnerAndName: %v", err)
  195. return nil, err
  196. }
  197. return refRepo, nil
  198. }
  199. // GetCommentLink returns link to action comment.
  200. func (a *Action) GetCommentLink() string {
  201. return a.getCommentLink(x)
  202. }
  203. func (a *Action) getCommentLink(e Engine) string {
  204. if a == nil {
  205. return "#"
  206. }
  207. if a.Comment == nil && a.CommentID != 0 {
  208. a.Comment, _ = GetCommentByID(a.CommentID)
  209. }
  210. if a.Comment != nil {
  211. return a.Comment.HTMLURL()
  212. }
  213. if len(a.GetIssueInfos()) == 0 {
  214. return "#"
  215. }
  216. //Return link to issue
  217. issueIDString := a.GetIssueInfos()[0]
  218. issueID, err := strconv.ParseInt(issueIDString, 10, 64)
  219. if err != nil {
  220. return "#"
  221. }
  222. issue, err := getIssueByID(e, issueID)
  223. if err != nil {
  224. return "#"
  225. }
  226. if err = issue.loadRepo(e); err != nil {
  227. return "#"
  228. }
  229. return issue.HTMLURL()
  230. }
  231. // GetBranch returns the action's repository branch.
  232. func (a *Action) GetBranch() string {
  233. return a.RefName
  234. }
  235. // GetContent returns the action's content.
  236. func (a *Action) GetContent() string {
  237. return a.Content
  238. }
  239. // GetCreate returns the action creation time.
  240. func (a *Action) GetCreate() time.Time {
  241. return a.CreatedUnix.AsTime()
  242. }
  243. // GetIssueInfos returns a list of issues associated with
  244. // the action.
  245. func (a *Action) GetIssueInfos() []string {
  246. return strings.SplitN(a.Content, "|", 2)
  247. }
  248. // GetIssueTitle returns the title of first issue associated
  249. // with the action.
  250. func (a *Action) GetIssueTitle() string {
  251. index := com.StrTo(a.GetIssueInfos()[0]).MustInt64()
  252. issue, err := GetIssueByIndex(a.RepoID, index)
  253. if err != nil {
  254. log.Error("GetIssueByIndex: %v", err)
  255. return "500 when get issue"
  256. }
  257. return issue.Title
  258. }
  259. // GetIssueContent returns the content of first issue associated with
  260. // this action.
  261. func (a *Action) GetIssueContent() string {
  262. index := com.StrTo(a.GetIssueInfos()[0]).MustInt64()
  263. issue, err := GetIssueByIndex(a.RepoID, index)
  264. if err != nil {
  265. log.Error("GetIssueByIndex: %v", err)
  266. return "500 when get issue"
  267. }
  268. return issue.Content
  269. }
  270. func newRepoAction(e Engine, u *User, repo *Repository) (err error) {
  271. if err = notifyWatchers(e, &Action{
  272. ActUserID: u.ID,
  273. ActUser: u,
  274. OpType: ActionCreateRepo,
  275. RepoID: repo.ID,
  276. Repo: repo,
  277. IsPrivate: repo.IsPrivate,
  278. }); err != nil {
  279. return fmt.Errorf("notify watchers '%d/%d': %v", u.ID, repo.ID, err)
  280. }
  281. log.Trace("action.newRepoAction: %s/%s", u.Name, repo.Name)
  282. return err
  283. }
  284. // NewRepoAction adds new action for creating repository.
  285. func NewRepoAction(u *User, repo *Repository) (err error) {
  286. return newRepoAction(x, u, repo)
  287. }
  288. func renameRepoAction(e Engine, actUser *User, oldRepoName string, repo *Repository) (err error) {
  289. if err = notifyWatchers(e, &Action{
  290. ActUserID: actUser.ID,
  291. ActUser: actUser,
  292. OpType: ActionRenameRepo,
  293. RepoID: repo.ID,
  294. Repo: repo,
  295. IsPrivate: repo.IsPrivate,
  296. Content: oldRepoName,
  297. }); err != nil {
  298. return fmt.Errorf("notify watchers: %v", err)
  299. }
  300. log.Trace("action.renameRepoAction: %s/%s", actUser.Name, repo.Name)
  301. return nil
  302. }
  303. // RenameRepoAction adds new action for renaming a repository.
  304. func RenameRepoAction(actUser *User, oldRepoName string, repo *Repository) error {
  305. return renameRepoAction(x, actUser, oldRepoName, repo)
  306. }
  307. func issueIndexTrimRight(c rune) bool {
  308. return !unicode.IsDigit(c)
  309. }
  310. // PushCommit represents a commit in a push operation.
  311. type PushCommit struct {
  312. Sha1 string
  313. Message string
  314. AuthorEmail string
  315. AuthorName string
  316. CommitterEmail string
  317. CommitterName string
  318. Timestamp time.Time
  319. }
  320. // PushCommits represents list of commits in a push operation.
  321. type PushCommits struct {
  322. Len int
  323. Commits []*PushCommit
  324. CompareURL string
  325. avatars map[string]string
  326. emailUsers map[string]*User
  327. }
  328. // NewPushCommits creates a new PushCommits object.
  329. func NewPushCommits() *PushCommits {
  330. return &PushCommits{
  331. avatars: make(map[string]string),
  332. emailUsers: make(map[string]*User),
  333. }
  334. }
  335. // ToAPIPayloadCommits converts a PushCommits object to
  336. // api.PayloadCommit format.
  337. func (pc *PushCommits) ToAPIPayloadCommits(repoPath, repoLink string) ([]*api.PayloadCommit, error) {
  338. commits := make([]*api.PayloadCommit, len(pc.Commits))
  339. if pc.emailUsers == nil {
  340. pc.emailUsers = make(map[string]*User)
  341. }
  342. var err error
  343. for i, commit := range pc.Commits {
  344. authorUsername := ""
  345. author, ok := pc.emailUsers[commit.AuthorEmail]
  346. if !ok {
  347. author, err = GetUserByEmail(commit.AuthorEmail)
  348. if err == nil {
  349. authorUsername = author.Name
  350. pc.emailUsers[commit.AuthorEmail] = author
  351. }
  352. } else {
  353. authorUsername = author.Name
  354. }
  355. committerUsername := ""
  356. committer, ok := pc.emailUsers[commit.CommitterEmail]
  357. if !ok {
  358. committer, err = GetUserByEmail(commit.CommitterEmail)
  359. if err == nil {
  360. // TODO: check errors other than email not found.
  361. committerUsername = committer.Name
  362. pc.emailUsers[commit.CommitterEmail] = committer
  363. }
  364. } else {
  365. committerUsername = committer.Name
  366. }
  367. fileStatus, err := git.GetCommitFileStatus(repoPath, commit.Sha1)
  368. if err != nil {
  369. return nil, fmt.Errorf("FileStatus [commit_sha1: %s]: %v", commit.Sha1, err)
  370. }
  371. commits[i] = &api.PayloadCommit{
  372. ID: commit.Sha1,
  373. Message: commit.Message,
  374. URL: fmt.Sprintf("%s/commit/%s", repoLink, commit.Sha1),
  375. Author: &api.PayloadUser{
  376. Name: commit.AuthorName,
  377. Email: commit.AuthorEmail,
  378. UserName: authorUsername,
  379. },
  380. Committer: &api.PayloadUser{
  381. Name: commit.CommitterName,
  382. Email: commit.CommitterEmail,
  383. UserName: committerUsername,
  384. },
  385. Added: fileStatus.Added,
  386. Removed: fileStatus.Removed,
  387. Modified: fileStatus.Modified,
  388. Timestamp: commit.Timestamp,
  389. }
  390. }
  391. return commits, nil
  392. }
  393. // AvatarLink tries to match user in database with e-mail
  394. // in order to show custom avatar, and falls back to general avatar link.
  395. func (pc *PushCommits) AvatarLink(email string) string {
  396. if pc.avatars == nil {
  397. pc.avatars = make(map[string]string)
  398. }
  399. avatar, ok := pc.avatars[email]
  400. if ok {
  401. return avatar
  402. }
  403. u, ok := pc.emailUsers[email]
  404. if !ok {
  405. var err error
  406. u, err = GetUserByEmail(email)
  407. if err != nil {
  408. pc.avatars[email] = base.AvatarLink(email)
  409. if !IsErrUserNotExist(err) {
  410. log.Error("GetUserByEmail: %v", err)
  411. return ""
  412. }
  413. } else {
  414. pc.emailUsers[email] = u
  415. }
  416. }
  417. if u != nil {
  418. pc.avatars[email] = u.RelAvatarLink()
  419. }
  420. return pc.avatars[email]
  421. }
  422. // getIssueFromRef returns the issue referenced by a ref. Returns a nil *Issue
  423. // if the provided ref is misformatted or references a non-existent issue.
  424. func getIssueFromRef(repo *Repository, ref string) (*Issue, error) {
  425. ref = ref[strings.IndexByte(ref, ' ')+1:]
  426. ref = strings.TrimRightFunc(ref, issueIndexTrimRight)
  427. var refRepo *Repository
  428. poundIndex := strings.IndexByte(ref, '#')
  429. if poundIndex < 0 {
  430. return nil, nil
  431. } else if poundIndex == 0 {
  432. refRepo = repo
  433. } else {
  434. slashIndex := strings.IndexByte(ref, '/')
  435. if slashIndex < 0 || slashIndex >= poundIndex {
  436. return nil, nil
  437. }
  438. ownerName := ref[:slashIndex]
  439. repoName := ref[slashIndex+1 : poundIndex]
  440. var err error
  441. refRepo, err = GetRepositoryByOwnerAndName(ownerName, repoName)
  442. if err != nil {
  443. if IsErrRepoNotExist(err) {
  444. return nil, nil
  445. }
  446. return nil, err
  447. }
  448. }
  449. issueIndex, err := strconv.ParseInt(ref[poundIndex+1:], 10, 64)
  450. if err != nil {
  451. return nil, nil
  452. }
  453. issue, err := GetIssueByIndex(refRepo.ID, issueIndex)
  454. if err != nil {
  455. if IsErrIssueNotExist(err) {
  456. return nil, nil
  457. }
  458. return nil, err
  459. }
  460. return issue, nil
  461. }
  462. func changeIssueStatus(repo *Repository, doer *User, ref string, refMarked map[int64]bool, status bool) error {
  463. issue, err := getIssueFromRef(repo, ref)
  464. if err != nil {
  465. return err
  466. }
  467. if issue == nil || refMarked[issue.ID] {
  468. return nil
  469. }
  470. refMarked[issue.ID] = true
  471. if issue.RepoID != repo.ID || issue.IsClosed == status {
  472. return nil
  473. }
  474. stopTimerIfAvailable := func(doer *User, issue *Issue) error {
  475. if StopwatchExists(doer.ID, issue.ID) {
  476. if err := CreateOrStopIssueStopwatch(doer, issue); err != nil {
  477. return err
  478. }
  479. }
  480. return nil
  481. }
  482. issue.Repo = repo
  483. if err = issue.ChangeStatus(doer, status); err != nil {
  484. // Don't return an error when dependencies are open as this would let the push fail
  485. if IsErrDependenciesLeft(err) {
  486. return stopTimerIfAvailable(doer, issue)
  487. }
  488. return err
  489. }
  490. return stopTimerIfAvailable(doer, issue)
  491. }
  492. // UpdateIssuesCommit checks if issues are manipulated by commit message.
  493. func UpdateIssuesCommit(doer *User, repo *Repository, commits []*PushCommit, branchName string) error {
  494. // Commits are appended in the reverse order.
  495. for i := len(commits) - 1; i >= 0; i-- {
  496. c := commits[i]
  497. refMarked := make(map[int64]bool)
  498. var refRepo *Repository
  499. var err error
  500. for _, m := range issueReferenceKeywordsPat.FindAllStringSubmatch(c.Message, -1) {
  501. if len(m[3]) == 0 {
  502. continue
  503. }
  504. ref := m[3]
  505. // issue is from another repo
  506. if len(m[1]) > 0 && len(m[2]) > 0 {
  507. refRepo, err = GetRepositoryFromMatch(m[1], m[2])
  508. if err != nil {
  509. continue
  510. }
  511. } else {
  512. refRepo = repo
  513. }
  514. issue, err := getIssueFromRef(refRepo, ref)
  515. if err != nil {
  516. return err
  517. }
  518. if issue == nil || refMarked[issue.ID] {
  519. continue
  520. }
  521. refMarked[issue.ID] = true
  522. message := fmt.Sprintf(`<a href="%s/commit/%s">%s</a>`, repo.Link(), c.Sha1, html.EscapeString(c.Message))
  523. if err = CreateRefComment(doer, refRepo, issue, message, c.Sha1); err != nil {
  524. return err
  525. }
  526. }
  527. // Change issue status only if the commit has been pushed to the default branch.
  528. // and if the repo is configured to allow only that
  529. if repo.DefaultBranch != branchName && !repo.CloseIssuesViaCommitInAnyBranch {
  530. continue
  531. }
  532. refMarked = make(map[int64]bool)
  533. for _, m := range issueCloseKeywordsPat.FindAllStringSubmatch(c.Message, -1) {
  534. if len(m[3]) == 0 {
  535. continue
  536. }
  537. ref := m[3]
  538. // issue is from another repo
  539. if len(m[1]) > 0 && len(m[2]) > 0 {
  540. refRepo, err = GetRepositoryFromMatch(m[1], m[2])
  541. if err != nil {
  542. continue
  543. }
  544. } else {
  545. refRepo = repo
  546. }
  547. perm, err := GetUserRepoPermission(refRepo, doer)
  548. if err != nil {
  549. return err
  550. }
  551. // only close issues in another repo if user has push access
  552. if perm.CanWrite(UnitTypeCode) {
  553. if err := changeIssueStatus(refRepo, doer, ref, refMarked, true); err != nil {
  554. return err
  555. }
  556. }
  557. }
  558. // It is conflict to have close and reopen at same time, so refsMarked doesn't need to reinit here.
  559. for _, m := range issueReopenKeywordsPat.FindAllStringSubmatch(c.Message, -1) {
  560. if len(m[3]) == 0 {
  561. continue
  562. }
  563. ref := m[3]
  564. // issue is from another repo
  565. if len(m[1]) > 0 && len(m[2]) > 0 {
  566. refRepo, err = GetRepositoryFromMatch(m[1], m[2])
  567. if err != nil {
  568. continue
  569. }
  570. } else {
  571. refRepo = repo
  572. }
  573. perm, err := GetUserRepoPermission(refRepo, doer)
  574. if err != nil {
  575. return err
  576. }
  577. // only reopen issues in another repo if user has push access
  578. if perm.CanWrite(UnitTypeCode) {
  579. if err := changeIssueStatus(refRepo, doer, ref, refMarked, false); err != nil {
  580. return err
  581. }
  582. }
  583. }
  584. }
  585. return nil
  586. }
  587. func transferRepoAction(e Engine, doer, oldOwner *User, repo *Repository) (err error) {
  588. if err = notifyWatchers(e, &Action{
  589. ActUserID: doer.ID,
  590. ActUser: doer,
  591. OpType: ActionTransferRepo,
  592. RepoID: repo.ID,
  593. Repo: repo,
  594. IsPrivate: repo.IsPrivate,
  595. Content: path.Join(oldOwner.Name, repo.Name),
  596. }); err != nil {
  597. return fmt.Errorf("notifyWatchers: %v", err)
  598. }
  599. // Remove watch for organization.
  600. if oldOwner.IsOrganization() {
  601. if err = watchRepo(e, oldOwner.ID, repo.ID, false); err != nil {
  602. return fmt.Errorf("watchRepo [false]: %v", err)
  603. }
  604. }
  605. return nil
  606. }
  607. // TransferRepoAction adds new action for transferring repository,
  608. // the Owner field of repository is assumed to be new owner.
  609. func TransferRepoAction(doer, oldOwner *User, repo *Repository) error {
  610. return transferRepoAction(x, doer, oldOwner, repo)
  611. }
  612. func mergePullRequestAction(e Engine, doer *User, repo *Repository, issue *Issue) error {
  613. return notifyWatchers(e, &Action{
  614. ActUserID: doer.ID,
  615. ActUser: doer,
  616. OpType: ActionMergePullRequest,
  617. Content: fmt.Sprintf("%d|%s", issue.Index, issue.Title),
  618. RepoID: repo.ID,
  619. Repo: repo,
  620. IsPrivate: repo.IsPrivate,
  621. })
  622. }
  623. // MergePullRequestAction adds new action for merging pull request.
  624. func MergePullRequestAction(actUser *User, repo *Repository, pull *Issue) error {
  625. return mergePullRequestAction(x, actUser, repo, pull)
  626. }
  627. func mirrorSyncAction(e Engine, opType ActionType, repo *Repository, refName string, data []byte) error {
  628. if err := notifyWatchers(e, &Action{
  629. ActUserID: repo.OwnerID,
  630. ActUser: repo.MustOwner(),
  631. OpType: opType,
  632. RepoID: repo.ID,
  633. Repo: repo,
  634. IsPrivate: repo.IsPrivate,
  635. RefName: refName,
  636. Content: string(data),
  637. }); err != nil {
  638. return fmt.Errorf("notifyWatchers: %v", err)
  639. }
  640. defer func() {
  641. go HookQueue.Add(repo.ID)
  642. }()
  643. return nil
  644. }
  645. // MirrorSyncPushActionOptions mirror synchronization action options.
  646. type MirrorSyncPushActionOptions struct {
  647. RefName string
  648. OldCommitID string
  649. NewCommitID string
  650. Commits *PushCommits
  651. }
  652. // MirrorSyncPushAction adds new action for mirror synchronization of pushed commits.
  653. func MirrorSyncPushAction(repo *Repository, opts MirrorSyncPushActionOptions) error {
  654. if len(opts.Commits.Commits) > setting.UI.FeedMaxCommitNum {
  655. opts.Commits.Commits = opts.Commits.Commits[:setting.UI.FeedMaxCommitNum]
  656. }
  657. apiCommits, err := opts.Commits.ToAPIPayloadCommits(repo.RepoPath(), repo.HTMLURL())
  658. if err != nil {
  659. return err
  660. }
  661. opts.Commits.CompareURL = repo.ComposeCompareURL(opts.OldCommitID, opts.NewCommitID)
  662. apiPusher := repo.MustOwner().APIFormat()
  663. if err := PrepareWebhooks(repo, HookEventPush, &api.PushPayload{
  664. Ref: opts.RefName,
  665. Before: opts.OldCommitID,
  666. After: opts.NewCommitID,
  667. CompareURL: setting.AppURL + opts.Commits.CompareURL,
  668. Commits: apiCommits,
  669. Repo: repo.APIFormat(AccessModeOwner),
  670. Pusher: apiPusher,
  671. Sender: apiPusher,
  672. }); err != nil {
  673. return fmt.Errorf("PrepareWebhooks: %v", err)
  674. }
  675. data, err := json.Marshal(opts.Commits)
  676. if err != nil {
  677. return err
  678. }
  679. return mirrorSyncAction(x, ActionMirrorSyncPush, repo, opts.RefName, data)
  680. }
  681. // MirrorSyncCreateAction adds new action for mirror synchronization of new reference.
  682. func MirrorSyncCreateAction(repo *Repository, refName string) error {
  683. return mirrorSyncAction(x, ActionMirrorSyncCreate, repo, refName, nil)
  684. }
  685. // MirrorSyncDeleteAction adds new action for mirror synchronization of delete reference.
  686. func MirrorSyncDeleteAction(repo *Repository, refName string) error {
  687. return mirrorSyncAction(x, ActionMirrorSyncDelete, repo, refName, nil)
  688. }
  689. // GetFeedsOptions options for retrieving feeds
  690. type GetFeedsOptions struct {
  691. RequestedUser *User
  692. RequestingUserID int64
  693. IncludePrivate bool // include private actions
  694. OnlyPerformedBy bool // only actions performed by requested user
  695. IncludeDeleted bool // include deleted actions
  696. }
  697. // GetFeeds returns actions according to the provided options
  698. func GetFeeds(opts GetFeedsOptions) ([]*Action, error) {
  699. cond := builder.NewCond()
  700. var repoIDs []int64
  701. if opts.RequestedUser.IsOrganization() {
  702. env, err := opts.RequestedUser.AccessibleReposEnv(opts.RequestingUserID)
  703. if err != nil {
  704. return nil, fmt.Errorf("AccessibleReposEnv: %v", err)
  705. }
  706. if repoIDs, err = env.RepoIDs(1, opts.RequestedUser.NumRepos); err != nil {
  707. return nil, fmt.Errorf("GetUserRepositories: %v", err)
  708. }
  709. cond = cond.And(builder.In("repo_id", repoIDs))
  710. }
  711. cond = cond.And(builder.Eq{"user_id": opts.RequestedUser.ID})
  712. if opts.OnlyPerformedBy {
  713. cond = cond.And(builder.Eq{"act_user_id": opts.RequestedUser.ID})
  714. }
  715. if !opts.IncludePrivate {
  716. cond = cond.And(builder.Eq{"is_private": false})
  717. }
  718. if !opts.IncludeDeleted {
  719. cond = cond.And(builder.Eq{"is_deleted": false})
  720. }
  721. actions := make([]*Action, 0, 20)
  722. if err := x.Limit(20).Desc("id").Where(cond).Find(&actions); err != nil {
  723. return nil, fmt.Errorf("Find: %v", err)
  724. }
  725. if err := ActionList(actions).LoadAttributes(); err != nil {
  726. return nil, fmt.Errorf("LoadAttributes: %v", err)
  727. }
  728. return actions, nil
  729. }