From e86728340654b18a657a65920c16e28a1b00cca7 Mon Sep 17 00:00:00 2001
From: Unknown <joe2010xtmf@163.com>
Date: Thu, 8 May 2014 12:24:11 -0400
Subject: [PATCH] Assignee back end

---
 models/access.go            |  5 ++--
 models/issue.go             | 50 +++++++++++++++++++++----------------
 models/repo.go              | 21 ++++++++++++++--
 modules/middleware/repo.go  | 26 ++++++++++++-------
 routers/repo/issue.go       | 29 +++++++++++++++------
 routers/repo/setting.go     |  2 +-
 templates/issue/create.tmpl |  6 +++--
 templates/issue/view.tmpl   |  3 ++-
 8 files changed, 96 insertions(+), 46 deletions(-)

diff --git a/models/access.go b/models/access.go
index 749a2604d5..4f944517ed 100644
--- a/models/access.go
+++ b/models/access.go
@@ -58,9 +58,10 @@ func UpdateAccessWithSession(sess *xorm.Session, access *Access) error {
 }
 
 // HasAccess returns true if someone can read or write to given repository.
-func HasAccess(userName, repoName string, mode int) (bool, error) {
+// The repoName should be in format <username>/<reponame>.
+func HasAccess(uname, repoName string, mode int) (bool, error) {
 	access := &Access{
-		UserName: strings.ToLower(userName),
+		UserName: strings.ToLower(uname),
 		RepoName: strings.ToLower(repoName),
 	}
 	has, err := orm.Get(access)
diff --git a/models/issue.go b/models/issue.go
index 14faa5b31f..525e75f2c3 100644
--- a/models/issue.go
+++ b/models/issue.go
@@ -28,8 +28,9 @@ type Issue struct {
 	Poster          *User `xorm:"-"`
 	MilestoneId     int64
 	AssigneeId      int64
-	IsRead          bool `xorm:"-"`
-	IsPull          bool // Indicates whether is a pull request or not.
+	Assignee        *User `xorm:"-"`
+	IsRead          bool  `xorm:"-"`
+	IsPull          bool  // Indicates whether is a pull request or not.
 	IsClosed        bool
 	Labels          string `xorm:"TEXT"`
 	Content         string `xorm:"TEXT"`
@@ -46,6 +47,14 @@ func (i *Issue) GetPoster() (err error) {
 	return err
 }
 
+func (i *Issue) GetAssignee() (err error) {
+	if i.AssigneeId == 0 {
+		return nil
+	}
+	i.Assignee, err = GetUserById(i.AssigneeId)
+	return err
+}
+
 // CreateIssue creates new issue for repository.
 func NewIssue(issue *Issue) (err error) {
 	sess := orm.NewSession()
@@ -159,38 +168,35 @@ type IssueUser struct {
 }
 
 // NewIssueUserPairs adds new issue-user pairs for new issue of repository.
-func NewIssueUserPairs(rid, iid, oid, uid, aid int64) (err error) {
+func NewIssueUserPairs(rid, iid, oid, pid, aid int64, repoName string) (err error) {
 	iu := &IssueUser{IssueId: iid, RepoId: rid}
 
-	ws, err := GetWatchers(rid)
+	us, err := GetCollaborators(repoName)
 	if err != nil {
 		return err
 	}
 
-	// TODO: check collaborators.
-	// Add owner.
-	ids := []int64{oid}
-	for _, id := range ids {
-		if IsWatching(id, rid) {
-			continue
+	isNeedAddPoster := true
+	for _, u := range us {
+		iu.Uid = u.Id
+		iu.IsPoster = iu.Uid == pid
+		if isNeedAddPoster && iu.IsPoster {
+			isNeedAddPoster = false
 		}
-
-		// In case owner is not watching.
-		ws = append(ws, &Watch{UserId: id})
-	}
-
-	for _, w := range ws {
-		if w.UserId == 0 {
-			continue
-		}
-
-		iu.Uid = w.UserId
-		iu.IsPoster = iu.Uid == uid
 		iu.IsAssigned = iu.Uid == aid
 		if _, err = orm.Insert(iu); err != nil {
 			return err
 		}
 	}
+	if isNeedAddPoster {
+		iu.Uid = pid
+		iu.IsPoster = true
+		iu.IsAssigned = iu.Uid == aid
+		if _, err = orm.Insert(iu); err != nil {
+			return err
+		}
+	}
+
 	return nil
 }
 
diff --git a/models/repo.go b/models/repo.go
index 595d79b5c3..e97164d26b 100644
--- a/models/repo.go
+++ b/models/repo.go
@@ -713,8 +713,8 @@ func GetRepositoryCount(user *User) (int64, error) {
 	return orm.Count(&Repository{OwnerId: user.Id})
 }
 
-// GetCollaborators returns a list of user name of repository's collaborators.
-func GetCollaborators(repoName string) ([]string, error) {
+// GetCollaboratorNames returns a list of user name of repository's collaborators.
+func GetCollaboratorNames(repoName string) ([]string, error) {
 	accesses := make([]*Access, 0, 10)
 	if err := orm.Find(&accesses, &Access{RepoName: strings.ToLower(repoName)}); err != nil {
 		return nil, err
@@ -727,6 +727,23 @@ func GetCollaborators(repoName string) ([]string, error) {
 	return names, nil
 }
 
+// GetCollaborators returns a list of users of repository's collaborators.
+func GetCollaborators(repoName string) (us []*User, err error) {
+	accesses := make([]*Access, 0, 10)
+	if err = orm.Find(&accesses, &Access{RepoName: strings.ToLower(repoName)}); err != nil {
+		return nil, err
+	}
+
+	us = make([]*User, len(accesses))
+	for i := range accesses {
+		us[i], err = GetUserByName(accesses[i].UserName)
+		if err != nil {
+			return nil, err
+		}
+	}
+	return us, nil
+}
+
 // Watch is connection request for receiving repository notifycation.
 type Watch struct {
 	Id     int64
diff --git a/modules/middleware/repo.go b/modules/middleware/repo.go
index 7d0cc97970..53d4dae4f8 100644
--- a/modules/middleware/repo.go
+++ b/modules/middleware/repo.go
@@ -21,6 +21,7 @@ import (
 
 func RepoAssignment(redirect bool, args ...bool) martini.Handler {
 	return func(ctx *Context, params martini.Params) {
+		log.Trace(fmt.Sprint(args))
 		// valid brachname
 		var validBranch bool
 		// display bare quick start if it is a bare repo
@@ -38,29 +39,36 @@ func RepoAssignment(redirect bool, args ...bool) martini.Handler {
 		}
 
 		var (
-			user *models.User
-			err  error
+			user        *models.User
+			err         error
+			isTrueOwner bool
 		)
 
 		userName := params["username"]
 		repoName := params["reponame"]
 		refName := params["branchname"]
 
-		// TODO: check collaborators
-		// get repository owner
-		ctx.Repo.IsOwner = ctx.IsSigned && ctx.User.LowerName == strings.ToLower(userName)
+		// Collaborators who have write access can be seen as owners.
+		if ctx.IsSigned {
+			ctx.Repo.IsOwner, err = models.HasAccess(ctx.User.Name, repoName, models.AU_WRITABLE)
+			if err != nil {
+				ctx.Handle(500, "RepoAssignment(HasAccess)", err)
+				return
+			}
+			isTrueOwner = ctx.User.LowerName == strings.ToLower(userName)
+		}
 
-		if !ctx.Repo.IsOwner {
-			user, err = models.GetUserByName(params["username"])
+		if !isTrueOwner {
+			user, err = models.GetUserByName(userName)
 			if err != nil {
 				if err == models.ErrUserNotExist {
-					ctx.Handle(404, "RepoAssignment", err)
+					ctx.Handle(404, "RepoAssignment(GetUserByName)", err)
 					return
 				} else if redirect {
 					ctx.Redirect("/")
 					return
 				}
-				ctx.Handle(500, "RepoAssignment", err)
+				ctx.Handle(500, "RepoAssignment(GetUserByName)", err)
 				return
 			}
 		} else {
diff --git a/routers/repo/issue.go b/routers/repo/issue.go
index da34c03c25..3d25f06ae5 100644
--- a/routers/repo/issue.go
+++ b/routers/repo/issue.go
@@ -114,6 +114,13 @@ func CreateIssue(ctx *middleware.Context, params martini.Params) {
 	ctx.Data["Title"] = "Create issue"
 	ctx.Data["IsRepoToolbarIssues"] = true
 	ctx.Data["IsRepoToolbarIssuesList"] = false
+
+	us, err := models.GetCollaborators(strings.TrimPrefix(ctx.Repo.RepoLink, "/"))
+	if err != nil {
+		ctx.Handle(500, "issue.CreateIssue(GetCollaborators)", err)
+		return
+	}
+	ctx.Data["Collaborators"] = us
 	ctx.HTML(200, "issue/create")
 }
 
@@ -122,6 +129,13 @@ func CreateIssuePost(ctx *middleware.Context, params martini.Params, form auth.C
 	ctx.Data["IsRepoToolbarIssues"] = true
 	ctx.Data["IsRepoToolbarIssuesList"] = false
 
+	us, err := models.GetCollaborators(strings.TrimPrefix(ctx.Repo.RepoLink, "/"))
+	if err != nil {
+		ctx.Handle(500, "issue.CreateIssue(GetCollaborators)", err)
+		return
+	}
+	ctx.Data["Collaborators"] = us
+
 	if ctx.HasError() {
 		ctx.HTML(200, "issue/create")
 		return
@@ -140,8 +154,8 @@ func CreateIssuePost(ctx *middleware.Context, params martini.Params, form auth.C
 	if err := models.NewIssue(issue); err != nil {
 		ctx.Handle(500, "issue.CreateIssue(NewIssue)", err)
 		return
-	} else if err := models.NewIssueUserPairs(issue.RepoId, issue.Id,
-		ctx.Repo.Owner.Id, ctx.User.Id, form.AssigneeId); err != nil {
+	} else if err := models.NewIssueUserPairs(issue.RepoId, issue.Id, ctx.Repo.Owner.Id,
+		ctx.User.Id, form.AssigneeId, ctx.Repo.Repository.Name); err != nil {
 		ctx.Handle(500, "issue.CreateIssue(NewIssueUserPairs)", err)
 		return
 	}
@@ -219,13 +233,14 @@ func ViewIssue(ctx *middleware.Context, params martini.Params) {
 		return
 	}
 
-	// Get poster.
-	u, err := models.GetUserById(issue.PosterId)
-	if err != nil {
-		ctx.Handle(500, "issue.ViewIssue(GetUserById): %v", err)
+	// Get poster and Assignee.
+	if err = issue.GetPoster(); err != nil {
+		ctx.Handle(500, "issue.ViewIssue(GetPoster): %v", err)
+		return
+	} else if err = issue.GetAssignee(); err != nil {
+		ctx.Handle(500, "issue.ViewIssue(GetAssignee): %v", err)
 		return
 	}
-	issue.Poster = u
 	issue.RenderedContent = string(base.RenderMarkdown([]byte(issue.Content), ctx.Repo.RepoLink))
 
 	// Get comments.
diff --git a/routers/repo/setting.go b/routers/repo/setting.go
index 96f9f857f1..d7c273c63a 100644
--- a/routers/repo/setting.go
+++ b/routers/repo/setting.go
@@ -136,7 +136,7 @@ func Collaboration(ctx *middleware.Context) {
 		return
 	}
 
-	names, err := models.GetCollaborators(repoLink)
+	names, err := models.GetCollaboratorNames(repoLink)
 	if err != nil {
 		ctx.Handle(500, "setting.Collaboration(GetCollaborators)", err)
 		return
diff --git a/templates/issue/create.tmpl b/templates/issue/create.tmpl
index 0306719c6e..6d08f69913 100644
--- a/templates/issue/create.tmpl
+++ b/templates/issue/create.tmpl
@@ -16,7 +16,7 @@
                 </div>
                 <div class="form-group panel-body">
                     <span><strong id="assigned" data-no-assigned="No one">No one</strong> will be assigned</span>
-                    <input type="hidden" name="assigned" value="0" id="assignee"/>&nbsp;&nbsp;
+                    <input type="hidden" name="assigneeid" value="0" id="assignee"/>&nbsp;&nbsp;
                     <div style="display: inline-block;position: relative">
                         <button type="button" class="dropdown-toggle btn btn-default btn-sm" data-toggle="dropdown">
                             <i class="fa fa-group"></i>
@@ -25,7 +25,9 @@
                         <div class="dropdown-menu assignee">
                             <ul class="list-unstyled">
                                 <li data-uid="0" class="clear-assignee hidden"><i class="fa fa-times-circle-o"></i> Clear assignee</li>
-                                <li data-uid="123"><img src="//1.gravatar.com/avatar/f72f7454ce9d710baa506394f68f4132"><strong>fuxiaohei</strong> nickname</li>
+                                {{range .Collaborators}}
+                                <li data-uid="{{.Id}}"><img src="{{.AvatarLink}}"><strong>{{.Name}}</strong> {{.FullName}}</li>
+                                {{end}}
                             </ul>
                         </div>
                     </div>
diff --git a/templates/issue/view.tmpl b/templates/issue/view.tmpl
index 6c069f3c86..780b19899f 100644
--- a/templates/issue/view.tmpl
+++ b/templates/issue/view.tmpl
@@ -98,10 +98,11 @@
                 </div>{{else}}<div class="alert alert-warning"><a class="btn btn-success btn-lg" href="/user/sign_up">Sign up for free</a> to join this conversation. Already have an account? <a href="/user/login">Sign in to comment</a></div>{{end}}
                 </div>
             </div>
+
             <div class="issue-bar col-md-2">
                 <div class="assignee">
                     <h4>Assignee</h4>
-                    <p><img src="//1.gravatar.com/avatar/f72f7454ce9d710baa506394f68f4132"><strong>fuxiaohei</strong></p>
+                    <p>{{if .Issue.Assignee}}<img src="{{.Issue.Assignee.AvatarLink}}"><strong>{{.Issue.Assignee.Name}}</strong>{{else}}No one assigned{{end}}</p>
                 </div>
             </div><!--
             <div class="col-md-3">