* Fix activity feed Preserve actions after user/repo name change * Add missing comment * Fix migration, and remove fields completely * Testsmaster
@@ -6,7 +6,7 @@ DB_TYPE = mysql | |||||
HOST = 127.0.0.1:3306 | HOST = 127.0.0.1:3306 | ||||
NAME = testgitea | NAME = testgitea | ||||
USER = root | USER = root | ||||
PASSWD = | |||||
PASSWD = | |||||
SSL_MODE = disable | SSL_MODE = disable | ||||
PATH = data/gitea.db | PATH = data/gitea.db | ||||
@@ -26,14 +26,14 @@ OFFLINE_MODE = false | |||||
ENABLED = false | ENABLED = false | ||||
[service] | [service] | ||||
REGISTER_EMAIL_CONFIRM = false | |||||
ENABLE_NOTIFY_MAIL = false | |||||
DISABLE_REGISTRATION = false | |||||
ENABLE_CAPTCHA = false | |||||
REQUIRE_SIGNIN_VIEW = false | |||||
DEFAULT_KEEP_EMAIL_PRIVATE = false | |||||
REGISTER_EMAIL_CONFIRM = false | |||||
ENABLE_NOTIFY_MAIL = false | |||||
DISABLE_REGISTRATION = false | |||||
ENABLE_CAPTCHA = false | |||||
REQUIRE_SIGNIN_VIEW = false | |||||
DEFAULT_KEEP_EMAIL_PRIVATE = false | |||||
DEFAULT_ALLOW_CREATE_ORGANIZATION = true | DEFAULT_ALLOW_CREATE_ORGANIZATION = true | ||||
NO_REPLY_ADDRESS = noreply.example.org | |||||
NO_REPLY_ADDRESS = noreply.example.org | |||||
[picture] | [picture] | ||||
DISABLE_GRAVATAR = false | DISABLE_GRAVATAR = false | ||||
@@ -53,5 +53,7 @@ LEVEL = Warn | |||||
LEVEL = Info | LEVEL = Info | ||||
[security] | [security] | ||||
INSTALL_LOCK = true | |||||
SECRET_KEY = 9pCviYTWSb | |||||
INSTALL_LOCK = true | |||||
SECRET_KEY = 9pCviYTWSb | |||||
INTERNAL_TOKEN = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE0OTU1NTE2MTh9.hhSVGOANkaKk3vfCd2jDOIww4pUk0xtg9JRde5UogyQ | |||||
@@ -70,20 +70,18 @@ func init() { | |||||
// repository. It implemented interface base.Actioner so that can be | // repository. It implemented interface base.Actioner so that can be | ||||
// used in template render. | // used in template render. | ||||
type Action struct { | type Action struct { | ||||
ID int64 `xorm:"pk autoincr"` | |||||
UserID int64 `xorm:"INDEX"` // Receiver user id. | |||||
OpType ActionType | |||||
ActUserID int64 `xorm:"INDEX"` // Action user id. | |||||
ActUserName string // Action user name. | |||||
ActAvatar string `xorm:"-"` | |||||
RepoID int64 `xorm:"INDEX"` | |||||
RepoUserName string | |||||
RepoName string | |||||
RefName string | |||||
IsPrivate bool `xorm:"INDEX NOT NULL DEFAULT false"` | |||||
Content string `xorm:"TEXT"` | |||||
Created time.Time `xorm:"-"` | |||||
CreatedUnix int64 `xorm:"INDEX"` | |||||
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:"-"` | |||||
RefName string | |||||
IsPrivate bool `xorm:"INDEX NOT NULL DEFAULT false"` | |||||
Content string `xorm:"TEXT"` | |||||
Created time.Time `xorm:"-"` | |||||
CreatedUnix int64 `xorm:"INDEX"` | |||||
} | } | ||||
// BeforeInsert will be invoked by XORM before inserting a record | // BeforeInsert will be invoked by XORM before inserting a record | ||||
@@ -106,42 +104,71 @@ func (a *Action) GetOpType() int { | |||||
return int(a.OpType) | return int(a.OpType) | ||||
} | } | ||||
func (a *Action) loadActUser() { | |||||
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 { | |||||
log.Error(4, "GetUserByID(%d): %v", a.ActUserID, err) | |||||
} | |||||
} | |||||
func (a *Action) loadRepo() { | |||||
if a.ActUser != nil { | |||||
return | |||||
} | |||||
var err error | |||||
a.Repo, err = GetRepositoryByID(a.RepoID) | |||||
if err != nil { | |||||
log.Error(4, "GetRepositoryByID(%d): %v", a.RepoID, err) | |||||
} | |||||
} | |||||
// GetActUserName gets the action's user name. | // GetActUserName gets the action's user name. | ||||
func (a *Action) GetActUserName() string { | func (a *Action) GetActUserName() string { | ||||
return a.ActUserName | |||||
a.loadActUser() | |||||
return a.ActUser.Name | |||||
} | } | ||||
// ShortActUserName gets the action's user name trimmed to max 20 | // ShortActUserName gets the action's user name trimmed to max 20 | ||||
// chars. | // chars. | ||||
func (a *Action) ShortActUserName() string { | func (a *Action) ShortActUserName() string { | ||||
return base.EllipsisString(a.ActUserName, 20) | |||||
return base.EllipsisString(a.GetActUserName(), 20) | |||||
} | } | ||||
// GetRepoUserName returns the name of the action repository owner. | // GetRepoUserName returns the name of the action repository owner. | ||||
func (a *Action) GetRepoUserName() string { | func (a *Action) GetRepoUserName() string { | ||||
return a.RepoUserName | |||||
a.loadRepo() | |||||
return a.Repo.MustOwner().Name | |||||
} | } | ||||
// ShortRepoUserName returns the name of the action repository owner | // ShortRepoUserName returns the name of the action repository owner | ||||
// trimmed to max 20 chars. | // trimmed to max 20 chars. | ||||
func (a *Action) ShortRepoUserName() string { | func (a *Action) ShortRepoUserName() string { | ||||
return base.EllipsisString(a.RepoUserName, 20) | |||||
return base.EllipsisString(a.GetRepoUserName(), 20) | |||||
} | } | ||||
// GetRepoName returns the name of the action repository. | // GetRepoName returns the name of the action repository. | ||||
func (a *Action) GetRepoName() string { | func (a *Action) GetRepoName() string { | ||||
return a.RepoName | |||||
a.loadRepo() | |||||
return a.Repo.Name | |||||
} | } | ||||
// ShortRepoName returns the name of the action repository | // ShortRepoName returns the name of the action repository | ||||
// trimmed to max 33 chars. | // trimmed to max 33 chars. | ||||
func (a *Action) ShortRepoName() string { | func (a *Action) ShortRepoName() string { | ||||
return base.EllipsisString(a.RepoName, 33) | |||||
return base.EllipsisString(a.GetRepoName(), 33) | |||||
} | } | ||||
// GetRepoPath returns the virtual path to the action repository. | // GetRepoPath returns the virtual path to the action repository. | ||||
func (a *Action) GetRepoPath() string { | func (a *Action) GetRepoPath() string { | ||||
return path.Join(a.RepoUserName, a.RepoName) | |||||
return path.Join(a.GetRepoUserName(), a.GetRepoName()) | |||||
} | } | ||||
// ShortRepoPath returns the virtual path to the action repository | // ShortRepoPath returns the virtual path to the action repository | ||||
@@ -205,13 +232,12 @@ func (a *Action) GetIssueContent() string { | |||||
func newRepoAction(e Engine, u *User, repo *Repository) (err error) { | func newRepoAction(e Engine, u *User, repo *Repository) (err error) { | ||||
if err = notifyWatchers(e, &Action{ | if err = notifyWatchers(e, &Action{ | ||||
ActUserID: u.ID, | |||||
ActUserName: u.Name, | |||||
OpType: ActionCreateRepo, | |||||
RepoID: repo.ID, | |||||
RepoUserName: repo.Owner.Name, | |||||
RepoName: repo.Name, | |||||
IsPrivate: repo.IsPrivate, | |||||
ActUserID: u.ID, | |||||
ActUser: u, | |||||
OpType: ActionCreateRepo, | |||||
RepoID: repo.ID, | |||||
Repo: repo, | |||||
IsPrivate: repo.IsPrivate, | |||||
}); err != nil { | }); err != nil { | ||||
return fmt.Errorf("notify watchers '%d/%d': %v", u.ID, repo.ID, err) | return fmt.Errorf("notify watchers '%d/%d': %v", u.ID, repo.ID, err) | ||||
} | } | ||||
@@ -227,14 +253,13 @@ func NewRepoAction(u *User, repo *Repository) (err error) { | |||||
func renameRepoAction(e Engine, actUser *User, oldRepoName string, repo *Repository) (err error) { | func renameRepoAction(e Engine, actUser *User, oldRepoName string, repo *Repository) (err error) { | ||||
if err = notifyWatchers(e, &Action{ | if err = notifyWatchers(e, &Action{ | ||||
ActUserID: actUser.ID, | |||||
ActUserName: actUser.Name, | |||||
OpType: ActionRenameRepo, | |||||
RepoID: repo.ID, | |||||
RepoUserName: repo.Owner.Name, | |||||
RepoName: repo.Name, | |||||
IsPrivate: repo.IsPrivate, | |||||
Content: oldRepoName, | |||||
ActUserID: actUser.ID, | |||||
ActUser: actUser, | |||||
OpType: ActionRenameRepo, | |||||
RepoID: repo.ID, | |||||
Repo: repo, | |||||
IsPrivate: repo.IsPrivate, | |||||
Content: oldRepoName, | |||||
}); err != nil { | }); err != nil { | ||||
return fmt.Errorf("notify watchers: %v", err) | return fmt.Errorf("notify watchers: %v", err) | ||||
} | } | ||||
@@ -521,15 +546,14 @@ func CommitRepoAction(opts CommitRepoActionOptions) error { | |||||
refName := git.RefEndName(opts.RefFullName) | refName := git.RefEndName(opts.RefFullName) | ||||
if err = NotifyWatchers(&Action{ | if err = NotifyWatchers(&Action{ | ||||
ActUserID: pusher.ID, | |||||
ActUserName: pusher.Name, | |||||
OpType: opType, | |||||
Content: string(data), | |||||
RepoID: repo.ID, | |||||
RepoUserName: repo.MustOwner().Name, | |||||
RepoName: repo.Name, | |||||
RefName: refName, | |||||
IsPrivate: repo.IsPrivate, | |||||
ActUserID: pusher.ID, | |||||
ActUser: pusher, | |||||
OpType: opType, | |||||
Content: string(data), | |||||
RepoID: repo.ID, | |||||
Repo: repo, | |||||
RefName: refName, | |||||
IsPrivate: repo.IsPrivate, | |||||
}); err != nil { | }); err != nil { | ||||
return fmt.Errorf("NotifyWatchers: %v", err) | return fmt.Errorf("NotifyWatchers: %v", err) | ||||
} | } | ||||
@@ -598,14 +622,13 @@ func CommitRepoAction(opts CommitRepoActionOptions) error { | |||||
func transferRepoAction(e Engine, doer, oldOwner *User, repo *Repository) (err error) { | func transferRepoAction(e Engine, doer, oldOwner *User, repo *Repository) (err error) { | ||||
if err = notifyWatchers(e, &Action{ | if err = notifyWatchers(e, &Action{ | ||||
ActUserID: doer.ID, | |||||
ActUserName: doer.Name, | |||||
OpType: ActionTransferRepo, | |||||
RepoID: repo.ID, | |||||
RepoUserName: repo.Owner.Name, | |||||
RepoName: repo.Name, | |||||
IsPrivate: repo.IsPrivate, | |||||
Content: path.Join(oldOwner.Name, repo.Name), | |||||
ActUserID: doer.ID, | |||||
ActUser: doer, | |||||
OpType: ActionTransferRepo, | |||||
RepoID: repo.ID, | |||||
Repo: repo, | |||||
IsPrivate: repo.IsPrivate, | |||||
Content: path.Join(oldOwner.Name, repo.Name), | |||||
}); err != nil { | }); err != nil { | ||||
return fmt.Errorf("notifyWatchers: %v", err) | return fmt.Errorf("notifyWatchers: %v", err) | ||||
} | } | ||||
@@ -628,14 +651,13 @@ func TransferRepoAction(doer, oldOwner *User, repo *Repository) error { | |||||
func mergePullRequestAction(e Engine, doer *User, repo *Repository, issue *Issue) error { | func mergePullRequestAction(e Engine, doer *User, repo *Repository, issue *Issue) error { | ||||
return notifyWatchers(e, &Action{ | return notifyWatchers(e, &Action{ | ||||
ActUserID: doer.ID, | |||||
ActUserName: doer.Name, | |||||
OpType: ActionMergePullRequest, | |||||
Content: fmt.Sprintf("%d|%s", issue.Index, issue.Title), | |||||
RepoID: repo.ID, | |||||
RepoUserName: repo.Owner.Name, | |||||
RepoName: repo.Name, | |||||
IsPrivate: repo.IsPrivate, | |||||
ActUserID: doer.ID, | |||||
ActUser: doer, | |||||
OpType: ActionMergePullRequest, | |||||
Content: fmt.Sprintf("%d|%s", issue.Index, issue.Title), | |||||
RepoID: repo.ID, | |||||
Repo: repo, | |||||
IsPrivate: repo.IsPrivate, | |||||
}) | }) | ||||
} | } | ||||
@@ -1,6 +1,7 @@ | |||||
package models | package models | ||||
import ( | import ( | ||||
"path" | |||||
"strings" | "strings" | ||||
"testing" | "testing" | ||||
@@ -10,22 +11,21 @@ import ( | |||||
) | ) | ||||
func TestAction_GetRepoPath(t *testing.T) { | func TestAction_GetRepoPath(t *testing.T) { | ||||
action := &Action{ | |||||
RepoUserName: "username", | |||||
RepoName: "reponame", | |||||
} | |||||
assert.Equal(t, "username/reponame", action.GetRepoPath()) | |||||
assert.NoError(t, PrepareTestDatabase()) | |||||
repo := AssertExistsAndLoadBean(t, &Repository{}).(*Repository) | |||||
owner := AssertExistsAndLoadBean(t, &User{ID: repo.OwnerID}).(*User) | |||||
action := &Action{RepoID: repo.ID} | |||||
assert.Equal(t, path.Join(owner.Name, repo.Name), action.GetRepoPath()) | |||||
} | } | ||||
func TestAction_GetRepoLink(t *testing.T) { | func TestAction_GetRepoLink(t *testing.T) { | ||||
action := &Action{ | |||||
RepoUserName: "username", | |||||
RepoName: "reponame", | |||||
} | |||||
assert.NoError(t, PrepareTestDatabase()) | |||||
repo := AssertExistsAndLoadBean(t, &Repository{}).(*Repository) | |||||
owner := AssertExistsAndLoadBean(t, &User{ID: repo.OwnerID}).(*User) | |||||
action := &Action{RepoID: repo.ID} | |||||
setting.AppSubURL = "/suburl/" | setting.AppSubURL = "/suburl/" | ||||
assert.Equal(t, "/suburl/username/reponame", action.GetRepoLink()) | |||||
setting.AppSubURL = "" | |||||
assert.Equal(t, "/username/reponame", action.GetRepoLink()) | |||||
expected := path.Join(setting.AppSubURL, owner.Name, repo.Name) | |||||
assert.Equal(t, expected, action.GetRepoLink()) | |||||
} | } | ||||
func TestNewRepoAction(t *testing.T) { | func TestNewRepoAction(t *testing.T) { | ||||
@@ -36,13 +36,12 @@ func TestNewRepoAction(t *testing.T) { | |||||
repo.Owner = user | repo.Owner = user | ||||
actionBean := &Action{ | actionBean := &Action{ | ||||
OpType: ActionCreateRepo, | |||||
ActUserID: user.ID, | |||||
RepoID: repo.ID, | |||||
ActUserName: user.Name, | |||||
RepoName: repo.Name, | |||||
RepoUserName: repo.Owner.Name, | |||||
IsPrivate: repo.IsPrivate, | |||||
OpType: ActionCreateRepo, | |||||
ActUserID: user.ID, | |||||
RepoID: repo.ID, | |||||
ActUser: user, | |||||
Repo: repo, | |||||
IsPrivate: repo.IsPrivate, | |||||
} | } | ||||
AssertNotExistsBean(t, actionBean) | AssertNotExistsBean(t, actionBean) | ||||
@@ -64,14 +63,13 @@ func TestRenameRepoAction(t *testing.T) { | |||||
repo.LowerName = strings.ToLower(newRepoName) | repo.LowerName = strings.ToLower(newRepoName) | ||||
actionBean := &Action{ | actionBean := &Action{ | ||||
OpType: ActionRenameRepo, | |||||
ActUserID: user.ID, | |||||
ActUserName: user.Name, | |||||
RepoID: repo.ID, | |||||
RepoName: repo.Name, | |||||
RepoUserName: repo.Owner.Name, | |||||
IsPrivate: repo.IsPrivate, | |||||
Content: oldRepoName, | |||||
OpType: ActionRenameRepo, | |||||
ActUserID: user.ID, | |||||
ActUser: user, | |||||
RepoID: repo.ID, | |||||
Repo: repo, | |||||
IsPrivate: repo.IsPrivate, | |||||
Content: oldRepoName, | |||||
} | } | ||||
AssertNotExistsBean(t, actionBean) | AssertNotExistsBean(t, actionBean) | ||||
assert.NoError(t, RenameRepoAction(user, oldRepoName, repo)) | assert.NoError(t, RenameRepoAction(user, oldRepoName, repo)) | ||||
@@ -232,13 +230,13 @@ func TestCommitRepoAction(t *testing.T) { | |||||
pushCommits.Len = len(pushCommits.Commits) | pushCommits.Len = len(pushCommits.Commits) | ||||
actionBean := &Action{ | actionBean := &Action{ | ||||
OpType: ActionCommitRepo, | |||||
ActUserID: user.ID, | |||||
ActUserName: user.Name, | |||||
RepoID: repo.ID, | |||||
RepoName: repo.Name, | |||||
RefName: "refName", | |||||
IsPrivate: repo.IsPrivate, | |||||
OpType: ActionCommitRepo, | |||||
ActUserID: user.ID, | |||||
ActUser: user, | |||||
RepoID: repo.ID, | |||||
Repo: repo, | |||||
RefName: "refName", | |||||
IsPrivate: repo.IsPrivate, | |||||
} | } | ||||
AssertNotExistsBean(t, actionBean) | AssertNotExistsBean(t, actionBean) | ||||
assert.NoError(t, CommitRepoAction(CommitRepoActionOptions{ | assert.NoError(t, CommitRepoAction(CommitRepoActionOptions{ | ||||
@@ -265,13 +263,12 @@ func TestTransferRepoAction(t *testing.T) { | |||||
repo.Owner = user4 | repo.Owner = user4 | ||||
actionBean := &Action{ | actionBean := &Action{ | ||||
OpType: ActionTransferRepo, | |||||
ActUserID: user2.ID, | |||||
ActUserName: user2.Name, | |||||
RepoID: repo.ID, | |||||
RepoName: repo.Name, | |||||
RepoUserName: repo.Owner.Name, | |||||
IsPrivate: repo.IsPrivate, | |||||
OpType: ActionTransferRepo, | |||||
ActUserID: user2.ID, | |||||
ActUser: user2, | |||||
RepoID: repo.ID, | |||||
Repo: repo, | |||||
IsPrivate: repo.IsPrivate, | |||||
} | } | ||||
AssertNotExistsBean(t, actionBean) | AssertNotExistsBean(t, actionBean) | ||||
assert.NoError(t, TransferRepoAction(user2, user2, repo)) | assert.NoError(t, TransferRepoAction(user2, user2, repo)) | ||||
@@ -290,13 +287,12 @@ func TestMergePullRequestAction(t *testing.T) { | |||||
issue := AssertExistsAndLoadBean(t, &Issue{ID: 3, RepoID: repo.ID}).(*Issue) | issue := AssertExistsAndLoadBean(t, &Issue{ID: 3, RepoID: repo.ID}).(*Issue) | ||||
actionBean := &Action{ | actionBean := &Action{ | ||||
OpType: ActionMergePullRequest, | |||||
ActUserID: user.ID, | |||||
ActUserName: user.Name, | |||||
RepoID: repo.ID, | |||||
RepoName: repo.Name, | |||||
RepoUserName: repo.Owner.Name, | |||||
IsPrivate: repo.IsPrivate, | |||||
OpType: ActionMergePullRequest, | |||||
ActUserID: user.ID, | |||||
ActUser: user, | |||||
RepoID: repo.ID, | |||||
Repo: repo, | |||||
IsPrivate: repo.IsPrivate, | |||||
} | } | ||||
AssertNotExistsBean(t, actionBean) | AssertNotExistsBean(t, actionBean) | ||||
assert.NoError(t, MergePullRequestAction(user, repo, issue)) | assert.NoError(t, MergePullRequestAction(user, repo, issue)) | ||||
@@ -162,11 +162,5 @@ func (team *Team) checkForConsistency(t *testing.T) { | |||||
func (action *Action) checkForConsistency(t *testing.T) { | func (action *Action) checkForConsistency(t *testing.T) { | ||||
repo := AssertExistsAndLoadBean(t, &Repository{ID: action.RepoID}).(*Repository) | repo := AssertExistsAndLoadBean(t, &Repository{ID: action.RepoID}).(*Repository) | ||||
owner := AssertExistsAndLoadBean(t, &User{ID: repo.OwnerID}).(*User) | |||||
actor := AssertExistsAndLoadBean(t, &User{ID: action.ActUserID}).(*User) | |||||
assert.Equal(t, repo.Name, action.RepoName, "action: %+v", action) | |||||
assert.Equal(t, repo.IsPrivate, action.IsPrivate, "action: %+v", action) | assert.Equal(t, repo.IsPrivate, action.IsPrivate, "action: %+v", action) | ||||
assert.Equal(t, owner.Name, action.RepoUserName, "action: %+v", action) | |||||
assert.Equal(t, actor.Name, action.ActUserName, "action: %+v", action) | |||||
} | } |
@@ -3,10 +3,7 @@ | |||||
user_id: 2 | user_id: 2 | ||||
op_type: 12 # close issue | op_type: 12 # close issue | ||||
act_user_id: 2 | act_user_id: 2 | ||||
act_user_name: user2 | |||||
repo_id: 2 | repo_id: 2 | ||||
repo_user_name: user2 | |||||
repo_name: repo2 | |||||
is_private: true | is_private: true | ||||
- | - | ||||
@@ -14,10 +11,7 @@ | |||||
user_id: 3 | user_id: 3 | ||||
op_type: 2 # rename repo | op_type: 2 # rename repo | ||||
act_user_id: 3 | act_user_id: 3 | ||||
act_user_name: user3 | |||||
repo_id: 3 | repo_id: 3 | ||||
repo_user_name: user3 | |||||
repo_name: repo3 | |||||
is_private: true | is_private: true | ||||
content: oldRepoName | content: oldRepoName | ||||
@@ -26,8 +20,5 @@ | |||||
user_id: 11 | user_id: 11 | ||||
op_type: 1 # create repo | op_type: 1 # create repo | ||||
act_user_id: 11 | act_user_id: 11 | ||||
act_user_name: user11 | |||||
repo_id: 9 | repo_id: 9 | ||||
repo_user_name: user11 | |||||
repo_name: repo9 | |||||
is_private: false | is_private: false |
@@ -918,14 +918,13 @@ func NewIssue(repo *Repository, issue *Issue, labelIDs []int64, uuids []string) | |||||
} | } | ||||
if err = NotifyWatchers(&Action{ | if err = NotifyWatchers(&Action{ | ||||
ActUserID: issue.Poster.ID, | |||||
ActUserName: issue.Poster.Name, | |||||
OpType: ActionCreateIssue, | |||||
Content: fmt.Sprintf("%d|%s", issue.Index, issue.Title), | |||||
RepoID: repo.ID, | |||||
RepoUserName: repo.Owner.Name, | |||||
RepoName: repo.Name, | |||||
IsPrivate: repo.IsPrivate, | |||||
ActUserID: issue.Poster.ID, | |||||
ActUser: issue.Poster, | |||||
OpType: ActionCreateIssue, | |||||
Content: fmt.Sprintf("%d|%s", issue.Index, issue.Title), | |||||
RepoID: repo.ID, | |||||
Repo: repo, | |||||
IsPrivate: repo.IsPrivate, | |||||
}); err != nil { | }); err != nil { | ||||
log.Error(4, "NotifyWatchers: %v", err) | log.Error(4, "NotifyWatchers: %v", err) | ||||
} | } | ||||
@@ -329,13 +329,12 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err | |||||
// Compose comment action, could be plain comment, close or reopen issue/pull request. | // Compose comment action, could be plain comment, close or reopen issue/pull request. | ||||
// This object will be used to notify watchers in the end of function. | // This object will be used to notify watchers in the end of function. | ||||
act := &Action{ | act := &Action{ | ||||
ActUserID: opts.Doer.ID, | |||||
ActUserName: opts.Doer.Name, | |||||
Content: fmt.Sprintf("%d|%s", opts.Issue.Index, strings.Split(opts.Content, "\n")[0]), | |||||
RepoID: opts.Repo.ID, | |||||
RepoUserName: opts.Repo.Owner.Name, | |||||
RepoName: opts.Repo.Name, | |||||
IsPrivate: opts.Repo.IsPrivate, | |||||
ActUserID: opts.Doer.ID, | |||||
ActUser: opts.Doer, | |||||
Content: fmt.Sprintf("%d|%s", opts.Issue.Index, strings.Split(opts.Content, "\n")[0]), | |||||
RepoID: opts.Repo.ID, | |||||
Repo: opts.Repo, | |||||
IsPrivate: opts.Repo.IsPrivate, | |||||
} | } | ||||
// Check comment type. | // Check comment type. | ||||
@@ -114,6 +114,8 @@ var migrations = []Migration{ | |||||
NewMigration("add field for login source synchronization", addLoginSourceSyncEnabledColumn), | NewMigration("add field for login source synchronization", addLoginSourceSyncEnabledColumn), | ||||
// v32 -> v33 | // v32 -> v33 | ||||
NewMigration("add units for team", addUnitsToRepoTeam), | NewMigration("add units for team", addUnitsToRepoTeam), | ||||
// v33 -> v34 | |||||
NewMigration("remove columns from action", removeActionColumns), | |||||
} | } | ||||
// Migrate database to current version | // Migrate database to current version | ||||
@@ -0,0 +1,44 @@ | |||||
// Copyright 2017 Gitea. All rights reserved. | |||||
// Use of this source code is governed by a MIT-style | |||||
// license that can be found in the LICENSE file. | |||||
package migrations | |||||
import ( | |||||
"fmt" | |||||
"code.gitea.io/gitea/modules/log" | |||||
"code.gitea.io/gitea/modules/setting" | |||||
"github.com/go-xorm/xorm" | |||||
) | |||||
// ActionV34 describes the removed fields | |||||
type ActionV34 struct { | |||||
ActUserName string `xorm:"-"` | |||||
RepoUserName string `xorm:"-"` | |||||
RepoName string `xorm:"-"` | |||||
} | |||||
// TableName will be invoked by XORM to customize the table name | |||||
func (*ActionV34) TableName() string { | |||||
return "action" | |||||
} | |||||
func removeActionColumns(x *xorm.Engine) error { | |||||
switch { | |||||
case setting.UseSQLite3: | |||||
log.Warn("Unable to drop columns in SQLite") | |||||
case setting.UseMySQL, setting.UsePostgreSQL, setting.UseMSSQL, setting.UseTiDB: | |||||
if _, err := x.Exec("ALTER TABLE action DROP COLUMN act_user_name"); err != nil { | |||||
return fmt.Errorf("DROP COLUMN act_user_name: %v", err) | |||||
} else if _, err = x.Exec("ALTER TABLE action DROP COLUMN repo_user_name"); err != nil { | |||||
return fmt.Errorf("DROP COLUMN repo_user_name: %v", err) | |||||
} else if _, err = x.Exec("ALTER TABLE action DROP COLUMN repo_name"); err != nil { | |||||
return fmt.Errorf("DROP COLUMN repo_name: %v", err) | |||||
} | |||||
default: | |||||
log.Fatal(4, "Unrecognized DB") | |||||
} | |||||
return nil | |||||
} |
@@ -635,14 +635,13 @@ func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []str | |||||
} | } | ||||
if err = NotifyWatchers(&Action{ | if err = NotifyWatchers(&Action{ | ||||
ActUserID: pull.Poster.ID, | |||||
ActUserName: pull.Poster.Name, | |||||
OpType: ActionCreatePullRequest, | |||||
Content: fmt.Sprintf("%d|%s", pull.Index, pull.Title), | |||||
RepoID: repo.ID, | |||||
RepoUserName: repo.Owner.Name, | |||||
RepoName: repo.Name, | |||||
IsPrivate: repo.IsPrivate, | |||||
ActUserID: pull.Poster.ID, | |||||
ActUser: pull.Poster, | |||||
OpType: ActionCreatePullRequest, | |||||
Content: fmt.Sprintf("%d|%s", pull.Index, pull.Title), | |||||
RepoID: repo.ID, | |||||
Repo: repo, | |||||
IsPrivate: repo.IsPrivate, | |||||
}); err != nil { | }); err != nil { | ||||
log.Error(4, "NotifyWatchers: %v", err) | log.Error(4, "NotifyWatchers: %v", err) | ||||
} else if err = pull.MailParticipants(); err != nil { | } else if err = pull.MailParticipants(); err != nil { | ||||
@@ -65,25 +65,50 @@ func retrieveFeeds(ctx *context.Context, ctxUser *models.User, userID, offset in | |||||
// Check access of private repositories. | // Check access of private repositories. | ||||
feeds := make([]*models.Action, 0, len(actions)) | feeds := make([]*models.Action, 0, len(actions)) | ||||
unameAvatars := map[string]string{ | |||||
ctxUser.Name: ctxUser.RelAvatarLink(), | |||||
} | |||||
userCache := map[int64]*models.User{ctxUser.ID: ctxUser} | |||||
repoCache := map[int64]*models.Repository{} | |||||
for _, act := range actions { | for _, act := range actions { | ||||
// Cache results to reduce queries. | // Cache results to reduce queries. | ||||
_, ok := unameAvatars[act.ActUserName] | |||||
u, ok := userCache[act.ActUserID] | |||||
if !ok { | if !ok { | ||||
u, err := models.GetUserByName(act.ActUserName) | |||||
u, err = models.GetUserByID(act.ActUserID) | |||||
if err != nil { | if err != nil { | ||||
if models.IsErrUserNotExist(err) { | if models.IsErrUserNotExist(err) { | ||||
continue | continue | ||||
} | } | ||||
ctx.Handle(500, "GetUserByName", err) | |||||
ctx.Handle(500, "GetUserByID", err) | |||||
return | |||||
} | |||||
userCache[act.ActUserID] = u | |||||
} | |||||
act.ActUser = u | |||||
repo, ok := repoCache[act.RepoID] | |||||
if !ok { | |||||
repo, err = models.GetRepositoryByID(act.RepoID) | |||||
if err != nil { | |||||
if models.IsErrRepoNotExist(err) { | |||||
continue | |||||
} | |||||
ctx.Handle(500, "GetRepositoryByID", err) | |||||
return | |||||
} | |||||
} | |||||
act.Repo = repo | |||||
repoOwner, ok := userCache[repo.OwnerID] | |||||
if !ok { | |||||
repoOwner, err = models.GetUserByID(repo.OwnerID) | |||||
if err != nil { | |||||
if models.IsErrUserNotExist(err) { | |||||
continue | |||||
} | |||||
ctx.Handle(500, "GetUserByID", err) | |||||
return | return | ||||
} | } | ||||
unameAvatars[act.ActUserName] = u.RelAvatarLink() | |||||
} | } | ||||
repo.Owner = repoOwner | |||||
act.ActAvatar = unameAvatars[act.ActUserName] | |||||
feeds = append(feeds, act) | feeds = append(feeds, act) | ||||
} | } | ||||
ctx.Data["Feeds"] = feeds | ctx.Data["Feeds"] = feeds | ||||