mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-11-26 14:12:50 +01:00
Assignee back end
This commit is contained in:
parent
a03f380fa8
commit
e867283406
8 changed files with 96 additions and 46 deletions
|
@ -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)
|
||||||
|
|
|
@ -28,8 +28,9 @@ type Issue struct {
|
||||||
Poster *User `xorm:"-"`
|
Poster *User `xorm:"-"`
|
||||||
MilestoneId int64
|
MilestoneId int64
|
||||||
AssigneeId int64
|
AssigneeId int64
|
||||||
IsRead bool `xorm:"-"`
|
Assignee *User `xorm:"-"`
|
||||||
IsPull bool // Indicates whether is a pull request or not.
|
IsRead bool `xorm:"-"`
|
||||||
|
IsPull bool // Indicates whether is a pull request or not.
|
||||||
IsClosed bool
|
IsClosed bool
|
||||||
Labels string `xorm:"TEXT"`
|
Labels string `xorm:"TEXT"`
|
||||||
Content string `xorm:"TEXT"`
|
Content string `xorm:"TEXT"`
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
@ -38,29 +39,36 @@ 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 {
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"/>
|
<input type="hidden" name="assigneeid" value="0" id="assignee"/>
|
||||||
<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>
|
||||||
|
|
|
@ -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">
|
||||||
|
|
Loading…
Reference in a new issue