2014-03-13 06:16:14 +01:00
// Copyright 2014 The Gogs Authors. All rights reserved.
2019-05-01 18:21:05 +02:00
// Copyright 2019 The Gitea Authors. All rights reserved.
2022-11-27 19:20:29 +01:00
// SPDX-License-Identifier: MIT
2014-03-13 06:16:14 +01:00
2022-08-25 04:31:57 +02:00
package activities
2014-03-13 06:16:14 +01:00
import (
2021-12-10 02:27:50 +01:00
"context"
2014-05-06 17:50:31 +02:00
"fmt"
2021-11-16 19:18:25 +01:00
"net/url"
2014-07-26 06:24:27 +02:00
"path"
2017-06-25 20:20:29 +02:00
"strconv"
2014-04-14 04:20:28 +02:00
"strings"
2014-03-13 06:16:14 +01:00
"time"
2014-03-22 11:20:00 +01:00
2021-09-19 13:49:59 +02:00
"code.gitea.io/gitea/models/db"
2022-06-13 11:37:59 +02:00
issues_model "code.gitea.io/gitea/models/issues"
2022-03-29 08:29:02 +02:00
"code.gitea.io/gitea/models/organization"
2022-05-11 12:09:36 +02:00
access_model "code.gitea.io/gitea/models/perm/access"
2021-12-10 02:27:50 +01:00
repo_model "code.gitea.io/gitea/models/repo"
2021-12-12 16:48:20 +01:00
"code.gitea.io/gitea/models/unit"
2021-11-24 10:49:20 +01:00
user_model "code.gitea.io/gitea/models/user"
2016-11-10 17:24:48 +01:00
"code.gitea.io/gitea/modules/base"
2020-12-14 18:08:37 +01:00
"code.gitea.io/gitea/modules/git"
2016-11-10 17:24:48 +01:00
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
2022-03-10 15:54:51 +01:00
"code.gitea.io/gitea/modules/structs"
2019-08-15 16:46:21 +02:00
"code.gitea.io/gitea/modules/timeutil"
2017-12-03 03:20:12 +01:00
2019-06-23 17:22:43 +02:00
"xorm.io/builder"
2022-06-18 10:46:50 +02:00
"xorm.io/xorm/schemas"
2014-03-13 06:16:14 +01:00
)
2016-11-22 11:43:30 +01:00
// ActionType represents the type of an action.
2014-07-26 06:24:27 +02:00
type ActionType int
2016-11-22 11:43:30 +01:00
// Possible action types.
2014-03-13 06:16:14 +01:00
const (
2021-06-23 06:14:22 +02:00
ActionCreateRepo ActionType = iota + 1 // 1
ActionRenameRepo // 2
ActionStarRepo // 3
ActionWatchRepo // 4
ActionCommitRepo // 5
ActionCreateIssue // 6
ActionCreatePullRequest // 7
ActionTransferRepo // 8
ActionPushTag // 9
ActionCommentIssue // 10
ActionMergePullRequest // 11
ActionCloseIssue // 12
ActionReopenIssue // 13
ActionClosePullRequest // 14
ActionReopenPullRequest // 15
ActionDeleteTag // 16
ActionDeleteBranch // 17
ActionMirrorSyncPush // 18
ActionMirrorSyncCreate // 19
ActionMirrorSyncDelete // 20
ActionApprovePullRequest // 21
ActionRejectPullRequest // 22
ActionCommentPull // 23
ActionPublishRelease // 24
ActionPullReviewDismissed // 25
ActionPullRequestReadyForReview // 26
2022-11-03 16:49:00 +01:00
ActionAutoMergePullRequest // 27
2014-03-13 06:16:14 +01:00
)
2023-04-04 15:35:31 +02:00
func ( at ActionType ) String ( ) string {
switch at {
case ActionCreateRepo :
return "create_repo"
case ActionRenameRepo :
return "rename_repo"
case ActionStarRepo :
return "star_repo"
case ActionWatchRepo :
return "watch_repo"
case ActionCommitRepo :
return "commit_repo"
case ActionCreateIssue :
return "create_issue"
case ActionCreatePullRequest :
return "create_pull_request"
case ActionTransferRepo :
return "transfer_repo"
case ActionPushTag :
return "push_tag"
case ActionCommentIssue :
return "comment_issue"
case ActionMergePullRequest :
return "merge_pull_request"
case ActionCloseIssue :
return "close_issue"
case ActionReopenIssue :
return "reopen_issue"
case ActionClosePullRequest :
return "close_pull_request"
case ActionReopenPullRequest :
return "reopen_pull_request"
case ActionDeleteTag :
return "delete_tag"
case ActionDeleteBranch :
return "delete_branch"
case ActionMirrorSyncPush :
return "mirror_sync_push"
case ActionMirrorSyncCreate :
return "mirror_sync_create"
case ActionMirrorSyncDelete :
return "mirror_sync_delete"
case ActionApprovePullRequest :
return "approve_pull_request"
case ActionRejectPullRequest :
return "reject_pull_request"
case ActionCommentPull :
return "comment_pull"
case ActionPublishRelease :
return "publish_release"
case ActionPullReviewDismissed :
return "pull_review_dismissed"
case ActionPullRequestReadyForReview :
return "pull_request_ready_for_review"
case ActionAutoMergePullRequest :
return "auto_merge_pull_request"
default :
return "action-" + strconv . Itoa ( int ( at ) )
}
}
2016-11-22 11:43:30 +01:00
// Action represents user operation type and other information to
// repository. It implemented interface base.Actioner so that can be
// used in template render.
2014-03-13 06:16:14 +01:00
type Action struct {
2017-05-26 03:38:18 +02:00
ID int64 ` xorm:"pk autoincr" `
2022-06-18 10:46:50 +02:00
UserID int64 // Receiver user id.
2017-05-26 03:38:18 +02:00
OpType ActionType
2022-06-18 10:46:50 +02:00
ActUserID int64 // Action user id.
ActUser * user_model . User ` xorm:"-" `
RepoID int64
2021-12-10 02:27:50 +01:00
Repo * repo_model . Repository ` xorm:"-" `
CommentID int64 ` xorm:"INDEX" `
2022-06-13 11:37:59 +02:00
Comment * issues_model . Comment ` xorm:"-" `
2022-06-18 10:46:50 +02:00
IsDeleted bool ` xorm:"NOT NULL DEFAULT false" `
2017-05-26 03:38:18 +02:00
RefName string
2022-06-18 10:46:50 +02:00
IsPrivate bool ` xorm:"NOT NULL DEFAULT false" `
2019-08-15 16:46:21 +02:00
Content string ` xorm:"TEXT" `
2022-06-18 10:46:50 +02:00
CreatedUnix timeutil . TimeStamp ` xorm:"created" `
2015-08-19 18:12:43 +02:00
}
2021-09-19 13:49:59 +02:00
func init ( ) {
db . RegisterModel ( new ( Action ) )
}
2022-06-18 10:46:50 +02:00
// TableIndices implements xorm's TableIndices interface
func ( a * Action ) TableIndices ( ) [ ] * schemas . Index {
2022-07-01 18:04:01 +02:00
repoIndex := schemas . NewIndex ( "r_u_d" , schemas . IndexType )
repoIndex . AddColumn ( "repo_id" , "user_id" , "is_deleted" )
2022-06-18 10:46:50 +02:00
actUserIndex := schemas . NewIndex ( "au_r_c_u_d" , schemas . IndexType )
actUserIndex . AddColumn ( "act_user_id" , "repo_id" , "created_unix" , "user_id" , "is_deleted" )
2023-03-24 16:44:33 +01:00
cudIndex := schemas . NewIndex ( "c_u_d" , schemas . IndexType )
cudIndex . AddColumn ( "created_unix" , "user_id" , "is_deleted" )
indices := [ ] * schemas . Index { actUserIndex , repoIndex , cudIndex }
2022-09-03 18:27:59 +02:00
return indices
2022-06-18 10:46:50 +02:00
}
2016-11-22 11:43:30 +01:00
// GetOpType gets the ActionType of this action.
2017-09-20 03:22:42 +02:00
func ( a * Action ) GetOpType ( ) ActionType {
return a . OpType
2014-03-15 05:50:51 +01:00
}
2020-12-09 06:11:15 +01:00
// LoadActUser loads a.ActUser
2022-12-03 03:48:26 +01:00
func ( a * Action ) LoadActUser ( ctx context . Context ) {
2017-05-26 03:38:18 +02:00
if a . ActUser != nil {
return
}
var err error
2022-12-03 03:48:26 +01:00
a . ActUser , err = user_model . GetUserByID ( ctx , a . ActUserID )
2017-05-26 03:38:18 +02:00
if err == nil {
return
2021-11-24 10:49:20 +01:00
} else if user_model . IsErrUserNotExist ( err ) {
a . ActUser = user_model . NewGhostUser ( )
2017-05-26 03:38:18 +02:00
} else {
2019-04-02 09:48:31 +02:00
log . Error ( "GetUserByID(%d): %v" , a . ActUserID , err )
2017-05-26 03:38:18 +02:00
}
}
2022-12-03 03:48:26 +01:00
func ( a * Action ) loadRepo ( ctx context . Context ) {
2017-06-14 02:37:50 +02:00
if a . Repo != nil {
2017-05-26 03:38:18 +02:00
return
}
var err error
2022-12-03 03:48:26 +01:00
a . Repo , err = repo_model . GetRepositoryByID ( ctx , a . RepoID )
2017-05-26 03:38:18 +02:00
if err != nil {
2021-12-10 02:27:50 +01:00
log . Error ( "repo_model.GetRepositoryByID(%d): %v" , a . RepoID , err )
2017-05-26 03:38:18 +02:00
}
}
2018-07-05 19:48:18 +02:00
// GetActFullName gets the action's user full name.
func ( a * Action ) GetActFullName ( ) string {
2022-12-03 03:48:26 +01:00
a . LoadActUser ( db . DefaultContext )
2018-07-05 19:48:18 +02:00
return a . ActUser . FullName
}
2016-11-22 11:43:30 +01:00
// GetActUserName gets the action's user name.
2016-01-11 13:41:43 +01:00
func ( a * Action ) GetActUserName ( ) string {
2022-12-03 03:48:26 +01:00
a . LoadActUser ( db . DefaultContext )
2017-05-26 03:38:18 +02:00
return a . ActUser . Name
2014-03-15 05:50:51 +01:00
}
2016-11-22 11:43:30 +01:00
// ShortActUserName gets the action's user name trimmed to max 20
// chars.
2016-01-11 13:41:43 +01:00
func ( a * Action ) ShortActUserName ( ) string {
2017-05-26 03:38:18 +02:00
return base . EllipsisString ( a . GetActUserName ( ) , 20 )
2016-01-11 13:41:43 +01:00
}
2020-02-26 23:08:24 +01:00
// GetDisplayName gets the action's display name based on DEFAULT_SHOW_FULL_NAME, or falls back to the username if it is blank.
2019-05-08 10:41:35 +02:00
func ( a * Action ) GetDisplayName ( ) string {
if setting . UI . DefaultShowFullName {
2020-02-26 23:08:24 +01:00
trimmedFullName := strings . TrimSpace ( a . GetActFullName ( ) )
if len ( trimmedFullName ) > 0 {
return trimmedFullName
}
2019-05-08 10:41:35 +02:00
}
return a . ShortActUserName ( )
}
// GetDisplayNameTitle gets the action's display name used for the title (tooltip) based on DEFAULT_SHOW_FULL_NAME
func ( a * Action ) GetDisplayNameTitle ( ) string {
if setting . UI . DefaultShowFullName {
return a . ShortActUserName ( )
}
return a . GetActFullName ( )
}
2016-11-22 11:43:30 +01:00
// GetRepoUserName returns the name of the action repository owner.
2016-01-11 13:41:43 +01:00
func ( a * Action ) GetRepoUserName ( ) string {
2022-12-03 03:48:26 +01:00
a . loadRepo ( db . DefaultContext )
2020-01-12 10:36:21 +01:00
return a . Repo . OwnerName
2014-05-09 08:42:50 +02:00
}
2016-11-22 11:43:30 +01:00
// ShortRepoUserName returns the name of the action repository owner
// trimmed to max 20 chars.
2016-01-11 13:41:43 +01:00
func ( a * Action ) ShortRepoUserName ( ) string {
2017-05-26 03:38:18 +02:00
return base . EllipsisString ( a . GetRepoUserName ( ) , 20 )
2016-01-11 13:41:43 +01:00
}
2016-11-22 11:43:30 +01:00
// GetRepoName returns the name of the action repository.
2016-01-11 13:41:43 +01:00
func ( a * Action ) GetRepoName ( ) string {
2022-12-03 03:48:26 +01:00
a . loadRepo ( db . DefaultContext )
2017-05-26 03:38:18 +02:00
return a . Repo . Name
2014-03-13 06:16:14 +01:00
}
2016-11-22 11:43:30 +01:00
// ShortRepoName returns the name of the action repository
// trimmed to max 33 chars.
2016-01-11 13:41:43 +01:00
func ( a * Action ) ShortRepoName ( ) string {
2017-05-26 03:38:18 +02:00
return base . EllipsisString ( a . GetRepoName ( ) , 33 )
2016-01-11 13:41:43 +01:00
}
2016-11-22 11:43:30 +01:00
// GetRepoPath returns the virtual path to the action repository.
2016-01-11 13:41:43 +01:00
func ( a * Action ) GetRepoPath ( ) string {
2017-05-26 03:38:18 +02:00
return path . Join ( a . GetRepoUserName ( ) , a . GetRepoName ( ) )
2016-01-15 11:00:39 +01:00
}
2016-11-22 11:43:30 +01:00
// ShortRepoPath returns the virtual path to the action repository
2017-01-05 01:50:34 +01:00
// trimmed to max 20 + 1 + 33 chars.
2016-01-15 11:00:39 +01:00
func ( a * Action ) ShortRepoPath ( ) string {
2016-01-11 13:41:43 +01:00
return path . Join ( a . ShortRepoUserName ( ) , a . ShortRepoName ( ) )
2015-03-12 21:01:23 +01:00
}
2016-11-22 11:43:30 +01:00
// GetRepoLink returns relative link to action repository.
2016-01-11 13:41:43 +01:00
func ( a * Action ) GetRepoLink ( ) string {
2021-11-16 19:18:25 +01:00
// path.Join will skip empty strings
return path . Join ( setting . AppSubURL , "/" , url . PathEscape ( a . GetRepoUserName ( ) ) , url . PathEscape ( a . GetRepoName ( ) ) )
2014-07-26 06:24:27 +02:00
}
2022-09-21 22:51:42 +02:00
// GetRepoAbsoluteLink returns the absolute link to action repository.
func ( a * Action ) GetRepoAbsoluteLink ( ) string {
return setting . AppURL + url . PathEscape ( a . GetRepoUserName ( ) ) + "/" + url . PathEscape ( a . GetRepoName ( ) )
}
2023-02-06 19:09:18 +01:00
// GetCommentHTMLURL returns link to action comment.
func ( a * Action ) GetCommentHTMLURL ( ) string {
return a . getCommentHTMLURL ( db . DefaultContext )
}
func ( a * Action ) loadComment ( ctx context . Context ) ( err error ) {
if a . CommentID == 0 || a . Comment != nil {
return nil
}
a . Comment , err = issues_model . GetCommentByID ( ctx , a . CommentID )
return err
}
func ( a * Action ) getCommentHTMLURL ( ctx context . Context ) string {
if a == nil {
return "#"
}
_ = a . loadComment ( ctx )
if a . Comment != nil {
return a . Comment . HTMLURL ( )
}
if len ( a . GetIssueInfos ( ) ) == 0 {
return "#"
}
// Return link to issue
issueIDString := a . GetIssueInfos ( ) [ 0 ]
issueID , err := strconv . ParseInt ( issueIDString , 10 , 64 )
if err != nil {
return "#"
}
issue , err := issues_model . GetIssueByID ( ctx , issueID )
if err != nil {
return "#"
}
if err = issue . LoadRepo ( ctx ) ; err != nil {
return "#"
}
return issue . HTMLURL ( )
}
2017-06-25 20:20:29 +02:00
// GetCommentLink returns link to action comment.
func ( a * Action ) GetCommentLink ( ) string {
2021-12-10 02:27:50 +01:00
return a . getCommentLink ( db . DefaultContext )
2018-12-13 16:55:43 +01:00
}
2021-12-10 02:27:50 +01:00
func ( a * Action ) getCommentLink ( ctx context . Context ) string {
2017-06-25 20:20:29 +02:00
if a == nil {
return "#"
}
2023-02-06 19:09:18 +01:00
_ = a . loadComment ( ctx )
2017-06-25 20:20:29 +02:00
if a . Comment != nil {
2023-02-06 19:09:18 +01:00
return a . Comment . Link ( )
2017-06-25 20:20:29 +02:00
}
if len ( a . GetIssueInfos ( ) ) == 0 {
return "#"
}
2021-03-14 19:52:12 +01:00
// Return link to issue
2017-06-25 20:20:29 +02:00
issueIDString := a . GetIssueInfos ( ) [ 0 ]
issueID , err := strconv . ParseInt ( issueIDString , 10 , 64 )
if err != nil {
return "#"
}
2022-06-13 11:37:59 +02:00
issue , err := issues_model . GetIssueByID ( ctx , issueID )
2017-06-25 20:20:29 +02:00
if err != nil {
return "#"
}
2022-04-08 11:11:15 +02:00
if err = issue . LoadRepo ( ctx ) ; err != nil {
2018-12-13 16:55:43 +01:00
return "#"
}
2023-02-06 19:09:18 +01:00
return issue . Link ( )
2017-06-25 20:20:29 +02:00
}
2016-11-22 11:43:30 +01:00
// GetBranch returns the action's repository branch.
2016-01-11 13:41:43 +01:00
func ( a * Action ) GetBranch ( ) string {
2020-12-14 18:08:37 +01:00
return strings . TrimPrefix ( a . RefName , git . BranchPrefix )
2014-03-16 16:30:35 +01:00
}
2021-12-16 20:01:14 +01:00
// GetRefLink returns the action's ref link.
func ( a * Action ) GetRefLink ( ) string {
2023-05-26 03:04:48 +02:00
return git . RefURL ( a . GetRepoLink ( ) , a . RefName )
2021-12-16 20:01:14 +01:00
}
2020-12-20 00:46:28 +01:00
// GetTag returns the action's repository tag.
func ( a * Action ) GetTag ( ) string {
return strings . TrimPrefix ( a . RefName , git . TagPrefix )
}
2016-11-22 11:43:30 +01:00
// GetContent returns the action's content.
2016-01-11 13:41:43 +01:00
func ( a * Action ) GetContent ( ) string {
2014-03-23 11:27:01 +01:00
return a . Content
2014-03-23 11:00:09 +01:00
}
2016-11-22 11:43:30 +01:00
// GetCreate returns the action creation time.
2016-01-11 13:41:43 +01:00
func ( a * Action ) GetCreate ( ) time . Time {
2017-12-11 05:37:04 +01:00
return a . CreatedUnix . AsTime ( )
2014-07-26 06:24:27 +02:00
}
2016-11-22 11:43:30 +01:00
// GetIssueInfos returns a list of issues associated with
// the action.
2016-01-11 13:41:43 +01:00
func ( a * Action ) GetIssueInfos ( ) [ ] string {
2021-02-11 18:32:25 +01:00
return strings . SplitN ( a . Content , "|" , 3 )
2014-07-26 06:24:27 +02:00
}
2016-11-22 11:43:30 +01:00
// GetIssueTitle returns the title of first issue associated
// with the action.
2016-01-11 13:41:43 +01:00
func ( a * Action ) GetIssueTitle ( ) string {
2020-12-25 10:59:32 +01:00
index , _ := strconv . ParseInt ( a . GetIssueInfos ( ) [ 0 ] , 10 , 64 )
2022-06-13 11:37:59 +02:00
issue , err := issues_model . GetIssueByIndex ( a . RepoID , index )
2015-11-12 22:16:51 +01:00
if err != nil {
2019-04-02 09:48:31 +02:00
log . Error ( "GetIssueByIndex: %v" , err )
2015-11-13 18:11:45 +01:00
return "500 when get issue"
2015-11-12 22:16:51 +01:00
}
2016-08-14 12:32:24 +02:00
return issue . Title
2015-11-12 21:09:48 +01:00
}
2016-11-22 11:43:30 +01:00
// GetIssueContent returns the content of first issue associated with
// this action.
2016-01-11 13:41:43 +01:00
func ( a * Action ) GetIssueContent ( ) string {
2020-12-25 10:59:32 +01:00
index , _ := strconv . ParseInt ( a . GetIssueInfos ( ) [ 0 ] , 10 , 64 )
2022-06-13 11:37:59 +02:00
issue , err := issues_model . GetIssueByIndex ( a . RepoID , index )
2015-11-13 18:11:45 +01:00
if err != nil {
2019-04-02 09:48:31 +02:00
log . Error ( "GetIssueByIndex: %v" , err )
2015-11-13 18:11:45 +01:00
return "500 when get issue"
}
return issue . Content
}
2017-06-02 02:42:25 +02:00
// GetFeedsOptions options for retrieving feeds
type GetFeedsOptions struct {
2022-03-10 15:54:51 +01:00
db . ListOptions
RequestedUser * user_model . User // the user we want activity for
2022-03-29 08:29:02 +02:00
RequestedTeam * organization . Team // the team we want activity for
2022-03-10 15:54:51 +01:00
RequestedRepo * repo_model . Repository // the repo we want activity for
Actor * user_model . User // the user viewing the activity
IncludePrivate bool // include private actions
OnlyPerformedBy bool // only actions performed by requested user
IncludeDeleted bool // include deleted actions
Date string // the day we want activity for: YYYY-MM-DD
2017-06-02 02:42:25 +02:00
}
// GetFeeds returns actions according to the provided options
2023-02-24 22:15:10 +01:00
func GetFeeds ( ctx context . Context , opts GetFeedsOptions ) ( ActionList , int64 , error ) {
2022-03-10 15:54:51 +01:00
if opts . RequestedUser == nil && opts . RequestedTeam == nil && opts . RequestedRepo == nil {
2023-02-24 22:15:10 +01:00
return nil , 0 , fmt . Errorf ( "need at least one of these filters: RequestedUser, RequestedTeam, RequestedRepo" )
2020-12-22 03:53:37 +01:00
}
2017-08-23 03:30:54 +02:00
2020-12-22 03:53:37 +01:00
cond , err := activityQueryCondition ( opts )
if err != nil {
2023-02-24 22:15:10 +01:00
return nil , 0 , err
2020-12-22 03:53:37 +01:00
}
2020-01-13 18:33:46 +01:00
2022-05-20 16:08:52 +02:00
sess := db . GetEngine ( ctx ) . Where ( cond ) .
2022-05-09 17:19:16 +02:00
Select ( "`action`.*" ) . // this line will avoid select other joined table's columns
Join ( "INNER" , "repository" , "`repository`.id = `action`.repo_id" )
2020-12-22 03:53:37 +01:00
2022-03-10 15:54:51 +01:00
opts . SetDefaultValues ( )
sess = db . SetSessionPagination ( sess , & opts )
actions := make ( [ ] * Action , 0 , opts . PageSize )
2023-02-24 22:15:10 +01:00
count , err := sess . Desc ( "`action`.created_unix" ) . FindAndCount ( & actions )
if err != nil {
return nil , 0 , fmt . Errorf ( "FindAndCount: %w" , err )
2020-01-13 18:33:46 +01:00
}
2022-05-20 16:08:52 +02:00
if err := ActionList ( actions ) . loadAttributes ( ctx ) ; err != nil {
2023-02-24 22:15:10 +01:00
return nil , 0 , fmt . Errorf ( "LoadAttributes: %w" , err )
2020-12-22 03:53:37 +01:00
}
2023-02-24 22:15:10 +01:00
return actions , count , nil
2020-12-22 03:53:37 +01:00
}
2022-08-25 04:31:57 +02:00
// ActivityReadable return whether doer can read activities of user
func ActivityReadable ( user , doer * user_model . User ) bool {
2022-03-10 15:54:51 +01:00
return ! user . KeepActivityPrivate ||
doer != nil && ( doer . IsAdmin || user . ID == doer . ID )
2020-12-22 03:53:37 +01:00
}
2017-08-23 03:30:54 +02:00
2020-12-22 03:53:37 +01:00
func activityQueryCondition ( opts GetFeedsOptions ) ( builder . Cond , error ) {
cond := builder . NewCond ( )
2022-03-10 15:54:51 +01:00
if opts . RequestedTeam != nil && opts . RequestedUser == nil {
2022-12-03 03:48:26 +01:00
org , err := user_model . GetUserByID ( db . DefaultContext , opts . RequestedTeam . OrgID )
2022-03-10 15:54:51 +01:00
if err != nil {
return nil , err
}
opts . RequestedUser = org
}
// check activity visibility for actor ( similar to activityReadable() )
if opts . Actor == nil {
cond = cond . And ( builder . In ( "act_user_id" ,
builder . Select ( "`user`.id" ) . Where (
builder . Eq { "keep_activity_private" : false , "visibility" : structs . VisibleTypePublic } ,
) . From ( "`user`" ) ,
) )
} else if ! opts . Actor . IsAdmin {
2023-05-10 06:14:58 +02:00
uidCond := builder . Select ( "`user`.id" ) . From ( "`user`" ) . Where (
builder . Eq { "keep_activity_private" : false } .
And ( builder . In ( "visibility" , structs . VisibleTypePublic , structs . VisibleTypeLimited ) ) ) .
Or ( builder . Eq { "id" : opts . Actor . ID } )
if opts . RequestedUser != nil {
if opts . RequestedUser . IsOrganization ( ) {
// An organization can always see the activities whose `act_user_id` is the same as its id.
uidCond = uidCond . Or ( builder . Eq { "id" : opts . RequestedUser . ID } )
} else {
// A user can always see the activities of the organizations to which the user belongs.
uidCond = uidCond . Or (
builder . Eq { "type" : user_model . UserTypeOrganization } .
And ( builder . In ( "`user`.id" , builder . Select ( "org_id" ) .
Where ( builder . Eq { "uid" : opts . RequestedUser . ID } ) .
From ( "team_user" ) ) ) ,
)
}
}
cond = cond . And ( builder . In ( "act_user_id" , uidCond ) )
2017-08-23 03:30:54 +02:00
}
2020-12-22 03:53:37 +01:00
// check readable repositories by doer/actor
2020-06-05 22:01:53 +02:00
if opts . Actor == nil || ! opts . Actor . IsAdmin {
2022-06-06 10:01:49 +02:00
cond = cond . And ( builder . In ( "repo_id" , repo_model . AccessibleRepoIDsQuery ( opts . Actor ) ) )
2022-03-10 15:54:51 +01:00
}
if opts . RequestedRepo != nil {
cond = cond . And ( builder . Eq { "repo_id" : opts . RequestedRepo . ID } )
2020-06-05 22:01:53 +02:00
}
2020-12-27 20:58:03 +01:00
if opts . RequestedTeam != nil {
2022-03-29 08:29:02 +02:00
env := organization . OrgFromUser ( opts . RequestedUser ) . AccessibleTeamReposEnv ( opts . RequestedTeam )
2020-12-27 20:58:03 +01:00
teamRepoIDs , err := env . RepoIDs ( 1 , opts . RequestedUser . NumRepos )
if err != nil {
2022-10-24 21:29:17 +02:00
return nil , fmt . Errorf ( "GetTeamRepositories: %w" , err )
2020-12-27 20:58:03 +01:00
}
cond = cond . And ( builder . In ( "repo_id" , teamRepoIDs ) )
}
2022-03-10 15:54:51 +01:00
if opts . RequestedUser != nil {
cond = cond . And ( builder . Eq { "user_id" : opts . RequestedUser . ID } )
2016-02-06 08:52:21 +01:00
2022-03-10 15:54:51 +01:00
if opts . OnlyPerformedBy {
cond = cond . And ( builder . Eq { "act_user_id" : opts . RequestedUser . ID } )
}
2017-06-02 02:42:25 +02:00
}
2022-03-10 15:54:51 +01:00
2017-06-02 02:42:25 +02:00
if ! opts . IncludePrivate {
2022-05-05 17:39:26 +02:00
cond = cond . And ( builder . Eq { "`action`.is_private" : false } )
2017-06-02 02:42:25 +02:00
}
2017-06-25 20:20:29 +02:00
if ! opts . IncludeDeleted {
2017-08-23 03:30:54 +02:00
cond = cond . And ( builder . Eq { "is_deleted" : false } )
2017-06-25 20:20:29 +02:00
}
2021-02-20 23:08:58 +01:00
if opts . Date != "" {
2021-04-01 12:52:17 +02:00
dateLow , err := time . ParseInLocation ( "2006-01-02" , opts . Date , setting . DefaultUILocation )
2021-02-20 23:08:58 +01:00
if err != nil {
log . Warn ( "Unable to parse %s, filter not applied: %v" , opts . Date , err )
} else {
dateHigh := dateLow . Add ( 86399000000000 ) // 23h59m59s
2022-05-05 17:39:26 +02:00
cond = cond . And ( builder . Gte { "`action`.created_unix" : dateLow . Unix ( ) } )
cond = cond . And ( builder . Lte { "`action`.created_unix" : dateHigh . Unix ( ) } )
2021-02-20 23:08:58 +01:00
}
}
2020-12-22 03:53:37 +01:00
return cond , nil
2014-03-13 06:16:14 +01:00
}
2021-05-01 14:17:02 +02:00
// DeleteOldActions deletes all old actions from database.
func DeleteOldActions ( olderThan time . Duration ) ( err error ) {
if olderThan <= 0 {
return nil
}
2021-09-23 17:45:36 +02:00
_ , err = db . GetEngine ( db . DefaultContext ) . Where ( "created_unix < ?" , time . Now ( ) . Add ( - olderThan ) . Unix ( ) ) . Delete ( & Action { } )
2022-06-20 12:02:49 +02:00
return err
2021-05-01 14:17:02 +02:00
}
2021-12-12 16:48:20 +01:00
2022-11-19 09:12:33 +01:00
// NotifyWatchers creates batch of actions for every watcher.
func NotifyWatchers ( ctx context . Context , actions ... * Action ) error {
2021-12-12 16:48:20 +01:00
var watchers [ ] * repo_model . Watch
var repo * repo_model . Repository
var err error
var permCode [ ] bool
var permIssue [ ] bool
var permPR [ ] bool
e := db . GetEngine ( ctx )
for _ , act := range actions {
repoChanged := repo == nil || repo . ID != act . RepoID
if repoChanged {
// Add feeds for user self and all watchers.
2023-03-12 13:28:18 +01:00
watchers , err = repo_model . GetWatchersExcludeBlocked ( ctx , act . RepoID , act . ActUserID )
2021-12-12 16:48:20 +01:00
if err != nil {
2022-10-24 21:29:17 +02:00
return fmt . Errorf ( "get watchers: %w" , err )
2021-12-12 16:48:20 +01:00
}
}
// Add feed for actioner.
act . UserID = act . ActUserID
if _ , err = e . Insert ( act ) ; err != nil {
2022-10-24 21:29:17 +02:00
return fmt . Errorf ( "insert new actioner: %w" , err )
2021-12-12 16:48:20 +01:00
}
if repoChanged {
2022-12-03 03:48:26 +01:00
act . loadRepo ( ctx )
2021-12-12 16:48:20 +01:00
repo = act . Repo
// check repo owner exist.
2023-02-18 13:11:03 +01:00
if err := act . Repo . LoadOwner ( ctx ) ; err != nil {
2022-10-24 21:29:17 +02:00
return fmt . Errorf ( "can't get repo owner: %w" , err )
2021-12-12 16:48:20 +01:00
}
} else if act . Repo == nil {
act . Repo = repo
}
// Add feed for organization
if act . Repo . Owner . IsOrganization ( ) && act . ActUserID != act . Repo . Owner . ID {
act . ID = 0
act . UserID = act . Repo . Owner . ID
2022-06-04 21:18:50 +02:00
if err = db . Insert ( ctx , act ) ; err != nil {
2022-10-24 21:29:17 +02:00
return fmt . Errorf ( "insert new actioner: %w" , err )
2021-12-12 16:48:20 +01:00
}
}
if repoChanged {
permCode = make ( [ ] bool , len ( watchers ) )
permIssue = make ( [ ] bool , len ( watchers ) )
permPR = make ( [ ] bool , len ( watchers ) )
for i , watcher := range watchers {
2022-12-03 03:48:26 +01:00
user , err := user_model . GetUserByID ( ctx , watcher . UserID )
2021-12-12 16:48:20 +01:00
if err != nil {
permCode [ i ] = false
permIssue [ i ] = false
permPR [ i ] = false
continue
}
2022-05-11 12:09:36 +02:00
perm , err := access_model . GetUserRepoPermission ( ctx , repo , user )
2021-12-12 16:48:20 +01:00
if err != nil {
permCode [ i ] = false
permIssue [ i ] = false
permPR [ i ] = false
continue
}
permCode [ i ] = perm . CanRead ( unit . TypeCode )
permIssue [ i ] = perm . CanRead ( unit . TypeIssues )
permPR [ i ] = perm . CanRead ( unit . TypePullRequests )
}
}
for i , watcher := range watchers {
if act . ActUserID == watcher . UserID {
continue
}
act . ID = 0
act . UserID = watcher . UserID
act . Repo . Units = nil
switch act . OpType {
case ActionCommitRepo , ActionPushTag , ActionDeleteTag , ActionPublishRelease , ActionDeleteBranch :
if ! permCode [ i ] {
continue
}
case ActionCreateIssue , ActionCommentIssue , ActionCloseIssue , ActionReopenIssue :
if ! permIssue [ i ] {
continue
}
2022-11-03 16:49:00 +01:00
case ActionCreatePullRequest , ActionCommentPull , ActionMergePullRequest , ActionClosePullRequest , ActionReopenPullRequest , ActionAutoMergePullRequest :
2021-12-12 16:48:20 +01:00
if ! permPR [ i ] {
continue
}
}
2022-06-04 21:18:50 +02:00
if err = db . Insert ( ctx , act ) ; err != nil {
2022-10-24 21:29:17 +02:00
return fmt . Errorf ( "insert new action: %w" , err )
2021-12-12 16:48:20 +01:00
}
}
}
return nil
}
// NotifyWatchersActions creates batch of actions for every watcher.
func NotifyWatchersActions ( acts [ ] * Action ) error {
2022-11-12 21:18:50 +01:00
ctx , committer , err := db . TxContext ( db . DefaultContext )
2021-12-12 16:48:20 +01:00
if err != nil {
return err
}
defer committer . Close ( )
for _ , act := range acts {
2022-11-19 09:12:33 +01:00
if err := NotifyWatchers ( ctx , act ) ; err != nil {
2021-12-12 16:48:20 +01:00
return err
}
}
return committer . Commit ( )
}
2022-06-13 11:37:59 +02:00
// DeleteIssueActions delete all actions related with issueID
func DeleteIssueActions ( ctx context . Context , repoID , issueID int64 ) error {
// delete actions assigned to this issue
2023-07-29 16:59:23 +02:00
// MySQL doesn't use the indexes on comment_id when using a subquery.
// It does uses the indexes when using an JOIN, however SQLite doesn't
// allow JOINs in DELETE statements and XORM doesn't allow them as well.
// So, an specific raw SQL query for MySQL so the query makes use of indexes.
if setting . Database . Type . IsMySQL ( ) {
if _ , err := db . GetEngine ( ctx ) . Exec ( "DELETE action FROM action JOIN comment ON action.comment_id = comment.id WHERE comment.issue_id = ?" , issueID ) ; err != nil {
return err
}
} else {
subQuery := builder . Select ( "`id`" ) . From ( "`comment`" ) .
Where ( builder . Eq { "`issue_id`" : issueID } )
if _ , err := db . GetEngine ( ctx ) . In ( "comment_id" , subQuery ) . Delete ( & Action { } ) ; err != nil {
return err
}
2022-06-13 11:37:59 +02:00
}
_ , err := db . GetEngine ( ctx ) . Table ( "action" ) . Where ( "repo_id = ?" , repoID ) .
In ( "op_type" , ActionCreateIssue , ActionCreatePullRequest ) .
Where ( "content LIKE ?" , strconv . FormatInt ( issueID , 10 ) + "|%" ) .
Delete ( & Action { } )
return err
}
2022-08-25 04:31:57 +02:00
// CountActionCreatedUnixString count actions where created_unix is an empty string
2022-11-19 09:12:33 +01:00
func CountActionCreatedUnixString ( ctx context . Context ) ( int64 , error ) {
2023-03-07 11:51:06 +01:00
if setting . Database . Type . IsSQLite3 ( ) {
2022-11-19 09:12:33 +01:00
return db . GetEngine ( ctx ) . Where ( ` created_unix = "" ` ) . Count ( new ( Action ) )
2022-08-25 04:31:57 +02:00
}
return 0 , nil
}
// FixActionCreatedUnixString set created_unix to zero if it is an empty string
2022-11-19 09:12:33 +01:00
func FixActionCreatedUnixString ( ctx context . Context ) ( int64 , error ) {
2023-03-07 11:51:06 +01:00
if setting . Database . Type . IsSQLite3 ( ) {
2022-11-19 09:12:33 +01:00
res , err := db . GetEngine ( ctx ) . Exec ( ` UPDATE action SET created_unix = 0 WHERE created_unix = "" ` )
2022-08-25 04:31:57 +02:00
if err != nil {
return 0 , err
}
return res . RowsAffected ( )
}
return 0 , nil
}