2014-03-13 09:16:14 +04:00
// Copyright 2014 The Gogs Authors. All rights reserved.
2019-05-01 19:21:05 +03:00
// Copyright 2019 The Gitea Authors. All rights reserved.
2014-03-13 09:16:14 +04:00
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
import (
2014-05-06 19:50:31 +04:00
"fmt"
2014-07-26 08:24:27 +04:00
"path"
2017-06-25 21:20:29 +03:00
"strconv"
2014-04-14 06:20:28 +04:00
"strings"
2014-03-13 09:16:14 +04:00
"time"
2014-03-22 14:20:00 +04:00
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/modules/base"
2020-12-14 20:08:37 +03:00
"code.gitea.io/gitea/modules/git"
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
2019-08-15 17:46:21 +03:00
"code.gitea.io/gitea/modules/timeutil"
2017-12-03 05:20:12 +03:00
2019-06-23 18:22:43 +03:00
"xorm.io/builder"
2014-03-13 09:16:14 +04:00
)
2016-11-22 13:43:30 +03:00
// ActionType represents the type of an action.
2014-07-26 08:24:27 +04:00
type ActionType int
2016-11-22 13:43:30 +03:00
// Possible action types.
2014-03-13 09:16:14 +04:00
const (
2021-06-23 07:14:22 +03: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
2014-03-13 09:16:14 +04:00
)
2016-11-22 13:43:30 +03: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 09:16:14 +04:00
type Action struct {
2017-05-26 04:38:18 +03:00
ID int64 ` xorm:"pk autoincr" `
UserID int64 ` xorm:"INDEX" ` // Receiver user id.
OpType ActionType
ActUserID int64 ` xorm:"INDEX" ` // Action user id.
ActUser * User ` xorm:"-" `
RepoID int64 ` xorm:"INDEX" `
Repo * Repository ` xorm:"-" `
2017-06-25 21:20:29 +03:00
CommentID int64 ` xorm:"INDEX" `
Comment * Comment ` xorm:"-" `
IsDeleted bool ` xorm:"INDEX NOT NULL DEFAULT false" `
2017-05-26 04:38:18 +03:00
RefName string
2019-08-15 17:46:21 +03:00
IsPrivate bool ` xorm:"INDEX NOT NULL DEFAULT false" `
Content string ` xorm:"TEXT" `
CreatedUnix timeutil . TimeStamp ` xorm:"INDEX created" `
2015-08-19 19:12:43 +03:00
}
2016-11-22 13:43:30 +03:00
// GetOpType gets the ActionType of this action.
2017-09-20 04:22:42 +03:00
func ( a * Action ) GetOpType ( ) ActionType {
return a . OpType
2014-03-15 08:50:51 +04:00
}
2020-12-09 08:11:15 +03:00
// LoadActUser loads a.ActUser
func ( a * Action ) LoadActUser ( ) {
2017-05-26 04:38:18 +03:00
if a . ActUser != nil {
return
}
var err error
a . ActUser , err = GetUserByID ( a . ActUserID )
if err == nil {
return
} else if IsErrUserNotExist ( err ) {
a . ActUser = NewGhostUser ( )
} else {
2019-04-02 10:48:31 +03:00
log . Error ( "GetUserByID(%d): %v" , a . ActUserID , err )
2017-05-26 04:38:18 +03:00
}
}
func ( a * Action ) loadRepo ( ) {
2017-06-14 03:37:50 +03:00
if a . Repo != nil {
2017-05-26 04:38:18 +03:00
return
}
var err error
a . Repo , err = GetRepositoryByID ( a . RepoID )
if err != nil {
2019-04-02 10:48:31 +03:00
log . Error ( "GetRepositoryByID(%d): %v" , a . RepoID , err )
2017-05-26 04:38:18 +03:00
}
}
2018-07-05 20:48:18 +03:00
// GetActFullName gets the action's user full name.
func ( a * Action ) GetActFullName ( ) string {
2020-12-09 08:11:15 +03:00
a . LoadActUser ( )
2018-07-05 20:48:18 +03:00
return a . ActUser . FullName
}
2016-11-22 13:43:30 +03:00
// GetActUserName gets the action's user name.
2016-01-11 15:41:43 +03:00
func ( a * Action ) GetActUserName ( ) string {
2020-12-09 08:11:15 +03:00
a . LoadActUser ( )
2017-05-26 04:38:18 +03:00
return a . ActUser . Name
2014-03-15 08:50:51 +04:00
}
2016-11-22 13:43:30 +03:00
// ShortActUserName gets the action's user name trimmed to max 20
// chars.
2016-01-11 15:41:43 +03:00
func ( a * Action ) ShortActUserName ( ) string {
2017-05-26 04:38:18 +03:00
return base . EllipsisString ( a . GetActUserName ( ) , 20 )
2016-01-11 15:41:43 +03:00
}
2020-02-27 01:08:24 +03: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 11:41:35 +03:00
func ( a * Action ) GetDisplayName ( ) string {
if setting . UI . DefaultShowFullName {
2020-02-27 01:08:24 +03:00
trimmedFullName := strings . TrimSpace ( a . GetActFullName ( ) )
if len ( trimmedFullName ) > 0 {
return trimmedFullName
}
2019-05-08 11:41:35 +03: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 13:43:30 +03:00
// GetRepoUserName returns the name of the action repository owner.
2016-01-11 15:41:43 +03:00
func ( a * Action ) GetRepoUserName ( ) string {
2017-05-26 04:38:18 +03:00
a . loadRepo ( )
2020-01-12 12:36:21 +03:00
return a . Repo . OwnerName
2014-05-09 10:42:50 +04:00
}
2016-11-22 13:43:30 +03:00
// ShortRepoUserName returns the name of the action repository owner
// trimmed to max 20 chars.
2016-01-11 15:41:43 +03:00
func ( a * Action ) ShortRepoUserName ( ) string {
2017-05-26 04:38:18 +03:00
return base . EllipsisString ( a . GetRepoUserName ( ) , 20 )
2016-01-11 15:41:43 +03:00
}
2016-11-22 13:43:30 +03:00
// GetRepoName returns the name of the action repository.
2016-01-11 15:41:43 +03:00
func ( a * Action ) GetRepoName ( ) string {
2017-05-26 04:38:18 +03:00
a . loadRepo ( )
return a . Repo . Name
2014-03-13 09:16:14 +04:00
}
2016-11-22 13:43:30 +03:00
// ShortRepoName returns the name of the action repository
// trimmed to max 33 chars.
2016-01-11 15:41:43 +03:00
func ( a * Action ) ShortRepoName ( ) string {
2017-05-26 04:38:18 +03:00
return base . EllipsisString ( a . GetRepoName ( ) , 33 )
2016-01-11 15:41:43 +03:00
}
2016-11-22 13:43:30 +03:00
// GetRepoPath returns the virtual path to the action repository.
2016-01-11 15:41:43 +03:00
func ( a * Action ) GetRepoPath ( ) string {
2017-05-26 04:38:18 +03:00
return path . Join ( a . GetRepoUserName ( ) , a . GetRepoName ( ) )
2016-01-15 13:00:39 +03:00
}
2016-11-22 13:43:30 +03:00
// ShortRepoPath returns the virtual path to the action repository
2017-01-05 03:50:34 +03:00
// trimmed to max 20 + 1 + 33 chars.
2016-01-15 13:00:39 +03:00
func ( a * Action ) ShortRepoPath ( ) string {
2016-01-11 15:41:43 +03:00
return path . Join ( a . ShortRepoUserName ( ) , a . ShortRepoName ( ) )
2015-03-12 23:01:23 +03:00
}
2016-11-22 13:43:30 +03:00
// GetRepoLink returns relative link to action repository.
2016-01-11 15:41:43 +03:00
func ( a * Action ) GetRepoLink ( ) string {
2016-11-27 13:14:25 +03:00
if len ( setting . AppSubURL ) > 0 {
return path . Join ( setting . AppSubURL , a . GetRepoPath ( ) )
2015-03-12 23:01:23 +03:00
}
return "/" + a . GetRepoPath ( )
2014-07-26 08:24:27 +04:00
}
2019-05-01 19:21:05 +03:00
// GetRepositoryFromMatch returns a *Repository from a username and repo strings
2021-03-14 21:52:12 +03:00
func GetRepositoryFromMatch ( ownerName , repoName string ) ( * Repository , error ) {
2019-05-01 19:21:05 +03:00
var err error
refRepo , err := GetRepositoryByOwnerAndName ( ownerName , repoName )
if err != nil {
if IsErrRepoNotExist ( err ) {
log . Warn ( "Repository referenced in commit but does not exist: %v" , err )
return nil , err
}
log . Error ( "GetRepositoryByOwnerAndName: %v" , err )
return nil , err
}
return refRepo , nil
}
2017-06-25 21:20:29 +03:00
// GetCommentLink returns link to action comment.
func ( a * Action ) GetCommentLink ( ) string {
2018-12-13 18:55:43 +03:00
return a . getCommentLink ( x )
}
func ( a * Action ) getCommentLink ( e Engine ) string {
2017-06-25 21:20:29 +03:00
if a == nil {
return "#"
}
if a . Comment == nil && a . CommentID != 0 {
2020-02-28 02:10:27 +03:00
a . Comment , _ = getCommentByID ( e , a . CommentID )
2017-06-25 21:20:29 +03:00
}
if a . Comment != nil {
return a . Comment . HTMLURL ( )
}
if len ( a . GetIssueInfos ( ) ) == 0 {
return "#"
}
2021-03-14 21:52:12 +03:00
// Return link to issue
2017-06-25 21:20:29 +03:00
issueIDString := a . GetIssueInfos ( ) [ 0 ]
issueID , err := strconv . ParseInt ( issueIDString , 10 , 64 )
if err != nil {
return "#"
}
2018-12-13 18:55:43 +03:00
issue , err := getIssueByID ( e , issueID )
2017-06-25 21:20:29 +03:00
if err != nil {
return "#"
}
2018-12-13 18:55:43 +03:00
if err = issue . loadRepo ( e ) ; err != nil {
return "#"
}
2017-06-25 21:20:29 +03:00
return issue . HTMLURL ( )
}
2016-11-22 13:43:30 +03:00
// GetBranch returns the action's repository branch.
2016-01-11 15:41:43 +03:00
func ( a * Action ) GetBranch ( ) string {
2020-12-14 20:08:37 +03:00
return strings . TrimPrefix ( a . RefName , git . BranchPrefix )
2014-03-16 19:30:35 +04:00
}
2020-12-20 02:46:28 +03:00
// GetTag returns the action's repository tag.
func ( a * Action ) GetTag ( ) string {
return strings . TrimPrefix ( a . RefName , git . TagPrefix )
}
2016-11-22 13:43:30 +03:00
// GetContent returns the action's content.
2016-01-11 15:41:43 +03:00
func ( a * Action ) GetContent ( ) string {
2014-03-23 14:27:01 +04:00
return a . Content
2014-03-23 14:00:09 +04:00
}
2016-11-22 13:43:30 +03:00
// GetCreate returns the action creation time.
2016-01-11 15:41:43 +03:00
func ( a * Action ) GetCreate ( ) time . Time {
2017-12-11 07:37:04 +03:00
return a . CreatedUnix . AsTime ( )
2014-07-26 08:24:27 +04:00
}
2016-11-22 13:43:30 +03:00
// GetIssueInfos returns a list of issues associated with
// the action.
2016-01-11 15:41:43 +03:00
func ( a * Action ) GetIssueInfos ( ) [ ] string {
2021-02-11 20:32:25 +03:00
return strings . SplitN ( a . Content , "|" , 3 )
2014-07-26 08:24:27 +04:00
}
2016-11-22 13:43:30 +03:00
// GetIssueTitle returns the title of first issue associated
// with the action.
2016-01-11 15:41:43 +03:00
func ( a * Action ) GetIssueTitle ( ) string {
2020-12-25 12:59:32 +03:00
index , _ := strconv . ParseInt ( a . GetIssueInfos ( ) [ 0 ] , 10 , 64 )
2015-11-13 18:05:50 +03:00
issue , err := GetIssueByIndex ( a . RepoID , index )
2015-11-13 00:16:51 +03:00
if err != nil {
2019-04-02 10:48:31 +03:00
log . Error ( "GetIssueByIndex: %v" , err )
2015-11-13 20:11:45 +03:00
return "500 when get issue"
2015-11-13 00:16:51 +03:00
}
2016-08-14 13:32:24 +03:00
return issue . Title
2015-11-12 23:09:48 +03:00
}
2016-11-22 13:43:30 +03:00
// GetIssueContent returns the content of first issue associated with
// this action.
2016-01-11 15:41:43 +03:00
func ( a * Action ) GetIssueContent ( ) string {
2020-12-25 12:59:32 +03:00
index , _ := strconv . ParseInt ( a . GetIssueInfos ( ) [ 0 ] , 10 , 64 )
2015-11-13 20:11:45 +03:00
issue , err := GetIssueByIndex ( a . RepoID , index )
if err != nil {
2019-04-02 10:48:31 +03:00
log . Error ( "GetIssueByIndex: %v" , err )
2015-11-13 20:11:45 +03:00
return "500 when get issue"
}
return issue . Content
}
2017-06-02 03:42:25 +03:00
// GetFeedsOptions options for retrieving feeds
type GetFeedsOptions struct {
2021-02-21 01:08:58 +03:00
RequestedUser * User // the user we want activity for
RequestedTeam * Team // the team we want activity for
Actor * 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 03:42:25 +03:00
}
// GetFeeds returns actions according to the provided options
func GetFeeds ( opts GetFeedsOptions ) ( [ ] * Action , error ) {
2020-12-22 05:53:37 +03:00
if ! activityReadable ( opts . RequestedUser , opts . Actor ) {
return make ( [ ] * Action , 0 ) , nil
}
2017-08-23 04:30:54 +03:00
2020-12-22 05:53:37 +03:00
cond , err := activityQueryCondition ( opts )
if err != nil {
return nil , err
}
2020-01-13 20:33:46 +03:00
2020-12-22 05:53:37 +03:00
actions := make ( [ ] * Action , 0 , setting . UI . FeedPagingNum )
if err := x . Limit ( setting . UI . FeedPagingNum ) . Desc ( "id" ) . Where ( cond ) . Find ( & actions ) ; err != nil {
return nil , fmt . Errorf ( "Find: %v" , err )
2020-01-13 20:33:46 +03:00
}
2020-12-22 05:53:37 +03:00
if err := ActionList ( actions ) . LoadAttributes ( ) ; err != nil {
return nil , fmt . Errorf ( "LoadAttributes: %v" , err )
}
return actions , nil
}
2021-03-14 21:52:12 +03:00
func activityReadable ( user , doer * User ) bool {
2020-12-22 05:53:37 +03:00
var doerID int64
if doer != nil {
doerID = doer . ID
}
if doer == nil || ! doer . IsAdmin {
if user . KeepActivityPrivate && doerID != user . ID {
return false
2016-02-06 10:52:21 +03:00
}
2020-12-22 05:53:37 +03:00
}
return true
}
2017-08-23 04:30:54 +03:00
2020-12-22 05:53:37 +03:00
func activityQueryCondition ( opts GetFeedsOptions ) ( builder . Cond , error ) {
cond := builder . NewCond ( )
var repoIDs [ ] int64
var actorID int64
if opts . Actor != nil {
actorID = opts . Actor . ID
2017-08-23 04:30:54 +03:00
}
2020-12-22 05:53:37 +03:00
// check readable repositories by doer/actor
2020-06-05 23:01:53 +03:00
if opts . Actor == nil || ! opts . Actor . IsAdmin {
2020-12-22 05:53:37 +03:00
if opts . RequestedUser . IsOrganization ( ) {
env , err := opts . RequestedUser . AccessibleReposEnv ( actorID )
if err != nil {
return nil , fmt . Errorf ( "AccessibleReposEnv: %v" , err )
}
if repoIDs , err = env . RepoIDs ( 1 , opts . RequestedUser . NumRepos ) ; err != nil {
return nil , fmt . Errorf ( "GetUserRepositories: %v" , err )
}
cond = cond . And ( builder . In ( "repo_id" , repoIDs ) )
} else {
cond = cond . And ( builder . In ( "repo_id" , AccessibleRepoIDsQuery ( opts . Actor ) ) )
2020-06-05 23:01:53 +03:00
}
}
2020-12-27 22:58:03 +03:00
if opts . RequestedTeam != nil {
env := opts . RequestedUser . AccessibleTeamReposEnv ( opts . RequestedTeam )
teamRepoIDs , err := env . RepoIDs ( 1 , opts . RequestedUser . NumRepos )
if err != nil {
return nil , fmt . Errorf ( "GetTeamRepositories: %v" , err )
}
cond = cond . And ( builder . In ( "repo_id" , teamRepoIDs ) )
}
2017-08-28 05:26:04 +03:00
cond = cond . And ( builder . Eq { "user_id" : opts . RequestedUser . ID } )
2016-02-06 10:52:21 +03:00
2017-06-02 03:42:25 +03:00
if opts . OnlyPerformedBy {
2017-08-23 04:30:54 +03:00
cond = cond . And ( builder . Eq { "act_user_id" : opts . RequestedUser . ID } )
2017-06-02 03:42:25 +03:00
}
if ! opts . IncludePrivate {
2017-08-23 04:30:54 +03:00
cond = cond . And ( builder . Eq { "is_private" : false } )
2017-06-02 03:42:25 +03:00
}
2017-06-25 21:20:29 +03:00
if ! opts . IncludeDeleted {
2017-08-23 04:30:54 +03:00
cond = cond . And ( builder . Eq { "is_deleted" : false } )
2017-06-25 21:20:29 +03:00
}
2021-02-21 01:08:58 +03:00
if opts . Date != "" {
2021-04-01 13:52:17 +03:00
dateLow , err := time . ParseInLocation ( "2006-01-02" , opts . Date , setting . DefaultUILocation )
2021-02-21 01:08:58 +03:00
if err != nil {
log . Warn ( "Unable to parse %s, filter not applied: %v" , opts . Date , err )
} else {
dateHigh := dateLow . Add ( 86399000000000 ) // 23h59m59s
cond = cond . And ( builder . Gte { "created_unix" : dateLow . Unix ( ) } )
cond = cond . And ( builder . Lte { "created_unix" : dateHigh . Unix ( ) } )
}
}
2020-12-22 05:53:37 +03:00
return cond , nil
2014-03-13 09:16:14 +04:00
}
2021-05-01 15:17:02 +03:00
// DeleteOldActions deletes all old actions from database.
func DeleteOldActions ( olderThan time . Duration ) ( err error ) {
if olderThan <= 0 {
return nil
}
_ , err = x . Where ( "created_unix < ?" , time . Now ( ) . Add ( - olderThan ) . Unix ( ) ) . Delete ( & Action { } )
return
}