summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--models/fixtures/email_address.yml35
-rw-r--r--models/fixtures/user.yml16
-rw-r--r--models/issue.go124
-rw-r--r--models/issue_comment.go63
-rw-r--r--models/repo.go12
-rw-r--r--models/user.go2
-rw-r--r--models/user_mail_test.go169
-rw-r--r--options/locale/locale_en-US.ini3
-rw-r--r--public/js/index.js4
-rw-r--r--routers/api/v1/repo/repo.go2
-rw-r--r--routers/repo/issue.go25
-rw-r--r--routers/user/home.go6
-rw-r--r--routers/user/profile.go64
-rw-r--r--templates/repo/issue/view_content.tmpl13
-rw-r--r--templates/user/profile.tmpl1
15 files changed, 410 insertions, 129 deletions
diff --git a/models/fixtures/email_address.yml b/models/fixtures/email_address.yml
new file mode 100644
index 0000000000..c37d9a7877
--- /dev/null
+++ b/models/fixtures/email_address.yml
@@ -0,0 +1,35 @@
+-
+ id: 1
+ uid: 1
+ email: user11@example.com
+ is_activated: false
+
+-
+ id: 2
+ uid: 1
+ email: user12@example.com
+ is_activated: false
+
+-
+ id: 3
+ uid: 2
+ email: user2@example.com
+ is_activated: true
+
+-
+ id: 4
+ uid: 2
+ email: user21@example.com
+ is_activated: false
+
+-
+ id: 5
+ uid: 9999999
+ email: user9999999@example.com
+ is_activated: true
+
+-
+ id: 6
+ uid: 10
+ email: user101@example.com
+ is_activated: true
diff --git a/models/fixtures/user.yml b/models/fixtures/user.yml
index e8f13b93bd..1bca6049f1 100644
--- a/models/fixtures/user.yml
+++ b/models/fixtures/user.yml
@@ -132,3 +132,19 @@
num_repos: 0
num_members: 1
is_active: false
+
+-
+ id: 10
+ lower_name: user10
+ name: user10
+ full_name: User Ten
+ email: user10@example.com
+ passwd: password
+ type: 0 # user
+ salt: salt
+ is_admin: false
+ avatar: avatar10
+ avatar_email: user10@example.com
+ num_repos: 0
+ num_members: 1
+ is_active: true
diff --git a/models/issue.go b/models/issue.go
index 05b17e4da5..7d7591837e 100644
--- a/models/issue.go
+++ b/models/issue.go
@@ -187,6 +187,18 @@ func (issue *Issue) LoadAttributes() error {
return issue.loadAttributes(x)
}
+// GetIsRead load the `IsRead` field of the issue
+func (issue *Issue) GetIsRead(userID int64) error {
+ issueUser := &IssueUser{IssueID: issue.ID, UID: userID}
+ if has, err := x.Get(issueUser); err != nil {
+ return err
+ } else if !has {
+ return ErrUserNotExist{UID: userID}
+ }
+ issue.IsRead = issueUser.IsRead
+ return nil
+}
+
// HTMLURL returns the absolute URL to this issue.
func (issue *Issue) HTMLURL() string {
var path string
@@ -554,8 +566,6 @@ func (issue *Issue) changeStatus(e *xorm.Session, doer *User, repo *Repository,
if err = updateIssueCols(e, issue, "is_closed"); err != nil {
return err
- } else if err = updateIssueUsersByStatus(e, issue.ID, isClosed); err != nil {
- return err
}
// Update issue count of labels
@@ -693,11 +703,23 @@ func (issue *Issue) ChangeContent(doer *User, content string) (err error) {
// ChangeAssignee changes the Asssignee field of this issue.
func (issue *Issue) ChangeAssignee(doer *User, assigneeID int64) (err error) {
+ var oldAssigneeID = issue.AssigneeID
issue.AssigneeID = assigneeID
if err = UpdateIssueUserByAssignee(issue); err != nil {
return fmt.Errorf("UpdateIssueUserByAssignee: %v", err)
}
+ sess := x.NewSession()
+ defer sess.Close()
+
+ if err = issue.loadRepo(sess); err != nil {
+ return fmt.Errorf("loadRepo: %v", err)
+ }
+
+ if _, err = createAssigneeComment(sess, doer, issue.Repo, issue, oldAssigneeID, assigneeID); err != nil {
+ return fmt.Errorf("createAssigneeComment: %v", err)
+ }
+
issue.Assignee, err = GetUserByID(issue.AssigneeID)
if err != nil && !IsErrUserNotExist(err) {
log.Error(4, "GetUserByID [assignee_id: %v]: %v", issue.AssigneeID, err)
@@ -788,6 +810,15 @@ func newIssue(e *xorm.Session, doer *User, opts NewIssueOptions) (err error) {
}
}
+ if opts.Issue.AssigneeID > 0 {
+ if err = opts.Issue.loadRepo(e); err != nil {
+ return err
+ }
+ if _, err = createAssigneeComment(e, doer, opts.Issue.Repo, opts.Issue, -1, opts.Issue.AssigneeID); err != nil {
+ return err
+ }
+ }
+
if opts.IsPull {
_, err = e.Exec("UPDATE `repository` SET num_pulls = num_pulls + 1 WHERE id = ?", opts.Issue.RepoID)
} else {
@@ -1087,13 +1118,9 @@ type IssueUser struct {
ID int64 `xorm:"pk autoincr"`
UID int64 `xorm:"INDEX"` // User ID.
IssueID int64
- RepoID int64 `xorm:"INDEX"`
- MilestoneID int64
IsRead bool
IsAssigned bool
IsMentioned bool
- IsPoster bool
- IsClosed bool
}
func newIssueUsers(e *xorm.Session, repo *Repository, issue *Issue) error {
@@ -1109,24 +1136,17 @@ func newIssueUsers(e *xorm.Session, repo *Repository, issue *Issue) error {
// and just waste 1 unit is cheaper than re-allocate memory once.
issueUsers := make([]*IssueUser, 0, len(assignees)+1)
for _, assignee := range assignees {
- isPoster := assignee.ID == issue.PosterID
issueUsers = append(issueUsers, &IssueUser{
IssueID: issue.ID,
- RepoID: repo.ID,
UID: assignee.ID,
- IsPoster: isPoster,
IsAssigned: assignee.ID == issue.AssigneeID,
})
- if !isPosterAssignee && isPoster {
- isPosterAssignee = true
- }
+ isPosterAssignee = isPosterAssignee || assignee.ID == issue.PosterID
}
if !isPosterAssignee {
issueUsers = append(issueUsers, &IssueUser{
- IssueID: issue.ID,
- RepoID: repo.ID,
- UID: issue.PosterID,
- IsPoster: true,
+ IssueID: issue.ID,
+ UID: issue.PosterID,
})
}
@@ -1151,62 +1171,6 @@ func NewIssueUsers(repo *Repository, issue *Issue) (err error) {
return sess.Commit()
}
-// PairsContains returns true when pairs list contains given issue.
-func PairsContains(ius []*IssueUser, issueID, uid int64) int {
- for i := range ius {
- if ius[i].IssueID == issueID &&
- ius[i].UID == uid {
- return i
- }
- }
- return -1
-}
-
-// GetIssueUsers returns issue-user pairs by given repository and user.
-func GetIssueUsers(rid, uid int64, isClosed bool) ([]*IssueUser, error) {
- ius := make([]*IssueUser, 0, 10)
- err := x.Where("is_closed=?", isClosed).Find(&ius, &IssueUser{RepoID: rid, UID: uid})
- return ius, err
-}
-
-// GetIssueUserPairsByRepoIds returns issue-user pairs by given repository IDs.
-func GetIssueUserPairsByRepoIds(rids []int64, isClosed bool, page int) ([]*IssueUser, error) {
- if len(rids) == 0 {
- return []*IssueUser{}, nil
- }
-
- ius := make([]*IssueUser, 0, 10)
- sess := x.
- Limit(20, (page-1)*20).
- Where("is_closed=?", isClosed).
- In("repo_id", rids)
- err := sess.Find(&ius)
- return ius, err
-}
-
-// GetIssueUserPairsByMode returns issue-user pairs by given repository and user.
-func GetIssueUserPairsByMode(uid, rid int64, isClosed bool, page, filterMode int) ([]*IssueUser, error) {
- ius := make([]*IssueUser, 0, 10)
- sess := x.
- Limit(20, (page-1)*20).
- Where("uid=?", uid).
- And("is_closed=?", isClosed)
- if rid > 0 {
- sess.And("repo_id=?", rid)
- }
-
- switch filterMode {
- case FilterModeAssign:
- sess.And("is_assigned=?", true)
- case FilterModeCreate:
- sess.And("is_poster=?", true)
- default:
- return ius, nil
- }
- err := sess.Find(&ius)
- return ius, err
-}
-
// UpdateIssueMentions extracts mentioned people from content and
// updates issue-user relations for them.
func UpdateIssueMentions(e Engine, issueID int64, mentions []string) error {
@@ -1436,16 +1400,6 @@ func UpdateIssue(issue *Issue) error {
return updateIssue(x, issue)
}
-func updateIssueUsersByStatus(e Engine, issueID int64, isClosed bool) error {
- _, err := e.Exec("UPDATE `issue_user` SET is_closed=? WHERE issue_id=?", isClosed, issueID)
- return err
-}
-
-// UpdateIssueUsersByStatus updates issue-user relations by issue status.
-func UpdateIssueUsersByStatus(issueID int64, isClosed bool) error {
- return updateIssueUsersByStatus(x, issueID, isClosed)
-}
-
func updateIssueUserByAssignee(e *xorm.Session, issue *Issue) (err error) {
if _, err = e.Exec("UPDATE `issue_user` SET is_assigned = ? WHERE issue_id = ?", false, issue.ID); err != nil {
return err
@@ -1790,8 +1744,6 @@ func changeMilestoneAssign(e *xorm.Session, doer *User, issue *Issue, oldMilesto
if err = updateMilestone(e, m); err != nil {
return err
- } else if _, err = e.Exec("UPDATE `issue_user` SET milestone_id = 0 WHERE issue_id = ?", issue.ID); err != nil {
- return err
}
}
@@ -1808,8 +1760,6 @@ func changeMilestoneAssign(e *xorm.Session, doer *User, issue *Issue, oldMilesto
if err = updateMilestone(e, m); err != nil {
return err
- } else if _, err = e.Exec("UPDATE `issue_user` SET milestone_id = ? WHERE issue_id = ?", m.ID, issue.ID); err != nil {
- return err
}
}
@@ -1873,8 +1823,6 @@ func DeleteMilestoneByRepoID(repoID, id int64) error {
if _, err = sess.Exec("UPDATE `issue` SET milestone_id = 0 WHERE milestone_id = ?", m.ID); err != nil {
return err
- } else if _, err = sess.Exec("UPDATE `issue_user` SET milestone_id = 0 WHERE milestone_id = ?", m.ID); err != nil {
- return err
}
return sess.Commit()
}
diff --git a/models/issue_comment.go b/models/issue_comment.go
index d128e2ebab..9e5e87e3c9 100644
--- a/models/issue_comment.go
+++ b/models/issue_comment.go
@@ -40,6 +40,8 @@ const (
CommentTypeLabel
// Milestone changed
CommentTypeMilestone
+ // Assignees changed
+ CommentTypeAssignees
)
// CommentTag defines comment tag type
@@ -55,17 +57,22 @@ const (
// Comment represents a comment in commit and issue page.
type Comment struct {
- ID int64 `xorm:"pk autoincr"`
- Type CommentType
- PosterID int64 `xorm:"INDEX"`
- Poster *User `xorm:"-"`
- IssueID int64 `xorm:"INDEX"`
- LabelID int64
- Label *Label `xorm:"-"`
- OldMilestoneID int64
- MilestoneID int64
- OldMilestone *Milestone `xorm:"-"`
- Milestone *Milestone `xorm:"-"`
+ ID int64 `xorm:"pk autoincr"`
+ Type CommentType
+ PosterID int64 `xorm:"INDEX"`
+ Poster *User `xorm:"-"`
+ IssueID int64 `xorm:"INDEX"`
+ LabelID int64
+ Label *Label `xorm:"-"`
+ OldMilestoneID int64
+ MilestoneID int64
+ OldMilestone *Milestone `xorm:"-"`
+ Milestone *Milestone `xorm:"-"`
+ OldAssigneeID int64
+ AssigneeID int64
+ Assignee *User `xorm:"-"`
+ OldAssignee *User `xorm:"-"`
+
CommitID int64
Line int64
Content string `xorm:"TEXT"`
@@ -240,6 +247,25 @@ func (c *Comment) LoadMilestone() error {
return nil
}
+// LoadAssignees if comment.Type is CommentTypeAssignees, then load assignees
+func (c *Comment) LoadAssignees() error {
+ var err error
+ if c.OldAssigneeID > 0 {
+ c.OldAssignee, err = getUserByID(x, c.OldAssigneeID)
+ if err != nil {
+ return err
+ }
+ }
+
+ if c.AssigneeID > 0 {
+ c.Assignee, err = getUserByID(x, c.AssigneeID)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
// MailParticipants sends new comment emails to repository watchers
// and mentioned people.
func (c *Comment) MailParticipants(e Engine, opType ActionType, issue *Issue) (err error) {
@@ -276,6 +302,8 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err
LabelID: LabelID,
OldMilestoneID: opts.OldMilestoneID,
MilestoneID: opts.MilestoneID,
+ OldAssigneeID: opts.OldAssigneeID,
+ AssigneeID: opts.AssigneeID,
CommitID: opts.CommitID,
CommitSHA: opts.CommitSHA,
Line: opts.LineNum,
@@ -416,6 +444,17 @@ func createMilestoneComment(e *xorm.Session, doer *User, repo *Repository, issue
})
}
+func createAssigneeComment(e *xorm.Session, doer *User, repo *Repository, issue *Issue, oldAssigneeID, assigneeID int64) (*Comment, error) {
+ return createComment(e, &CreateCommentOptions{
+ Type: CommentTypeAssignees,
+ Doer: doer,
+ Repo: repo,
+ Issue: issue,
+ OldAssigneeID: oldAssigneeID,
+ AssigneeID: assigneeID,
+ })
+}
+
// CreateCommentOptions defines options for creating comment
type CreateCommentOptions struct {
Type CommentType
@@ -426,6 +465,8 @@ type CreateCommentOptions struct {
OldMilestoneID int64
MilestoneID int64
+ OldAssigneeID int64
+ AssigneeID int64
CommitID int64
CommitSHA string
LineNum int64
diff --git a/models/repo.go b/models/repo.go
index e286970373..3a503d8953 100644
--- a/models/repo.go
+++ b/models/repo.go
@@ -1424,7 +1424,6 @@ func DeleteRepository(uid, repoID int64) error {
&Watch{RepoID: repoID},
&Star{RepoID: repoID},
&Mirror{RepoID: repoID},
- &IssueUser{RepoID: repoID},
&Milestone{RepoID: repoID},
&Release{RepoID: repoID},
&Collaboration{RepoID: repoID},
@@ -1445,6 +1444,9 @@ func DeleteRepository(uid, repoID int64) error {
if _, err = sess.Delete(&Comment{IssueID: issues[i].ID}); err != nil {
return err
}
+ if _, err = sess.Delete(&IssueUser{IssueID: issues[i].ID}); err != nil {
+ return err
+ }
attachments := make([]*Attachment, 0, 5)
if err = sess.
@@ -1577,10 +1579,14 @@ func GetRepositoryByID(id int64) (*Repository, error) {
}
// GetUserRepositories returns a list of repositories of given user.
-func GetUserRepositories(userID int64, private bool, page, pageSize int) ([]*Repository, error) {
+func GetUserRepositories(userID int64, private bool, page, pageSize int, orderBy string) ([]*Repository, error) {
+ if len(orderBy) == 0 {
+ orderBy = "updated_unix DESC"
+ }
+
sess := x.
Where("owner_id = ?", userID).
- Desc("updated_unix")
+ OrderBy(orderBy)
if !private {
sess.And("is_private=?", false)
}
diff --git a/models/user.go b/models/user.go
index c4a1ce3d56..c7ceacaa6b 100644
--- a/models/user.go
+++ b/models/user.go
@@ -491,7 +491,7 @@ func (u *User) GetOrganizationCount() (int64, error) {
// GetRepositories returns repositories that user owns, including private repositories.
func (u *User) GetRepositories(page, pageSize int) (err error) {
- u.Repos, err = GetUserRepositories(u.ID, true, page, pageSize)
+ u.Repos, err = GetUserRepositories(u.ID, true, page, pageSize, "")
return err
}
diff --git a/models/user_mail_test.go b/models/user_mail_test.go
new file mode 100644
index 0000000000..4c0b6804d6
--- /dev/null
+++ b/models/user_mail_test.go
@@ -0,0 +1,169 @@
+// 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 models
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestGetEmailAddresses(t *testing.T) {
+ assert.NoError(t, PrepareTestDatabase())
+
+ emails, _ := GetEmailAddresses(int64(1))
+ assert.Len(t, emails, 3)
+ assert.False(t, emails[0].IsPrimary)
+ assert.True(t, emails[2].IsActivated)
+ assert.True(t, emails[2].IsPrimary)
+
+ emails, _ = GetEmailAddresses(int64(2))
+ assert.Len(t, emails, 2)
+ assert.True(t, emails[0].IsPrimary)
+ assert.True(t, emails[0].IsActivated)
+}
+
+func TestIsEmailUsed(t *testing.T) {
+ assert.NoError(t, PrepareTestDatabase())
+
+ isExist, _ := IsEmailUsed("")
+ assert.True(t, isExist)
+ isExist, _ = IsEmailUsed("user11@example.com")
+ assert.True(t, isExist)
+ isExist, _ = IsEmailUsed("user1234567890@example.com")
+ assert.False(t, isExist)
+}
+
+func TestAddEmailAddress(t *testing.T) {
+ assert.NoError(t, PrepareTestDatabase())
+
+ assert.NoError(t, AddEmailAddress(&EmailAddress{
+ Email: "user1234567890@example.com",
+ IsPrimary: true,
+ IsActivated: true,
+ }))
+
+ // ErrEmailAlreadyUsed
+ err := AddEmailAddress(&EmailAddress{
+ Email: "user1234567890@example.com",
+ })
+ assert.Error(t, err)
+ assert.True(t, IsErrEmailAlreadyUsed(err))
+}
+
+func TestAddEmailAddresses(t *testing.T) {
+ assert.NoError(t, PrepareTestDatabase())
+
+ // insert multiple email address
+ emails := make([]*EmailAddress, 2)
+ emails[0] = &EmailAddress{
+ Email: "user1234@example.com",
+ IsActivated: true,
+ }
+ emails[1] = &EmailAddress{
+ Email: "user5678@example.com",
+ IsActivated: true,
+ }
+ assert.NoError(t, AddEmailAddresses(emails))
+
+ // ErrEmailAlreadyUsed
+ err := AddEmailAddresses(emails)
+ assert.Error(t, err)
+ assert.True(t, IsErrEmailAlreadyUsed(err))
+}
+
+func TestDeleteEmailAddress(t *testing.T) {
+ assert.NoError(t, PrepareTestDatabase())
+
+ assert.NoError(t, DeleteEmailAddress(&EmailAddress{
+ UID: int64(1),
+ ID: int64(1),
+ Email: "user11@example.com",
+ }))
+
+ assert.NoError(t, DeleteEmailAddress(&EmailAddress{
+ UID: int64(1),
+ Email: "user12@example.com",
+ }))
+
+ // Email address does not exist
+ err := DeleteEmailAddress(&EmailAddress{
+ UID: int64(1),
+ Email: "user1234567890@example.com",
+ })
+ assert.Error(t, err)
+}
+
+func TestDeleteEmailAddresses(t *testing.T) {
+ assert.NoError(t, PrepareTestDatabase())
+
+ // delete multiple email address
+ emails := make([]*EmailAddress, 2)
+ emails[0] = &EmailAddress{
+ UID: int64(2),
+ ID: int64(3),
+ Email: "user2@example.com",
+ }
+ emails[1] = &EmailAddress{
+ UID: int64(2),
+ Email: "user21@example.com",
+ }
+ assert.NoError(t, DeleteEmailAddresses(emails))
+
+ // ErrEmailAlreadyUsed
+ err := DeleteEmailAddresses(emails)
+ assert.Error(t, err)
+}
+
+func TestMakeEmailPrimary(t *testing.T) {
+ assert.NoError(t, PrepareTestDatabase())
+
+ email := &EmailAddress{
+ Email: "user567890@example.com",
+ }
+ err := MakeEmailPrimary(email)
+ assert.Error(t, err)
+ assert.Equal(t, ErrEmailNotExist.Error(), err.Error())
+
+ email = &EmailAddress{
+ Email: "user11@example.com",
+ }
+ err = MakeEmailPrimary(email)
+ assert.Error(t, err)
+ assert.Equal(t, ErrEmailNotActivated.Error(), err.Error())
+
+ email = &EmailAddress{
+ Email: "user9999999@example.com",
+ }
+ err = MakeEmailPrimary(email)
+ assert.Error(t, err)
+ assert.True(t, IsErrUserNotExist(err))
+
+ email = &EmailAddress{
+ Email: "user101@example.com",
+ }
+ err = MakeEmailPrimary(email)
+ assert.NoError(t, err)
+
+ user, _ := GetUserByID(int64(10))
+ assert.Equal(t, "user101@example.com", user.Email)
+}
+
+func TestActivate(t *testing.T) {
+ assert.NoError(t, PrepareTestDatabase())
+
+ email := &EmailAddress{
+ ID: int64(1),
+ UID: int64(1),
+ Email: "user11@example.com",
+ }
+ assert.NoError(t, email.Activate())
+
+ emails, _ := GetEmailAddresses(int64(1))
+ assert.Len(t, emails, 3)
+ assert.True(t, emails[0].IsActivated)
+ assert.True(t, emails[2].IsActivated)
+ assert.True(t, emails[2].IsPrimary)
+}
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index cd2b67f8da..0859709db1 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -546,6 +546,9 @@ issues.remove_label_at = `removed the <div class="ui label" style="color: %s; ba
issues.add_milestone_at = `added this to the <b>%s</b> milestone %s`
issues.change_milestone_at = `modified the milestone from <b>%s</b> to <b>%s</b> %s`
issues.remove_milestone_at = `removed this from the <b>%s</b> milestone %s`
+issues.self_assign_at = `self-assigned this %s`
+issues.add_assignee_at = `was assigned by <b>%s</b> %s`
+issues.remove_assignee_at = `removed their assignment %s`
issues.open_tab = %d Open
issues.close_tab = %d Closed
issues.filter_label = Label
diff --git a/public/js/index.js b/public/js/index.js
index 086ec7656f..5f278ab011 100644
--- a/public/js/index.js
+++ b/public/js/index.js
@@ -989,7 +989,7 @@ function initAdmin() {
switch (authType) {
case '2': // LDAP
$('.ldap').show();
- $('.ldap div.required input').attr('required', 'required');
+ $('.ldap div.required:not(.dldap) input').attr('required', 'required');
break;
case '3': // SMTP
$('.smtp').show();
@@ -1002,7 +1002,7 @@ function initAdmin() {
break;
case '5': // LDAP
$('.dldap').show();
- $('.dldap div.required input').attr('required', 'required');
+ $('.dldap div.required:not(.ldap) input').attr('required', 'required');
break;
}
diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go
index fea625fbe7..d65f30d729 100644
--- a/routers/api/v1/repo/repo.go
+++ b/routers/api/v1/repo/repo.go
@@ -80,7 +80,7 @@ func Search(ctx *context.APIContext) {
// ListMyRepos list all my repositories
// see https://github.com/gogits/go-gogs-client/wiki/Repositories#list-your-repositories
func ListMyRepos(ctx *context.APIContext) {
- ownRepos, err := models.GetUserRepositories(ctx.User.ID, true, 1, ctx.User.NumRepos)
+ ownRepos, err := models.GetUserRepositories(ctx.User.ID, true, 1, ctx.User.NumRepos, "")
if err != nil {
ctx.Error(500, "GetRepositories", err)
return
diff --git a/routers/repo/issue.go b/routers/repo/issue.go
index 76440359ef..8e8d058f2c 100644
--- a/routers/repo/issue.go
+++ b/routers/repo/issue.go
@@ -213,26 +213,14 @@ func Issues(ctx *context.Context) {
}
}
- // Get issue-user relations.
- pairs, err := models.GetIssueUsers(repo.ID, posterID, isShowClosed)
- if err != nil {
- ctx.Handle(500, "GetIssueUsers", err)
- return
- }
-
// Get posters.
for i := range issues {
+ // Check read status
if !ctx.IsSigned {
issues[i].IsRead = true
- continue
- }
-
- // Check read status.
- idx := models.PairsContains(pairs, issues[i].ID, ctx.User.ID)
- if idx > -1 {
- issues[i].IsRead = pairs[idx].IsRead
- } else {
- issues[i].IsRead = true
+ } else if err = issues[i].GetIsRead(ctx.User.ID); err != nil {
+ ctx.Handle(500, "GetIsRead", err)
+ return
}
}
ctx.Data["Issues"] = issues
@@ -627,6 +615,11 @@ func ViewIssue(ctx *context.Context) {
ctx.Handle(500, "LoadMilestone", err)
return
}
+ } else if comment.Type == models.CommentTypeAssignees {
+ if err = comment.LoadAssignees(); err != nil {
+ ctx.Handle(500, "LoadAssignees", err)
+ return
+ }
}
}
diff --git a/routers/user/home.go b/routers/user/home.go
index 66ee6570c0..09711f4bc0 100644
--- a/routers/user/home.go
+++ b/routers/user/home.go
@@ -65,7 +65,9 @@ func retrieveFeeds(ctx *context.Context, ctxUser *models.User, userID, offset in
// Check access of private repositories.
feeds := make([]*models.Action, 0, len(actions))
- unameAvatars := make(map[string]string)
+ unameAvatars := map[string]string{
+ ctxUser.Name: ctxUser.RelAvatarLink(),
+ }
for _, act := range actions {
// Cache results to reduce queries.
_, ok := unameAvatars[act.ActUserName]
@@ -381,7 +383,7 @@ func showOrgProfile(ctx *context.Context) {
ctx.Data["Repos"] = repos
} else {
showPrivate := ctx.IsSigned && ctx.User.IsAdmin
- repos, err = models.GetUserRepositories(org.ID, showPrivate, page, setting.UI.User.RepoPagingNum)
+ repos, err = models.GetUserRepositories(org.ID, showPrivate, page, setting.UI.User.RepoPagingNum, "")
if err != nil {
ctx.Handle(500, "GetRepositories", err)
return
diff --git a/routers/user/profile.go b/routers/user/profile.go
index 857b9e3f5b..3b4d5a6b3c 100644
--- a/routers/user/profile.go
+++ b/routers/user/profile.go
@@ -109,12 +109,66 @@ func Profile(ctx *context.Context) {
page = 1
}
- ctx.Data["Repos"], err = models.GetUserRepositories(ctxUser.ID, showPrivate, page, setting.UI.User.RepoPagingNum)
- if err != nil {
- ctx.Handle(500, "GetRepositories", err)
- return
+ var (
+ repos []*models.Repository
+ count int64
+ err error
+ orderBy string
+ )
+ switch ctx.Query("sort") {
+ case "newest":
+ orderBy = "created_unix DESC"
+ case "oldest":
+ orderBy = "created_unix ASC"
+ case "recentupdate":
+ orderBy = "updated_unix DESC"
+ case "leastupdate":
+ orderBy = "updated_unix ASC"
+ case "reversealphabetically":
+ orderBy = "name DESC"
+ case "alphabetically":
+ orderBy = "name ASC"
+ default:
+ orderBy = "updated_unix DESC"
+ }
+
+ keyword := ctx.Query("q")
+ if len(keyword) == 0 {
+ repos, err = models.GetUserRepositories(ctxUser.ID, showPrivate, page, setting.UI.User.RepoPagingNum, orderBy)
+ if err != nil {
+ ctx.Handle(500, "GetRepositories", err)
+ return
+ }
+ ctx.Data["Repos"] = repos
+ ctx.Data["Page"] = paginater.New(ctxUser.NumRepos, setting.UI.User.RepoPagingNum, page, 5)
+ ctx.Data["Total"] = ctxUser.NumRepos
+ } else {
+ repos, count, err = models.SearchRepositoryByName(&models.SearchRepoOptions{
+ Keyword: keyword,
+ OwnerID: ctxUser.ID,
+ OrderBy: orderBy,
+ Private: ctx.IsSigned && ctx.User.ID == ctxUser.ID,
+ Page: page,
+ PageSize: setting.UI.User.RepoPagingNum,
+ })
+ if err != nil {
+ ctx.Handle(500, "SearchRepositoryByName", err)
+ return
+ }
+
+ ctx.Data["Repos"] = repos
+ ctx.Data["Page"] = paginater.New(int(count), setting.UI.User.RepoPagingNum, page, 5)
+ ctx.Data["Total"] = count
}
- ctx.Data["Page"] = paginater.New(ctxUser.NumRepos, setting.UI.User.RepoPagingNum, page, 5)
+
+ // set default sort value.
+ if ctx.Query("sort") == "" {
+ ctx.Data["SortType"] = "recentupdate"
+ } else {
+ ctx.Data["SortType"] = ctx.Query("sort")
+ }
+
+ ctx.Data["Keyword"] = keyword
}
ctx.HTML(200, tplProfile)
diff --git a/templates/repo/issue/view_content.tmpl b/templates/repo/issue/view_content.tmpl
index 39e0587d94..17263cf3fd 100644
--- a/templates/repo/issue/view_content.tmpl
+++ b/templates/repo/issue/view_content.tmpl
@@ -162,6 +162,19 @@
<span class="text grey"><a href="{{.Poster.HomeLink}}">{{.Poster.Name}}</a>
{{if gt .OldMilestoneID 0}}{{if gt .MilestoneID 0}}{{$.i18n.Tr "repo.issues.change_milestone_at" .OldMilestone.Name .Milestone.Name $createdStr | Safe}}{{else}}{{$.i18n.Tr "repo.issues.remove_milestone_at" .OldMilestone.Name $createdStr | Safe}}{{end}}{{else if gt .MilestoneID 0}}{{$.i18n.Tr "repo.issues.add_milestone_at" .Milestone.Name $createdStr | Safe}}{{end}}</span>
</div>
+ {{else if eq .Type 9}}
+ <div class="event">
+ <span class="octicon octicon-primitive-dot"></span>
+ {{if gt .AssigneeID 0}}{{if eq .Poster.ID .AssigneeID}}<a class="ui avatar image" href="{{.Poster.HomeLink}}">
+ <img src="{{.Poster.RelAvatarLink}}">
+ </a> <span class="text grey"><a href="{{.Poster.HomeLink}}">{{.Poster.Name}}</a> {{$.i18n.Tr "repo.issues.self_assign_at" $createdStr | Safe}} </span>
+ {{else}}<a class="ui avatar image" href="{{.Assignee.HomeLink}}">
+ <img src="{{.Assignee.RelAvatarLink}}">
+ </a><span class="text grey"><a href="{{.Assignee.HomeLink}}">{{.Assignee.Name}}</a> {{$.i18n.Tr "repo.issues.add_assignee_at" .Poster.Name $createdStr | Safe}} </span>{{end}}{{else if gt .OldAssigneeID 0}}
+ <a class="ui avatar image" href="{{.Poster.HomeLink}}">
+ <img src="{{.Poster.RelAvatarLink}}">
+ </a> <span class="text grey"><a href="{{.Poster.HomeLink}}">{{.Poster.Name}}</a> {{$.i18n.Tr "repo.issues.remove_assignee_at" $createdStr | Safe}} </span>{{end}}
+ </div>
{{end}}
{{end}}
diff --git a/templates/user/profile.tmpl b/templates/user/profile.tmpl
index 198a9e3b60..d93ceb8274 100644
--- a/templates/user/profile.tmpl
+++ b/templates/user/profile.tmpl
@@ -95,6 +95,7 @@
{{template "explore/repo_list" .}}
</div>
{{else}}
+ {{template "explore/search" .}}
{{template "explore/repo_list" .}}
{{template "base/paginate" .}}
{{end}}