Assignee back end

This commit is contained in:
Unknown 2014-05-08 12:24:11 -04:00
parent a03f380fa8
commit e867283406
8 changed files with 96 additions and 46 deletions

View file

@ -58,9 +58,10 @@ func UpdateAccessWithSession(sess *xorm.Session, access *Access) error {
} }
// HasAccess returns true if someone can read or write to given repository. // 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{ access := &Access{
UserName: strings.ToLower(userName), UserName: strings.ToLower(uname),
RepoName: strings.ToLower(repoName), RepoName: strings.ToLower(repoName),
} }
has, err := orm.Get(access) has, err := orm.Get(access)

View file

@ -28,6 +28,7 @@ type Issue struct {
Poster *User `xorm:"-"` Poster *User `xorm:"-"`
MilestoneId int64 MilestoneId int64
AssigneeId int64 AssigneeId int64
Assignee *User `xorm:"-"`
IsRead bool `xorm:"-"` IsRead bool `xorm:"-"`
IsPull bool // Indicates whether is a pull request or not. IsPull bool // Indicates whether is a pull request or not.
IsClosed bool IsClosed bool
@ -46,6 +47,14 @@ func (i *Issue) GetPoster() (err error) {
return err 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. // CreateIssue creates new issue for repository.
func NewIssue(issue *Issue) (err error) { func NewIssue(issue *Issue) (err error) {
sess := orm.NewSession() sess := orm.NewSession()
@ -159,38 +168,35 @@ type IssueUser struct {
} }
// NewIssueUserPairs adds new issue-user pairs for new issue of repository. // 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} iu := &IssueUser{IssueId: iid, RepoId: rid}
ws, err := GetWatchers(rid) us, err := GetCollaborators(repoName)
if err != nil { if err != nil {
return err return err
} }
// TODO: check collaborators. isNeedAddPoster := true
// Add owner. for _, u := range us {
ids := []int64{oid} iu.Uid = u.Id
for _, id := range ids { iu.IsPoster = iu.Uid == pid
if IsWatching(id, rid) { if isNeedAddPoster && iu.IsPoster {
continue 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 iu.IsAssigned = iu.Uid == aid
if _, err = orm.Insert(iu); err != nil { if _, err = orm.Insert(iu); err != nil {
return err 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 return nil
} }

View file

@ -713,8 +713,8 @@ func GetRepositoryCount(user *User) (int64, error) {
return orm.Count(&Repository{OwnerId: user.Id}) return orm.Count(&Repository{OwnerId: user.Id})
} }
// GetCollaborators returns a list of user name of repository's collaborators. // GetCollaboratorNames returns a list of user name of repository's collaborators.
func GetCollaborators(repoName string) ([]string, error) { func GetCollaboratorNames(repoName string) ([]string, error) {
accesses := make([]*Access, 0, 10) accesses := make([]*Access, 0, 10)
if err := orm.Find(&accesses, &Access{RepoName: strings.ToLower(repoName)}); err != nil { if err := orm.Find(&accesses, &Access{RepoName: strings.ToLower(repoName)}); err != nil {
return nil, err return nil, err
@ -727,6 +727,23 @@ func GetCollaborators(repoName string) ([]string, error) {
return names, nil 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. // Watch is connection request for receiving repository notifycation.
type Watch struct { type Watch struct {
Id int64 Id int64

View file

@ -21,6 +21,7 @@ import (
func RepoAssignment(redirect bool, args ...bool) martini.Handler { func RepoAssignment(redirect bool, args ...bool) martini.Handler {
return func(ctx *Context, params martini.Params) { return func(ctx *Context, params martini.Params) {
log.Trace(fmt.Sprint(args))
// valid brachname // valid brachname
var validBranch bool var validBranch bool
// display bare quick start if it is a bare repo // display bare quick start if it is a bare repo
@ -40,27 +41,34 @@ func RepoAssignment(redirect bool, args ...bool) martini.Handler {
var ( var (
user *models.User user *models.User
err error err error
isTrueOwner bool
) )
userName := params["username"] userName := params["username"]
repoName := params["reponame"] repoName := params["reponame"]
refName := params["branchname"] refName := params["branchname"]
// TODO: check collaborators // Collaborators who have write access can be seen as owners.
// get repository owner if ctx.IsSigned {
ctx.Repo.IsOwner = ctx.IsSigned && ctx.User.LowerName == strings.ToLower(userName) 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 { if !isTrueOwner {
user, err = models.GetUserByName(params["username"]) user, err = models.GetUserByName(userName)
if err != nil { if err != nil {
if err == models.ErrUserNotExist { if err == models.ErrUserNotExist {
ctx.Handle(404, "RepoAssignment", err) ctx.Handle(404, "RepoAssignment(GetUserByName)", err)
return return
} else if redirect { } else if redirect {
ctx.Redirect("/") ctx.Redirect("/")
return return
} }
ctx.Handle(500, "RepoAssignment", err) ctx.Handle(500, "RepoAssignment(GetUserByName)", err)
return return
} }
} else { } else {

View file

@ -114,6 +114,13 @@ func CreateIssue(ctx *middleware.Context, params martini.Params) {
ctx.Data["Title"] = "Create issue" ctx.Data["Title"] = "Create issue"
ctx.Data["IsRepoToolbarIssues"] = true ctx.Data["IsRepoToolbarIssues"] = true
ctx.Data["IsRepoToolbarIssuesList"] = false 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") 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["IsRepoToolbarIssues"] = true
ctx.Data["IsRepoToolbarIssuesList"] = false 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() { if ctx.HasError() {
ctx.HTML(200, "issue/create") ctx.HTML(200, "issue/create")
return return
@ -140,8 +154,8 @@ func CreateIssuePost(ctx *middleware.Context, params martini.Params, form auth.C
if err := models.NewIssue(issue); err != nil { if err := models.NewIssue(issue); err != nil {
ctx.Handle(500, "issue.CreateIssue(NewIssue)", err) ctx.Handle(500, "issue.CreateIssue(NewIssue)", err)
return return
} else if err := models.NewIssueUserPairs(issue.RepoId, issue.Id, } else if err := models.NewIssueUserPairs(issue.RepoId, issue.Id, ctx.Repo.Owner.Id,
ctx.Repo.Owner.Id, ctx.User.Id, form.AssigneeId); err != nil { ctx.User.Id, form.AssigneeId, ctx.Repo.Repository.Name); err != nil {
ctx.Handle(500, "issue.CreateIssue(NewIssueUserPairs)", err) ctx.Handle(500, "issue.CreateIssue(NewIssueUserPairs)", err)
return return
} }
@ -219,13 +233,14 @@ func ViewIssue(ctx *middleware.Context, params martini.Params) {
return return
} }
// Get poster. // Get poster and Assignee.
u, err := models.GetUserById(issue.PosterId) if err = issue.GetPoster(); err != nil {
if err != nil { ctx.Handle(500, "issue.ViewIssue(GetPoster): %v", err)
ctx.Handle(500, "issue.ViewIssue(GetUserById): %v", err) return
} else if err = issue.GetAssignee(); err != nil {
ctx.Handle(500, "issue.ViewIssue(GetAssignee): %v", err)
return return
} }
issue.Poster = u
issue.RenderedContent = string(base.RenderMarkdown([]byte(issue.Content), ctx.Repo.RepoLink)) issue.RenderedContent = string(base.RenderMarkdown([]byte(issue.Content), ctx.Repo.RepoLink))
// Get comments. // Get comments.

View file

@ -136,7 +136,7 @@ func Collaboration(ctx *middleware.Context) {
return return
} }
names, err := models.GetCollaborators(repoLink) names, err := models.GetCollaboratorNames(repoLink)
if err != nil { if err != nil {
ctx.Handle(500, "setting.Collaboration(GetCollaborators)", err) ctx.Handle(500, "setting.Collaboration(GetCollaborators)", err)
return return

View file

@ -16,7 +16,7 @@
</div> </div>
<div class="form-group panel-body"> <div class="form-group panel-body">
<span><strong id="assigned" data-no-assigned="No one">No one</strong> will be assigned</span> <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"> <div style="display: inline-block;position: relative">
<button type="button" class="dropdown-toggle btn btn-default btn-sm" data-toggle="dropdown"> <button type="button" class="dropdown-toggle btn btn-default btn-sm" data-toggle="dropdown">
<i class="fa fa-group"></i> <i class="fa fa-group"></i>
@ -25,7 +25,9 @@
<div class="dropdown-menu assignee"> <div class="dropdown-menu assignee">
<ul class="list-unstyled"> <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="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> </ul>
</div> </div>
</div> </div>

View file

@ -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>{{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> </div>
<div class="issue-bar col-md-2"> <div class="issue-bar col-md-2">
<div class="assignee"> <div class="assignee">
<h4>Assignee</h4> <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><!-- </div><!--
<div class="col-md-3"> <div class="col-md-3">