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.

action.go 19KB

10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
7 vuotta sitten
7 vuotta sitten
10 vuotta sitten
10 vuotta sitten
7 vuotta sitten
10 vuotta sitten
8 vuotta sitten
7 vuotta sitten
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 vuotta sitten
7 vuotta sitten
7 vuotta sitten
7 vuotta sitten
10 vuotta sitten
7 vuotta sitten
7 vuotta sitten
10 vuotta sitten
7 vuotta sitten
7 vuotta sitten
8 vuotta sitten
7 vuotta sitten
8 vuotta sitten
7 vuotta sitten
7 vuotta sitten
10 vuotta sitten
7 vuotta sitten
10 vuotta sitten
10 vuotta sitten
7 vuotta sitten
7 vuotta sitten
7 vuotta sitten
8 vuotta sitten
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 vuotta sitten
8 vuotta sitten
7 vuotta sitten
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 vuotta sitten
Restricted users (#6274) * Restricted users (#4334): initial implementation * Add User.IsRestricted & UI to edit it * Pass user object instead of user id to places where IsRestricted flag matters * Restricted users: maintain access rows for all referenced repos (incl public) * Take logged in user & IsRestricted flag into account in org/repo listings, searches and accesses * Add basic repo access tests for restricted users Signed-off-by: Manush Dodunekov <manush@stendahls.se> * Mention restricted users in the faq Signed-off-by: Manush Dodunekov <manush@stendahls.se> * Revert unnecessary change `.isUserPartOfOrg` -> `.IsUserPartOfOrg` Signed-off-by: Manush Dodunekov <manush@stendahls.se> * Remove unnecessary `org.IsOrganization()` call Signed-off-by: Manush Dodunekov <manush@stendahls.se> * Revert to an `int64` keyed `accessMap` * Add type `userAccess` * Add convenience func updateUserAccess() * Turn accessMap into a `map[int64]userAccess` Signed-off-by: Manush Dodunekov <manush@stendahls.se> * or even better: `map[int64]*userAccess` * updateUserAccess(): use tighter syntax as suggested by lafriks * even tighter * Avoid extra loop * Don't disclose limited orgs to unauthenticated users * Don't assume block only applies to orgs * Use an array of `VisibleType` for filtering * fix yet another thinko * Ok - no need for u * Revert "Ok - no need for u" This reverts commit 5c3e886aabd5acd997a3b35687d322439732c200. Co-authored-by: Antoine GIRARD <sapk@users.noreply.github.com> Co-authored-by: Lauris BH <lauris@nix.lv>
4 vuotta sitten
Restricted users (#6274) * Restricted users (#4334): initial implementation * Add User.IsRestricted & UI to edit it * Pass user object instead of user id to places where IsRestricted flag matters * Restricted users: maintain access rows for all referenced repos (incl public) * Take logged in user & IsRestricted flag into account in org/repo listings, searches and accesses * Add basic repo access tests for restricted users Signed-off-by: Manush Dodunekov <manush@stendahls.se> * Mention restricted users in the faq Signed-off-by: Manush Dodunekov <manush@stendahls.se> * Revert unnecessary change `.isUserPartOfOrg` -> `.IsUserPartOfOrg` Signed-off-by: Manush Dodunekov <manush@stendahls.se> * Remove unnecessary `org.IsOrganization()` call Signed-off-by: Manush Dodunekov <manush@stendahls.se> * Revert to an `int64` keyed `accessMap` * Add type `userAccess` * Add convenience func updateUserAccess() * Turn accessMap into a `map[int64]userAccess` Signed-off-by: Manush Dodunekov <manush@stendahls.se> * or even better: `map[int64]*userAccess` * updateUserAccess(): use tighter syntax as suggested by lafriks * even tighter * Avoid extra loop * Don't disclose limited orgs to unauthenticated users * Don't assume block only applies to orgs * Use an array of `VisibleType` for filtering * fix yet another thinko * Ok - no need for u * Revert "Ok - no need for u" This reverts commit 5c3e886aabd5acd997a3b35687d322439732c200. Co-authored-by: Antoine GIRARD <sapk@users.noreply.github.com> Co-authored-by: Lauris BH <lauris@nix.lv>
4 vuotta sitten
10 vuotta sitten
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Copyright 2019 The Gitea Authors. All rights reserved.
  3. // SPDX-License-Identifier: MIT
  4. package activities
  5. import (
  6. "context"
  7. "fmt"
  8. "net/url"
  9. "path"
  10. "strconv"
  11. "strings"
  12. "time"
  13. "code.gitea.io/gitea/models/db"
  14. issues_model "code.gitea.io/gitea/models/issues"
  15. "code.gitea.io/gitea/models/organization"
  16. access_model "code.gitea.io/gitea/models/perm/access"
  17. repo_model "code.gitea.io/gitea/models/repo"
  18. "code.gitea.io/gitea/models/unit"
  19. user_model "code.gitea.io/gitea/models/user"
  20. "code.gitea.io/gitea/modules/base"
  21. "code.gitea.io/gitea/modules/git"
  22. "code.gitea.io/gitea/modules/log"
  23. "code.gitea.io/gitea/modules/setting"
  24. "code.gitea.io/gitea/modules/structs"
  25. "code.gitea.io/gitea/modules/timeutil"
  26. "code.gitea.io/gitea/modules/util"
  27. "xorm.io/builder"
  28. "xorm.io/xorm/schemas"
  29. )
  30. // ActionType represents the type of an action.
  31. type ActionType int
  32. // Possible action types.
  33. const (
  34. ActionCreateRepo ActionType = iota + 1 // 1
  35. ActionRenameRepo // 2
  36. ActionStarRepo // 3
  37. ActionWatchRepo // 4
  38. ActionCommitRepo // 5
  39. ActionCreateIssue // 6
  40. ActionCreatePullRequest // 7
  41. ActionTransferRepo // 8
  42. ActionPushTag // 9
  43. ActionCommentIssue // 10
  44. ActionMergePullRequest // 11
  45. ActionCloseIssue // 12
  46. ActionReopenIssue // 13
  47. ActionClosePullRequest // 14
  48. ActionReopenPullRequest // 15
  49. ActionDeleteTag // 16
  50. ActionDeleteBranch // 17
  51. ActionMirrorSyncPush // 18
  52. ActionMirrorSyncCreate // 19
  53. ActionMirrorSyncDelete // 20
  54. ActionApprovePullRequest // 21
  55. ActionRejectPullRequest // 22
  56. ActionCommentPull // 23
  57. ActionPublishRelease // 24
  58. ActionPullReviewDismissed // 25
  59. ActionPullRequestReadyForReview // 26
  60. ActionAutoMergePullRequest // 27
  61. )
  62. // Action represents user operation type and other information to
  63. // repository. It implemented interface base.Actioner so that can be
  64. // used in template render.
  65. type Action struct {
  66. ID int64 `xorm:"pk autoincr"`
  67. UserID int64 // Receiver user id.
  68. OpType ActionType
  69. ActUserID int64 // Action user id.
  70. ActUser *user_model.User `xorm:"-"`
  71. RepoID int64
  72. Repo *repo_model.Repository `xorm:"-"`
  73. CommentID int64 `xorm:"INDEX"`
  74. Comment *issues_model.Comment `xorm:"-"`
  75. IsDeleted bool `xorm:"NOT NULL DEFAULT false"`
  76. RefName string
  77. IsPrivate bool `xorm:"NOT NULL DEFAULT false"`
  78. Content string `xorm:"TEXT"`
  79. CreatedUnix timeutil.TimeStamp `xorm:"created"`
  80. }
  81. func init() {
  82. db.RegisterModel(new(Action))
  83. }
  84. // TableIndices implements xorm's TableIndices interface
  85. func (a *Action) TableIndices() []*schemas.Index {
  86. repoIndex := schemas.NewIndex("r_u_d", schemas.IndexType)
  87. repoIndex.AddColumn("repo_id", "user_id", "is_deleted")
  88. actUserIndex := schemas.NewIndex("au_r_c_u_d", schemas.IndexType)
  89. actUserIndex.AddColumn("act_user_id", "repo_id", "created_unix", "user_id", "is_deleted")
  90. indices := []*schemas.Index{actUserIndex, repoIndex}
  91. if setting.Database.UsePostgreSQL {
  92. cudIndex := schemas.NewIndex("c_u_d", schemas.IndexType)
  93. cudIndex.AddColumn("created_unix", "user_id", "is_deleted")
  94. indices = append(indices, cudIndex)
  95. }
  96. return indices
  97. }
  98. // GetOpType gets the ActionType of this action.
  99. func (a *Action) GetOpType() ActionType {
  100. return a.OpType
  101. }
  102. // LoadActUser loads a.ActUser
  103. func (a *Action) LoadActUser() {
  104. if a.ActUser != nil {
  105. return
  106. }
  107. var err error
  108. a.ActUser, err = user_model.GetUserByID(a.ActUserID)
  109. if err == nil {
  110. return
  111. } else if user_model.IsErrUserNotExist(err) {
  112. a.ActUser = user_model.NewGhostUser()
  113. } else {
  114. log.Error("GetUserByID(%d): %v", a.ActUserID, err)
  115. }
  116. }
  117. func (a *Action) loadRepo() {
  118. if a.Repo != nil {
  119. return
  120. }
  121. var err error
  122. a.Repo, err = repo_model.GetRepositoryByID(a.RepoID)
  123. if err != nil {
  124. log.Error("repo_model.GetRepositoryByID(%d): %v", a.RepoID, err)
  125. }
  126. }
  127. // GetActFullName gets the action's user full name.
  128. func (a *Action) GetActFullName() string {
  129. a.LoadActUser()
  130. return a.ActUser.FullName
  131. }
  132. // GetActUserName gets the action's user name.
  133. func (a *Action) GetActUserName() string {
  134. a.LoadActUser()
  135. return a.ActUser.Name
  136. }
  137. // ShortActUserName gets the action's user name trimmed to max 20
  138. // chars.
  139. func (a *Action) ShortActUserName() string {
  140. return base.EllipsisString(a.GetActUserName(), 20)
  141. }
  142. // GetDisplayName gets the action's display name based on DEFAULT_SHOW_FULL_NAME, or falls back to the username if it is blank.
  143. func (a *Action) GetDisplayName() string {
  144. if setting.UI.DefaultShowFullName {
  145. trimmedFullName := strings.TrimSpace(a.GetActFullName())
  146. if len(trimmedFullName) > 0 {
  147. return trimmedFullName
  148. }
  149. }
  150. return a.ShortActUserName()
  151. }
  152. // GetDisplayNameTitle gets the action's display name used for the title (tooltip) based on DEFAULT_SHOW_FULL_NAME
  153. func (a *Action) GetDisplayNameTitle() string {
  154. if setting.UI.DefaultShowFullName {
  155. return a.ShortActUserName()
  156. }
  157. return a.GetActFullName()
  158. }
  159. // GetRepoUserName returns the name of the action repository owner.
  160. func (a *Action) GetRepoUserName() string {
  161. a.loadRepo()
  162. return a.Repo.OwnerName
  163. }
  164. // ShortRepoUserName returns the name of the action repository owner
  165. // trimmed to max 20 chars.
  166. func (a *Action) ShortRepoUserName() string {
  167. return base.EllipsisString(a.GetRepoUserName(), 20)
  168. }
  169. // GetRepoName returns the name of the action repository.
  170. func (a *Action) GetRepoName() string {
  171. a.loadRepo()
  172. return a.Repo.Name
  173. }
  174. // ShortRepoName returns the name of the action repository
  175. // trimmed to max 33 chars.
  176. func (a *Action) ShortRepoName() string {
  177. return base.EllipsisString(a.GetRepoName(), 33)
  178. }
  179. // GetRepoPath returns the virtual path to the action repository.
  180. func (a *Action) GetRepoPath() string {
  181. return path.Join(a.GetRepoUserName(), a.GetRepoName())
  182. }
  183. // ShortRepoPath returns the virtual path to the action repository
  184. // trimmed to max 20 + 1 + 33 chars.
  185. func (a *Action) ShortRepoPath() string {
  186. return path.Join(a.ShortRepoUserName(), a.ShortRepoName())
  187. }
  188. // GetRepoLink returns relative link to action repository.
  189. func (a *Action) GetRepoLink() string {
  190. // path.Join will skip empty strings
  191. return path.Join(setting.AppSubURL, "/", url.PathEscape(a.GetRepoUserName()), url.PathEscape(a.GetRepoName()))
  192. }
  193. // GetRepoAbsoluteLink returns the absolute link to action repository.
  194. func (a *Action) GetRepoAbsoluteLink() string {
  195. return setting.AppURL + url.PathEscape(a.GetRepoUserName()) + "/" + url.PathEscape(a.GetRepoName())
  196. }
  197. // GetCommentLink returns link to action comment.
  198. func (a *Action) GetCommentLink() string {
  199. return a.getCommentLink(db.DefaultContext)
  200. }
  201. func (a *Action) getCommentLink(ctx context.Context) string {
  202. if a == nil {
  203. return "#"
  204. }
  205. if a.Comment == nil && a.CommentID != 0 {
  206. a.Comment, _ = issues_model.GetCommentByID(ctx, a.CommentID)
  207. }
  208. if a.Comment != nil {
  209. return a.Comment.HTMLURL()
  210. }
  211. if len(a.GetIssueInfos()) == 0 {
  212. return "#"
  213. }
  214. // Return link to issue
  215. issueIDString := a.GetIssueInfos()[0]
  216. issueID, err := strconv.ParseInt(issueIDString, 10, 64)
  217. if err != nil {
  218. return "#"
  219. }
  220. issue, err := issues_model.GetIssueByID(ctx, issueID)
  221. if err != nil {
  222. return "#"
  223. }
  224. if err = issue.LoadRepo(ctx); err != nil {
  225. return "#"
  226. }
  227. return issue.HTMLURL()
  228. }
  229. // GetBranch returns the action's repository branch.
  230. func (a *Action) GetBranch() string {
  231. return strings.TrimPrefix(a.RefName, git.BranchPrefix)
  232. }
  233. // GetRefLink returns the action's ref link.
  234. func (a *Action) GetRefLink() string {
  235. switch {
  236. case strings.HasPrefix(a.RefName, git.BranchPrefix):
  237. return a.GetRepoLink() + "/src/branch/" + util.PathEscapeSegments(strings.TrimPrefix(a.RefName, git.BranchPrefix))
  238. case strings.HasPrefix(a.RefName, git.TagPrefix):
  239. return a.GetRepoLink() + "/src/tag/" + util.PathEscapeSegments(strings.TrimPrefix(a.RefName, git.TagPrefix))
  240. case len(a.RefName) == 40 && git.IsValidSHAPattern(a.RefName):
  241. return a.GetRepoLink() + "/src/commit/" + a.RefName
  242. default:
  243. // FIXME: we will just assume it's a branch - this was the old way - at some point we may want to enforce that there is always a ref here.
  244. return a.GetRepoLink() + "/src/branch/" + util.PathEscapeSegments(strings.TrimPrefix(a.RefName, git.BranchPrefix))
  245. }
  246. }
  247. // GetTag returns the action's repository tag.
  248. func (a *Action) GetTag() string {
  249. return strings.TrimPrefix(a.RefName, git.TagPrefix)
  250. }
  251. // GetContent returns the action's content.
  252. func (a *Action) GetContent() string {
  253. return a.Content
  254. }
  255. // GetCreate returns the action creation time.
  256. func (a *Action) GetCreate() time.Time {
  257. return a.CreatedUnix.AsTime()
  258. }
  259. // GetIssueInfos returns a list of issues associated with
  260. // the action.
  261. func (a *Action) GetIssueInfos() []string {
  262. return strings.SplitN(a.Content, "|", 3)
  263. }
  264. // GetIssueTitle returns the title of first issue associated
  265. // with the action.
  266. func (a *Action) GetIssueTitle() string {
  267. index, _ := strconv.ParseInt(a.GetIssueInfos()[0], 10, 64)
  268. issue, err := issues_model.GetIssueByIndex(a.RepoID, index)
  269. if err != nil {
  270. log.Error("GetIssueByIndex: %v", err)
  271. return "500 when get issue"
  272. }
  273. return issue.Title
  274. }
  275. // GetIssueContent returns the content of first issue associated with
  276. // this action.
  277. func (a *Action) GetIssueContent() string {
  278. index, _ := strconv.ParseInt(a.GetIssueInfos()[0], 10, 64)
  279. issue, err := issues_model.GetIssueByIndex(a.RepoID, index)
  280. if err != nil {
  281. log.Error("GetIssueByIndex: %v", err)
  282. return "500 when get issue"
  283. }
  284. return issue.Content
  285. }
  286. // GetFeedsOptions options for retrieving feeds
  287. type GetFeedsOptions struct {
  288. db.ListOptions
  289. RequestedUser *user_model.User // the user we want activity for
  290. RequestedTeam *organization.Team // the team we want activity for
  291. RequestedRepo *repo_model.Repository // the repo we want activity for
  292. Actor *user_model.User // the user viewing the activity
  293. IncludePrivate bool // include private actions
  294. OnlyPerformedBy bool // only actions performed by requested user
  295. IncludeDeleted bool // include deleted actions
  296. Date string // the day we want activity for: YYYY-MM-DD
  297. }
  298. // GetFeeds returns actions according to the provided options
  299. func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, error) {
  300. if opts.RequestedUser == nil && opts.RequestedTeam == nil && opts.RequestedRepo == nil {
  301. return nil, fmt.Errorf("need at least one of these filters: RequestedUser, RequestedTeam, RequestedRepo")
  302. }
  303. cond, err := activityQueryCondition(opts)
  304. if err != nil {
  305. return nil, err
  306. }
  307. sess := db.GetEngine(ctx).Where(cond).
  308. Select("`action`.*"). // this line will avoid select other joined table's columns
  309. Join("INNER", "repository", "`repository`.id = `action`.repo_id")
  310. opts.SetDefaultValues()
  311. sess = db.SetSessionPagination(sess, &opts)
  312. actions := make([]*Action, 0, opts.PageSize)
  313. if err := sess.Desc("`action`.created_unix").Find(&actions); err != nil {
  314. return nil, fmt.Errorf("Find: %w", err)
  315. }
  316. if err := ActionList(actions).loadAttributes(ctx); err != nil {
  317. return nil, fmt.Errorf("LoadAttributes: %w", err)
  318. }
  319. return actions, nil
  320. }
  321. // ActivityReadable return whether doer can read activities of user
  322. func ActivityReadable(user, doer *user_model.User) bool {
  323. return !user.KeepActivityPrivate ||
  324. doer != nil && (doer.IsAdmin || user.ID == doer.ID)
  325. }
  326. func activityQueryCondition(opts GetFeedsOptions) (builder.Cond, error) {
  327. cond := builder.NewCond()
  328. if opts.RequestedTeam != nil && opts.RequestedUser == nil {
  329. org, err := user_model.GetUserByID(opts.RequestedTeam.OrgID)
  330. if err != nil {
  331. return nil, err
  332. }
  333. opts.RequestedUser = org
  334. }
  335. // check activity visibility for actor ( similar to activityReadable() )
  336. if opts.Actor == nil {
  337. cond = cond.And(builder.In("act_user_id",
  338. builder.Select("`user`.id").Where(
  339. builder.Eq{"keep_activity_private": false, "visibility": structs.VisibleTypePublic},
  340. ).From("`user`"),
  341. ))
  342. } else if !opts.Actor.IsAdmin {
  343. cond = cond.And(builder.In("act_user_id",
  344. builder.Select("`user`.id").Where(
  345. builder.Eq{"keep_activity_private": false}.
  346. And(builder.In("visibility", structs.VisibleTypePublic, structs.VisibleTypeLimited))).
  347. Or(builder.Eq{"id": opts.Actor.ID}).From("`user`"),
  348. ))
  349. }
  350. // check readable repositories by doer/actor
  351. if opts.Actor == nil || !opts.Actor.IsAdmin {
  352. cond = cond.And(builder.In("repo_id", repo_model.AccessibleRepoIDsQuery(opts.Actor)))
  353. }
  354. if opts.RequestedRepo != nil {
  355. cond = cond.And(builder.Eq{"repo_id": opts.RequestedRepo.ID})
  356. }
  357. if opts.RequestedTeam != nil {
  358. env := organization.OrgFromUser(opts.RequestedUser).AccessibleTeamReposEnv(opts.RequestedTeam)
  359. teamRepoIDs, err := env.RepoIDs(1, opts.RequestedUser.NumRepos)
  360. if err != nil {
  361. return nil, fmt.Errorf("GetTeamRepositories: %w", err)
  362. }
  363. cond = cond.And(builder.In("repo_id", teamRepoIDs))
  364. }
  365. if opts.RequestedUser != nil {
  366. cond = cond.And(builder.Eq{"user_id": opts.RequestedUser.ID})
  367. if opts.OnlyPerformedBy {
  368. cond = cond.And(builder.Eq{"act_user_id": opts.RequestedUser.ID})
  369. }
  370. }
  371. if !opts.IncludePrivate {
  372. cond = cond.And(builder.Eq{"`action`.is_private": false})
  373. }
  374. if !opts.IncludeDeleted {
  375. cond = cond.And(builder.Eq{"is_deleted": false})
  376. }
  377. if opts.Date != "" {
  378. dateLow, err := time.ParseInLocation("2006-01-02", opts.Date, setting.DefaultUILocation)
  379. if err != nil {
  380. log.Warn("Unable to parse %s, filter not applied: %v", opts.Date, err)
  381. } else {
  382. dateHigh := dateLow.Add(86399000000000) // 23h59m59s
  383. cond = cond.And(builder.Gte{"`action`.created_unix": dateLow.Unix()})
  384. cond = cond.And(builder.Lte{"`action`.created_unix": dateHigh.Unix()})
  385. }
  386. }
  387. return cond, nil
  388. }
  389. // DeleteOldActions deletes all old actions from database.
  390. func DeleteOldActions(olderThan time.Duration) (err error) {
  391. if olderThan <= 0 {
  392. return nil
  393. }
  394. _, err = db.GetEngine(db.DefaultContext).Where("created_unix < ?", time.Now().Add(-olderThan).Unix()).Delete(&Action{})
  395. return err
  396. }
  397. // NotifyWatchers creates batch of actions for every watcher.
  398. func NotifyWatchers(ctx context.Context, actions ...*Action) error {
  399. var watchers []*repo_model.Watch
  400. var repo *repo_model.Repository
  401. var err error
  402. var permCode []bool
  403. var permIssue []bool
  404. var permPR []bool
  405. e := db.GetEngine(ctx)
  406. for _, act := range actions {
  407. repoChanged := repo == nil || repo.ID != act.RepoID
  408. if repoChanged {
  409. // Add feeds for user self and all watchers.
  410. watchers, err = repo_model.GetWatchers(ctx, act.RepoID)
  411. if err != nil {
  412. return fmt.Errorf("get watchers: %w", err)
  413. }
  414. }
  415. // Add feed for actioner.
  416. act.UserID = act.ActUserID
  417. if _, err = e.Insert(act); err != nil {
  418. return fmt.Errorf("insert new actioner: %w", err)
  419. }
  420. if repoChanged {
  421. act.loadRepo()
  422. repo = act.Repo
  423. // check repo owner exist.
  424. if err := act.Repo.GetOwner(ctx); err != nil {
  425. return fmt.Errorf("can't get repo owner: %w", err)
  426. }
  427. } else if act.Repo == nil {
  428. act.Repo = repo
  429. }
  430. // Add feed for organization
  431. if act.Repo.Owner.IsOrganization() && act.ActUserID != act.Repo.Owner.ID {
  432. act.ID = 0
  433. act.UserID = act.Repo.Owner.ID
  434. if err = db.Insert(ctx, act); err != nil {
  435. return fmt.Errorf("insert new actioner: %w", err)
  436. }
  437. }
  438. if repoChanged {
  439. permCode = make([]bool, len(watchers))
  440. permIssue = make([]bool, len(watchers))
  441. permPR = make([]bool, len(watchers))
  442. for i, watcher := range watchers {
  443. user, err := user_model.GetUserByIDCtx(ctx, watcher.UserID)
  444. if err != nil {
  445. permCode[i] = false
  446. permIssue[i] = false
  447. permPR[i] = false
  448. continue
  449. }
  450. perm, err := access_model.GetUserRepoPermission(ctx, repo, user)
  451. if err != nil {
  452. permCode[i] = false
  453. permIssue[i] = false
  454. permPR[i] = false
  455. continue
  456. }
  457. permCode[i] = perm.CanRead(unit.TypeCode)
  458. permIssue[i] = perm.CanRead(unit.TypeIssues)
  459. permPR[i] = perm.CanRead(unit.TypePullRequests)
  460. }
  461. }
  462. for i, watcher := range watchers {
  463. if act.ActUserID == watcher.UserID {
  464. continue
  465. }
  466. act.ID = 0
  467. act.UserID = watcher.UserID
  468. act.Repo.Units = nil
  469. switch act.OpType {
  470. case ActionCommitRepo, ActionPushTag, ActionDeleteTag, ActionPublishRelease, ActionDeleteBranch:
  471. if !permCode[i] {
  472. continue
  473. }
  474. case ActionCreateIssue, ActionCommentIssue, ActionCloseIssue, ActionReopenIssue:
  475. if !permIssue[i] {
  476. continue
  477. }
  478. case ActionCreatePullRequest, ActionCommentPull, ActionMergePullRequest, ActionClosePullRequest, ActionReopenPullRequest, ActionAutoMergePullRequest:
  479. if !permPR[i] {
  480. continue
  481. }
  482. }
  483. if err = db.Insert(ctx, act); err != nil {
  484. return fmt.Errorf("insert new action: %w", err)
  485. }
  486. }
  487. }
  488. return nil
  489. }
  490. // NotifyWatchersActions creates batch of actions for every watcher.
  491. func NotifyWatchersActions(acts []*Action) error {
  492. ctx, committer, err := db.TxContext(db.DefaultContext)
  493. if err != nil {
  494. return err
  495. }
  496. defer committer.Close()
  497. for _, act := range acts {
  498. if err := NotifyWatchers(ctx, act); err != nil {
  499. return err
  500. }
  501. }
  502. return committer.Commit()
  503. }
  504. // DeleteIssueActions delete all actions related with issueID
  505. func DeleteIssueActions(ctx context.Context, repoID, issueID int64) error {
  506. // delete actions assigned to this issue
  507. subQuery := builder.Select("`id`").
  508. From("`comment`").
  509. Where(builder.Eq{"`issue_id`": issueID})
  510. if _, err := db.GetEngine(ctx).In("comment_id", subQuery).Delete(&Action{}); err != nil {
  511. return err
  512. }
  513. _, err := db.GetEngine(ctx).Table("action").Where("repo_id = ?", repoID).
  514. In("op_type", ActionCreateIssue, ActionCreatePullRequest).
  515. Where("content LIKE ?", strconv.FormatInt(issueID, 10)+"|%").
  516. Delete(&Action{})
  517. return err
  518. }
  519. // CountActionCreatedUnixString count actions where created_unix is an empty string
  520. func CountActionCreatedUnixString(ctx context.Context) (int64, error) {
  521. if setting.Database.UseSQLite3 {
  522. return db.GetEngine(ctx).Where(`created_unix = ""`).Count(new(Action))
  523. }
  524. return 0, nil
  525. }
  526. // FixActionCreatedUnixString set created_unix to zero if it is an empty string
  527. func FixActionCreatedUnixString(ctx context.Context) (int64, error) {
  528. if setting.Database.UseSQLite3 {
  529. res, err := db.GetEngine(ctx).Exec(`UPDATE action SET created_unix = 0 WHERE created_unix = ""`)
  530. if err != nil {
  531. return 0, err
  532. }
  533. return res.RowsAffected()
  534. }
  535. return 0, nil
  536. }