aboutsummaryrefslogtreecommitdiffstats
path: root/models/issue_assignees.go
diff options
context:
space:
mode:
authorkolaente <konrad@kola-entertainments.de>2018-05-09 18:29:04 +0200
committerLauris BH <lauris@nix.lv>2018-05-09 19:29:04 +0300
commit95f2e2b57beedcdeb2b9623dc86e26f252fdd7bd (patch)
treeebfeb5b898f72fc57ac1fe9680374fdb4bc91aeb /models/issue_assignees.go
parent238a997ec0b5d3e3bed0e55edcacebb44f86d373 (diff)
downloadgitea-95f2e2b57beedcdeb2b9623dc86e26f252fdd7bd.tar.gz
gitea-95f2e2b57beedcdeb2b9623dc86e26f252fdd7bd.zip
Multiple assignees (#3705)
Diffstat (limited to 'models/issue_assignees.go')
-rw-r--r--models/issue_assignees.go263
1 files changed, 263 insertions, 0 deletions
diff --git a/models/issue_assignees.go b/models/issue_assignees.go
new file mode 100644
index 0000000000..3e68126a6c
--- /dev/null
+++ b/models/issue_assignees.go
@@ -0,0 +1,263 @@
+// Copyright 2018 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 (
+ "fmt"
+
+ "code.gitea.io/gitea/modules/log"
+
+ api "code.gitea.io/sdk/gitea"
+ "github.com/go-xorm/xorm"
+)
+
+// IssueAssignees saves all issue assignees
+type IssueAssignees struct {
+ ID int64 `xorm:"pk autoincr"`
+ AssigneeID int64 `xorm:"INDEX"`
+ IssueID int64 `xorm:"INDEX"`
+}
+
+// This loads all assignees of an issue
+func (issue *Issue) loadAssignees(e Engine) (err error) {
+ // Reset maybe preexisting assignees
+ issue.Assignees = []*User{}
+
+ err = e.Table("`user`").
+ Join("INNER", "issue_assignees", "assignee_id = `user`.id").
+ Where("issue_assignees.issue_id = ?", issue.ID).
+ Find(&issue.Assignees)
+
+ if err != nil {
+ return err
+ }
+
+ // Check if we have at least one assignee and if yes put it in as `Assignee`
+ if len(issue.Assignees) > 0 {
+ issue.Assignee = issue.Assignees[0]
+ }
+
+ return
+}
+
+// GetAssigneesByIssue returns everyone assigned to that issue
+func GetAssigneesByIssue(issue *Issue) (assignees []*User, err error) {
+ err = issue.loadAssignees(x)
+ if err != nil {
+ return assignees, err
+ }
+
+ return issue.Assignees, nil
+}
+
+// IsUserAssignedToIssue returns true when the user is assigned to the issue
+func IsUserAssignedToIssue(issue *Issue, user *User) (isAssigned bool, err error) {
+ isAssigned, err = x.Exist(&IssueAssignees{IssueID: issue.ID, AssigneeID: user.ID})
+ return
+}
+
+// DeleteNotPassedAssignee deletes all assignees who aren't passed via the "assignees" array
+func DeleteNotPassedAssignee(issue *Issue, doer *User, assignees []*User) (err error) {
+ var found bool
+
+ for _, assignee := range issue.Assignees {
+
+ found = false
+ for _, alreadyAssignee := range assignees {
+ if assignee.ID == alreadyAssignee.ID {
+ found = true
+ break
+ }
+ }
+
+ if !found {
+ // This function also does comments and hooks, which is why we call it seperatly instead of directly removing the assignees here
+ if err := UpdateAssignee(issue, doer, assignee.ID); err != nil {
+ return err
+ }
+ }
+ }
+
+ return nil
+}
+
+// MakeAssigneeList concats a string with all names of the assignees. Useful for logs.
+func MakeAssigneeList(issue *Issue) (assigneeList string, err error) {
+ err = issue.loadAssignees(x)
+ if err != nil {
+ return "", err
+ }
+
+ for in, assignee := range issue.Assignees {
+ assigneeList += assignee.Name
+
+ if len(issue.Assignees) > (in + 1) {
+ assigneeList += ", "
+ }
+ }
+ return
+}
+
+// ClearAssigneeByUserID deletes all assignments of an user
+func clearAssigneeByUserID(sess *xorm.Session, userID int64) (err error) {
+ _, err = sess.Delete(&IssueAssignees{AssigneeID: userID})
+ return
+}
+
+// AddAssigneeIfNotAssigned adds an assignee only if he isn't aleady assigned to the issue
+func AddAssigneeIfNotAssigned(issue *Issue, doer *User, assigneeID int64) (err error) {
+ // Check if the user is already assigned
+ isAssigned, err := IsUserAssignedToIssue(issue, &User{ID: assigneeID})
+ if err != nil {
+ return err
+ }
+
+ if !isAssigned {
+ return issue.ChangeAssignee(doer, assigneeID)
+ }
+ return nil
+}
+
+// UpdateAssignee deletes or adds an assignee to an issue
+func UpdateAssignee(issue *Issue, doer *User, assigneeID int64) (err error) {
+ return issue.ChangeAssignee(doer, assigneeID)
+}
+
+// ChangeAssignee changes the Assignee of this issue.
+func (issue *Issue) ChangeAssignee(doer *User, assigneeID int64) (err error) {
+ sess := x.NewSession()
+ defer sess.Close()
+
+ if err := sess.Begin(); err != nil {
+ return err
+ }
+
+ if err := issue.changeAssignee(sess, doer, assigneeID); err != nil {
+ return err
+ }
+
+ return sess.Commit()
+}
+
+func (issue *Issue) changeAssignee(sess *xorm.Session, doer *User, assigneeID int64) (err error) {
+
+ // Update the assignee
+ removed, err := updateIssueAssignee(sess, issue, assigneeID)
+ if err != nil {
+ return fmt.Errorf("UpdateIssueUserByAssignee: %v", err)
+ }
+
+ // Repo infos
+ if err = issue.loadRepo(sess); err != nil {
+ return fmt.Errorf("loadRepo: %v", err)
+ }
+
+ // Comment
+ if _, err = createAssigneeComment(sess, doer, issue.Repo, issue, assigneeID, removed); err != nil {
+ return fmt.Errorf("createAssigneeComment: %v", err)
+ }
+
+ if issue.IsPull {
+ issue.PullRequest = &PullRequest{Issue: issue}
+ apiPullRequest := &api.PullRequestPayload{
+ Index: issue.Index,
+ PullRequest: issue.PullRequest.APIFormat(),
+ Repository: issue.Repo.APIFormat(AccessModeNone),
+ Sender: doer.APIFormat(),
+ }
+ if removed {
+ apiPullRequest.Action = api.HookIssueUnassigned
+ } else {
+ apiPullRequest.Action = api.HookIssueAssigned
+ }
+ if err := PrepareWebhooks(issue.Repo, HookEventPullRequest, apiPullRequest); err != nil {
+ log.Error(4, "PrepareWebhooks [is_pull: %v, remove_assignee: %v]: %v", issue.IsPull, removed, err)
+ return nil
+ }
+ }
+ go HookQueue.Add(issue.RepoID)
+ return nil
+}
+
+// UpdateAPIAssignee is a helper function to add or delete one or multiple issue assignee(s)
+// Deleting is done the Github way (quote from their api documentation):
+// https://developer.github.com/v3/issues/#edit-an-issue
+// "assignees" (array): Logins for Users to assign to this issue.
+// Pass one or more user logins to replace the set of assignees on this Issue.
+// Send an empty array ([]) to clear all assignees from the Issue.
+func UpdateAPIAssignee(issue *Issue, oneAssignee string, multipleAssignees []string, doer *User) (err error) {
+ var allNewAssignees []*User
+
+ // Keep the old assignee thingy for compatibility reasons
+ if oneAssignee != "" {
+ // Prevent double adding assignees
+ var isDouble bool
+ for _, assignee := range multipleAssignees {
+ if assignee == oneAssignee {
+ isDouble = true
+ break
+ }
+ }
+
+ if !isDouble {
+ multipleAssignees = append(multipleAssignees, oneAssignee)
+ }
+ }
+
+ // Loop through all assignees to add them
+ for _, assigneeName := range multipleAssignees {
+ assignee, err := GetUserByName(assigneeName)
+ if err != nil {
+ return err
+ }
+
+ allNewAssignees = append(allNewAssignees, assignee)
+ }
+
+ // Delete all old assignees not passed
+ if err = DeleteNotPassedAssignee(issue, doer, allNewAssignees); err != nil {
+ return err
+ }
+
+ // Add all new assignees
+ // Update the assignee. The function will check if the user exists, is already
+ // assigned (which he shouldn't as we deleted all assignees before) and
+ // has access to the repo.
+ for _, assignee := range allNewAssignees {
+ // Extra method to prevent double adding (which would result in removing)
+ err = AddAssigneeIfNotAssigned(issue, doer, assignee.ID)
+ if err != nil {
+ return err
+ }
+ }
+
+ return
+}
+
+// MakeIDsFromAPIAssigneesToAdd returns an array with all assignee IDs
+func MakeIDsFromAPIAssigneesToAdd(oneAssignee string, multipleAssignees []string) (assigneeIDs []int64, err error) {
+
+ // Keeping the old assigning method for compatibility reasons
+ if oneAssignee != "" {
+
+ // Prevent double adding assignees
+ var isDouble bool
+ for _, assignee := range multipleAssignees {
+ if assignee == oneAssignee {
+ isDouble = true
+ break
+ }
+ }
+
+ if !isDouble {
+ multipleAssignees = append(multipleAssignees, oneAssignee)
+ }
+ }
+
+ // Get the IDs of all assignees
+ assigneeIDs = GetUserIDsByNames(multipleAssignees)
+
+ return
+}