1// Copyright 2019 The Gitea Authors. All rights reserved. 2// Use of this source code is governed by a MIT-style 3// license that can be found in the LICENSE file. 4 5package issue 6 7import ( 8 "code.gitea.io/gitea/models" 9 "code.gitea.io/gitea/models/db" 10 repo_model "code.gitea.io/gitea/models/repo" 11 user_model "code.gitea.io/gitea/models/user" 12 "code.gitea.io/gitea/modules/git" 13 "code.gitea.io/gitea/modules/notification" 14 "code.gitea.io/gitea/modules/util" 15) 16 17// NewIssue creates new issue with labels for repository. 18func NewIssue(repo *repo_model.Repository, issue *models.Issue, labelIDs []int64, uuids []string, assigneeIDs []int64) error { 19 if err := models.NewIssue(repo, issue, labelIDs, uuids); err != nil { 20 return err 21 } 22 23 for _, assigneeID := range assigneeIDs { 24 if err := AddAssigneeIfNotAssigned(issue, issue.Poster, assigneeID); err != nil { 25 return err 26 } 27 } 28 29 mentions, err := issue.FindAndUpdateIssueMentions(db.DefaultContext, issue.Poster, issue.Content) 30 if err != nil { 31 return err 32 } 33 34 notification.NotifyNewIssue(issue, mentions) 35 if len(issue.Labels) > 0 { 36 notification.NotifyIssueChangeLabels(issue.Poster, issue, issue.Labels, nil) 37 } 38 if issue.Milestone != nil { 39 notification.NotifyIssueChangeMilestone(issue.Poster, issue, 0) 40 } 41 42 return nil 43} 44 45// ChangeTitle changes the title of this issue, as the given user. 46func ChangeTitle(issue *models.Issue, doer *user_model.User, title string) (err error) { 47 oldTitle := issue.Title 48 issue.Title = title 49 50 if err = issue.ChangeTitle(doer, oldTitle); err != nil { 51 return 52 } 53 54 notification.NotifyIssueChangeTitle(doer, issue, oldTitle) 55 56 return nil 57} 58 59// ChangeIssueRef changes the branch of this issue, as the given user. 60func ChangeIssueRef(issue *models.Issue, doer *user_model.User, ref string) error { 61 oldRef := issue.Ref 62 issue.Ref = ref 63 64 if err := issue.ChangeRef(doer, oldRef); err != nil { 65 return err 66 } 67 68 notification.NotifyIssueChangeRef(doer, issue, oldRef) 69 70 return nil 71} 72 73// UpdateAssignees is a helper function to add or delete one or multiple issue assignee(s) 74// Deleting is done the GitHub way (quote from their api documentation): 75// https://developer.github.com/v3/issues/#edit-an-issue 76// "assignees" (array): Logins for Users to assign to this issue. 77// Pass one or more user logins to replace the set of assignees on this Issue. 78// Send an empty array ([]) to clear all assignees from the Issue. 79func UpdateAssignees(issue *models.Issue, oneAssignee string, multipleAssignees []string, doer *user_model.User) (err error) { 80 var allNewAssignees []*user_model.User 81 82 // Keep the old assignee thingy for compatibility reasons 83 if oneAssignee != "" { 84 // Prevent double adding assignees 85 var isDouble bool 86 for _, assignee := range multipleAssignees { 87 if assignee == oneAssignee { 88 isDouble = true 89 break 90 } 91 } 92 93 if !isDouble { 94 multipleAssignees = append(multipleAssignees, oneAssignee) 95 } 96 } 97 98 // Loop through all assignees to add them 99 for _, assigneeName := range multipleAssignees { 100 assignee, err := user_model.GetUserByName(assigneeName) 101 if err != nil { 102 return err 103 } 104 105 allNewAssignees = append(allNewAssignees, assignee) 106 } 107 108 // Delete all old assignees not passed 109 if err = DeleteNotPassedAssignee(issue, doer, allNewAssignees); err != nil { 110 return err 111 } 112 113 // Add all new assignees 114 // Update the assignee. The function will check if the user exists, is already 115 // assigned (which he shouldn't as we deleted all assignees before) and 116 // has access to the repo. 117 for _, assignee := range allNewAssignees { 118 // Extra method to prevent double adding (which would result in removing) 119 err = AddAssigneeIfNotAssigned(issue, doer, assignee.ID) 120 if err != nil { 121 return err 122 } 123 } 124 125 return 126} 127 128// AddAssigneeIfNotAssigned adds an assignee only if he isn't already assigned to the issue. 129// Also checks for access of assigned user 130func AddAssigneeIfNotAssigned(issue *models.Issue, doer *user_model.User, assigneeID int64) (err error) { 131 assignee, err := user_model.GetUserByID(assigneeID) 132 if err != nil { 133 return err 134 } 135 136 // Check if the user is already assigned 137 isAssigned, err := models.IsUserAssignedToIssue(issue, assignee) 138 if err != nil { 139 return err 140 } 141 if isAssigned { 142 // nothing to to 143 return nil 144 } 145 146 valid, err := models.CanBeAssigned(assignee, issue.Repo, issue.IsPull) 147 if err != nil { 148 return err 149 } 150 if !valid { 151 return models.ErrUserDoesNotHaveAccessToRepo{UserID: assigneeID, RepoName: issue.Repo.Name} 152 } 153 154 _, _, err = ToggleAssignee(issue, doer, assigneeID) 155 if err != nil { 156 return err 157 } 158 159 return nil 160} 161 162// GetRefEndNamesAndURLs retrieves the ref end names (e.g. refs/heads/branch-name -> branch-name) 163// and their respective URLs. 164func GetRefEndNamesAndURLs(issues []*models.Issue, repoLink string) (map[int64]string, map[int64]string) { 165 var issueRefEndNames = make(map[int64]string, len(issues)) 166 var issueRefURLs = make(map[int64]string, len(issues)) 167 for _, issue := range issues { 168 if issue.Ref != "" { 169 issueRefEndNames[issue.ID] = git.RefEndName(issue.Ref) 170 issueRefURLs[issue.ID] = git.RefURL(repoLink, util.PathEscapeSegments(issue.Ref)) 171 } 172 } 173 return issueRefEndNames, issueRefURLs 174} 175