| @@ -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) | |||
| @@ -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.IsAssigned = iu.Uid == aid | |||
| if _, err = orm.Insert(iu); err != nil { | |||
| return err | |||
| } | |||
| iu.Uid = w.UserId | |||
| iu.IsPoster = iu.Uid == uid | |||
| } | |||
| if isNeedAddPoster { | |||
| iu.Uid = pid | |||
| iu.IsPoster = true | |||
| iu.IsAssigned = iu.Uid == aid | |||
| if _, err = orm.Insert(iu); err != nil { | |||
| return err | |||
| } | |||
| } | |||
| return nil | |||
| } | |||
| @@ -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 | |||
| @@ -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 { | |||
| @@ -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. | |||
| @@ -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 | |||
| @@ -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"/> | |||
| <input type="hidden" name="assigneeid" value="0" id="assignee"/> | |||
| <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> | |||
| @@ -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"> | |||