@@ -9,6 +9,7 @@ import ( | |||
"fmt" | |||
"path" | |||
"regexp" | |||
"strconv" | |||
"strings" | |||
"time" | |||
"unicode" | |||
@@ -77,6 +78,9 @@ type Action struct { | |||
ActUser *User `xorm:"-"` | |||
RepoID int64 `xorm:"INDEX"` | |||
Repo *Repository `xorm:"-"` | |||
CommentID int64 `xorm:"INDEX"` | |||
Comment *Comment `xorm:"-"` | |||
IsDeleted bool `xorm:"INDEX NOT NULL DEFAULT false"` | |||
RefName string | |||
IsPrivate bool `xorm:"INDEX NOT NULL DEFAULT false"` | |||
Content string `xorm:"TEXT"` | |||
@@ -191,6 +195,35 @@ func (a *Action) GetRepoLink() string { | |||
return "/" + a.GetRepoPath() | |||
} | |||
// GetCommentLink returns link to action comment. | |||
func (a *Action) GetCommentLink() string { | |||
if a == nil { | |||
return "#" | |||
} | |||
if a.Comment == nil && a.CommentID != 0 { | |||
a.Comment, _ = GetCommentByID(a.CommentID) | |||
} | |||
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 := GetIssueByID(issueID) | |||
if err != nil { | |||
return "#" | |||
} | |||
return issue.HTMLURL() | |||
} | |||
// GetBranch returns the action's repository branch. | |||
func (a *Action) GetBranch() string { | |||
return a.RefName | |||
@@ -678,6 +711,7 @@ type GetFeedsOptions struct { | |||
RequestingUserID int64 | |||
IncludePrivate bool // include private actions | |||
OnlyPerformedBy bool // only actions performed by requested user | |||
IncludeDeleted bool // include deleted actions | |||
} | |||
// GetFeeds returns actions according to the provided options | |||
@@ -706,5 +740,11 @@ func GetFeeds(opts GetFeedsOptions) ([]*Action, error) { | |||
if opts.RequestedUser.IsOrganization() { | |||
sess.In("repo_id", repoIDs) | |||
} | |||
if !opts.IncludeDeleted { | |||
sess.And("is_deleted = ?", false) | |||
} | |||
return actions, sess.Find(&actions) | |||
} |
@@ -310,6 +310,7 @@ func TestGetFeeds(t *testing.T) { | |||
RequestingUserID: user.ID, | |||
IncludePrivate: true, | |||
OnlyPerformedBy: false, | |||
IncludeDeleted: true, | |||
}) | |||
assert.NoError(t, err) | |||
assert.Len(t, actions, 1) | |||
@@ -337,6 +338,7 @@ func TestGetFeeds2(t *testing.T) { | |||
RequestingUserID: userID, | |||
IncludePrivate: true, | |||
OnlyPerformedBy: false, | |||
IncludeDeleted: true, | |||
}) | |||
assert.NoError(t, err) | |||
assert.Len(t, actions, 1) | |||
@@ -348,6 +350,7 @@ func TestGetFeeds2(t *testing.T) { | |||
RequestingUserID: userID, | |||
IncludePrivate: false, | |||
OnlyPerformedBy: false, | |||
IncludeDeleted: true, | |||
}) | |||
assert.NoError(t, err) | |||
assert.Len(t, actions, 0) |
@@ -334,6 +334,8 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err | |||
Content: fmt.Sprintf("%d|%s", opts.Issue.Index, strings.Split(opts.Content, "\n")[0]), | |||
RepoID: opts.Repo.ID, | |||
Repo: opts.Repo, | |||
Comment: comment, | |||
CommentID: comment.ID, | |||
IsPrivate: opts.Repo.IsPrivate, | |||
} | |||
@@ -666,6 +668,7 @@ func DeleteComment(comment *Comment) error { | |||
return err | |||
} | |||
} | |||
sess.Where("comment_id = ?", comment.ID).Cols("is_deleted").Update(&Action{IsDeleted: true}) | |||
return sess.Commit() | |||
} |
@@ -118,6 +118,8 @@ var migrations = []Migration{ | |||
NewMigration("remove columns from action", removeActionColumns), | |||
// v34 -> v35 | |||
NewMigration("give all units to owner teams", giveAllUnitsToOwnerTeams), | |||
// v35 -> v36 | |||
NewMigration("adds comment to an action", addCommentIDToAction), | |||
} | |||
// Migrate database to current version |
@@ -0,0 +1,25 @@ | |||
// Copyright 2017 The Gitea Authors. 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" | |||
"github.com/go-xorm/xorm" | |||
) | |||
func addCommentIDToAction(x *xorm.Engine) error { | |||
// Action see models/action.go | |||
type Action struct { | |||
CommentID int64 `xorm:"INDEX"` | |||
IsDeleted bool `xorm:"INDEX NOT NULL DEFAULT false"` | |||
} | |||
if err := x.Sync2(new(Action)); err != nil { | |||
return fmt.Errorf("Sync2: %v", err) | |||
} | |||
return nil | |||
} |
@@ -54,7 +54,7 @@ func getDashboardContextUser(ctx *context.Context) *models.User { | |||
} | |||
// retrieveFeeds loads feeds for the specified user | |||
func retrieveFeeds(ctx *context.Context, user *models.User, includePrivate, isProfile bool) { | |||
func retrieveFeeds(ctx *context.Context, user *models.User, includePrivate, isProfile bool, includeDeletedComments bool) { | |||
var requestingID int64 | |||
if ctx.User != nil { | |||
requestingID = ctx.User.ID | |||
@@ -64,6 +64,7 @@ func retrieveFeeds(ctx *context.Context, user *models.User, includePrivate, isPr | |||
RequestingUserID: requestingID, | |||
IncludePrivate: includePrivate, | |||
OnlyPerformedBy: isProfile, | |||
IncludeDeleted: includeDeletedComments, | |||
}) | |||
if err != nil { | |||
ctx.Handle(500, "GetFeeds", err) | |||
@@ -186,7 +187,7 @@ func Dashboard(ctx *context.Context) { | |||
ctx.Data["MirrorCount"] = len(mirrors) | |||
ctx.Data["Mirrors"] = mirrors | |||
retrieveFeeds(ctx, ctxUser, true, false) | |||
retrieveFeeds(ctx, ctxUser, true, false, false) | |||
if ctx.Written() { | |||
return | |||
} |
@@ -138,7 +138,7 @@ func Profile(ctx *context.Context) { | |||
ctx.Data["Keyword"] = keyword | |||
switch tab { | |||
case "activity": | |||
retrieveFeeds(ctx, ctxUser, showPrivate, true) | |||
retrieveFeeds(ctx, ctxUser, showPrivate, true, false) | |||
if ctx.Written() { | |||
return | |||
} |
@@ -63,7 +63,7 @@ | |||
{{else if eq .GetOpType 7}} | |||
<span class="text truncate issue title has-emoji">{{index .GetIssueInfos 1}}</span> | |||
{{else if eq .GetOpType 10}} | |||
<span class="text truncate issue title has-emoji">{{.GetIssueTitle}}</span> | |||
<a href="{{.GetCommentLink}}" class="text truncate issue title has-emoji">{{.GetIssueTitle}}</a> | |||
<p class="text light grey has-emoji">{{index .GetIssueInfos 1}}</p> | |||
{{else if eq .GetOpType 11}} | |||
<p class="text light grey has-emoji">{{index .GetIssueInfos 1}}</p> |