You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

issue.go 9.2 kB

11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. // Copyright 2014 The Gogs 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. package models
  5. import (
  6. "errors"
  7. "strings"
  8. "time"
  9. )
  10. var (
  11. ErrIssueNotExist = errors.New("Issue does not exist")
  12. )
  13. // Issue represents an issue or pull request of repository.
  14. type Issue struct {
  15. Id int64
  16. Index int64 // Index in one repository.
  17. Name string
  18. RepoId int64 `xorm:"INDEX"`
  19. Repo *Repository `xorm:"-"`
  20. PosterId int64
  21. Poster *User `xorm:"-"`
  22. MilestoneId int64
  23. AssigneeId int64
  24. IsPull bool // Indicates whether is a pull request or not.
  25. IsClosed bool
  26. Labels string `xorm:"TEXT"`
  27. Content string `xorm:"TEXT"`
  28. RenderedContent string `xorm:"-"`
  29. Priority int
  30. NumComments int
  31. Deadline time.Time
  32. Created time.Time `xorm:"CREATED"`
  33. Updated time.Time `xorm:"UPDATED"`
  34. }
  35. func (i *Issue) GetPoster() (err error) {
  36. i.Poster, err = GetUserById(i.PosterId)
  37. return err
  38. }
  39. // IssseUser represents an issue-user relation.
  40. type IssseUser struct {
  41. Id int64
  42. Iid int64 // Issue ID.
  43. Rid int64 // Repository ID.
  44. Uid int64 // User ID.
  45. IsRead bool
  46. IsAssigned bool
  47. IsMentioned bool
  48. IsClosed bool
  49. }
  50. // CreateIssue creates new issue for repository.
  51. func NewIssue(issue *Issue) (err error) {
  52. sess := orm.NewSession()
  53. defer sess.Close()
  54. if err = sess.Begin(); err != nil {
  55. return err
  56. }
  57. if _, err = sess.Insert(issue); err != nil {
  58. sess.Rollback()
  59. return err
  60. }
  61. rawSql := "UPDATE `repository` SET num_issues = num_issues + 1 WHERE id = ?"
  62. if _, err = sess.Exec(rawSql, issue.RepoId); err != nil {
  63. sess.Rollback()
  64. return err
  65. }
  66. return sess.Commit()
  67. }
  68. // GetIssueByIndex returns issue by given index in repository.
  69. func GetIssueByIndex(rid, index int64) (*Issue, error) {
  70. issue := &Issue{RepoId: rid, Index: index}
  71. has, err := orm.Get(issue)
  72. if err != nil {
  73. return nil, err
  74. } else if !has {
  75. return nil, ErrIssueNotExist
  76. }
  77. return issue, nil
  78. }
  79. // GetIssues returns a list of issues by given conditions.
  80. func GetIssues(uid, rid, pid, mid int64, page int, isClosed bool, labels, sortType string) ([]Issue, error) {
  81. sess := orm.Limit(20, (page-1)*20)
  82. if rid > 0 {
  83. sess.Where("repo_id=?", rid).And("is_closed=?", isClosed)
  84. } else {
  85. sess.Where("is_closed=?", isClosed)
  86. }
  87. if uid > 0 {
  88. sess.And("assignee_id=?", uid)
  89. } else if pid > 0 {
  90. sess.And("poster_id=?", pid)
  91. }
  92. if mid > 0 {
  93. sess.And("milestone_id=?", mid)
  94. }
  95. if len(labels) > 0 {
  96. for _, label := range strings.Split(labels, ",") {
  97. sess.And("labels like '%$" + label + "|%'")
  98. }
  99. }
  100. switch sortType {
  101. case "oldest":
  102. sess.Asc("created")
  103. case "recentupdate":
  104. sess.Desc("updated")
  105. case "leastupdate":
  106. sess.Asc("updated")
  107. case "mostcomment":
  108. sess.Desc("num_comments")
  109. case "leastcomment":
  110. sess.Asc("num_comments")
  111. default:
  112. sess.Desc("created")
  113. }
  114. var issues []Issue
  115. err := sess.Find(&issues)
  116. return issues, err
  117. }
  118. // PairsContains returns true when pairs list contains given issue.
  119. func PairsContains(ius []*IssseUser, issueId int64) bool {
  120. for i := range ius {
  121. if ius[i].Iid == issueId {
  122. return true
  123. }
  124. }
  125. return false
  126. }
  127. // GetIssueUserPairs returns all issue-user pairs by given repository and user.
  128. func GetIssueUserPairs(rid, uid int64, isClosed bool) ([]*IssseUser, error) {
  129. ius := make([]*IssseUser, 0, 10)
  130. err := orm.Find(&ius, &IssseUser{Rid: rid, Uid: uid, IsClosed: isClosed})
  131. return ius, err
  132. }
  133. // GetUserIssueCount returns the number of issues that were created by given user in repository.
  134. func GetUserIssueCount(uid, rid int64) int64 {
  135. count, _ := orm.Where("poster_id=?", uid).And("repo_id=?", rid).Count(new(Issue))
  136. return count
  137. }
  138. // IssueStats represents issue statistic information.
  139. type IssueStats struct {
  140. OpenCount, ClosedCount int64
  141. AllCount int64
  142. AssignCount int64
  143. CreateCount int64
  144. MentionCount int64
  145. }
  146. // Filter modes.
  147. const (
  148. FM_ASSIGN = iota + 1
  149. FM_CREATE
  150. FM_MENTION
  151. )
  152. // GetIssueStats returns issue statistic information by given condition.
  153. func GetIssueStats(rid, uid int64, isShowClosed bool, filterMode int) *IssueStats {
  154. stats := &IssueStats{}
  155. issue := new(Issue)
  156. sess := orm.Where("repo_id=?", rid)
  157. tmpSess := sess
  158. stats.OpenCount, _ = tmpSess.And("is_closed=?", false).Count(issue)
  159. *tmpSess = *sess
  160. stats.ClosedCount, _ = tmpSess.And("is_closed=?", true).Count(issue)
  161. if isShowClosed {
  162. stats.AllCount = stats.ClosedCount
  163. } else {
  164. stats.AllCount = stats.OpenCount
  165. }
  166. if filterMode != FM_MENTION {
  167. sess = orm.Where("repo_id=?", rid)
  168. switch filterMode {
  169. case FM_ASSIGN:
  170. sess.And("assignee_id=?", uid)
  171. case FM_CREATE:
  172. sess.And("poster_id=?", uid)
  173. default:
  174. goto nofilter
  175. }
  176. *tmpSess = *sess
  177. stats.OpenCount, _ = tmpSess.And("is_closed=?", false).Count(issue)
  178. *tmpSess = *sess
  179. stats.ClosedCount, _ = tmpSess.And("is_closed=?", true).Count(issue)
  180. } else {
  181. sess := orm.Where("rid=?", rid).And("uid=?", uid).And("is_mentioned=?", true)
  182. tmpSess := sess
  183. stats.OpenCount, _ = tmpSess.And("is_closed=?", false).Count(new(IssseUser))
  184. *tmpSess = *sess
  185. stats.ClosedCount, _ = tmpSess.And("is_closed=?", true).Count(new(IssseUser))
  186. }
  187. nofilter:
  188. stats.AssignCount, _ = orm.Where("repo_id=?", rid).And("is_closed=?", isShowClosed).And("assignee_id=?", uid).Count(issue)
  189. stats.CreateCount, _ = orm.Where("repo_id=?", rid).And("is_closed=?", isShowClosed).And("poster_id=?", uid).Count(issue)
  190. stats.MentionCount, _ = orm.Where("rid=?", rid).And("uid=?", uid).And("is_closed=?", isShowClosed).And("is_mentioned=?", true).Count(new(IssseUser))
  191. return stats
  192. }
  193. // GetUserIssueStats returns issue statistic information for dashboard by given condition.
  194. func GetUserIssueStats(uid int64, filterMode int) *IssueStats {
  195. stats := &IssueStats{}
  196. issue := new(Issue)
  197. iu := new(IssseUser)
  198. sess := orm.Where("uid=?", uid)
  199. tmpSess := sess
  200. if filterMode == 0 {
  201. stats.OpenCount, _ = tmpSess.And("is_closed=?", false).Count(iu)
  202. *tmpSess = *sess
  203. stats.ClosedCount, _ = tmpSess.And("is_closed=?", true).Count(iu)
  204. }
  205. switch filterMode {
  206. case FM_ASSIGN:
  207. sess.And("is_assigned=?", true)
  208. *tmpSess = *sess
  209. stats.OpenCount, _ = tmpSess.And("is_closed=?", false).Count(iu)
  210. *tmpSess = *sess
  211. stats.ClosedCount, _ = tmpSess.And("is_closed=?", true).Count(iu)
  212. case FM_CREATE:
  213. sess.Where("poster_id=?", uid)
  214. *tmpSess = *sess
  215. stats.OpenCount, _ = tmpSess.And("is_closed=?", false).Count(issue)
  216. *tmpSess = *sess
  217. stats.ClosedCount, _ = tmpSess.And("is_closed=?", true).Count(issue)
  218. }
  219. stats.AssignCount, _ = orm.Where("assignee_id=?", uid).And("is_closed=?", false).Count(issue)
  220. stats.CreateCount, _ = orm.Where("poster_id=?", uid).And("is_closed=?", false).Count(issue)
  221. return stats
  222. }
  223. // UpdateIssue updates information of issue.
  224. func UpdateIssue(issue *Issue) error {
  225. _, err := orm.AllCols().Update(issue)
  226. return err
  227. }
  228. // Label represents a list of labels of repository for issues.
  229. type Label struct {
  230. Id int64
  231. RepoId int64 `xorm:"INDEX"`
  232. Names string
  233. Colors string
  234. }
  235. // Milestone represents a milestone of repository.
  236. type Milestone struct {
  237. Id int64
  238. Name string
  239. RepoId int64 `xorm:"INDEX"`
  240. IsClosed bool
  241. Content string
  242. NumIssues int
  243. DueDate time.Time
  244. Created time.Time `xorm:"CREATED"`
  245. }
  246. // Issue types.
  247. const (
  248. IT_PLAIN = iota // Pure comment.
  249. IT_REOPEN // Issue reopen status change prompt.
  250. IT_CLOSE // Issue close status change prompt.
  251. )
  252. // Comment represents a comment in commit and issue page.
  253. type Comment struct {
  254. Id int64
  255. Type int
  256. PosterId int64
  257. Poster *User `xorm:"-"`
  258. IssueId int64
  259. CommitId int64
  260. Line int64
  261. Content string
  262. Created time.Time `xorm:"CREATED"`
  263. }
  264. // CreateComment creates comment of issue or commit.
  265. func CreateComment(userId, repoId, issueId, commitId, line int64, cmtType int, content string) error {
  266. sess := orm.NewSession()
  267. defer sess.Close()
  268. sess.Begin()
  269. if _, err := sess.Insert(&Comment{PosterId: userId, Type: cmtType, IssueId: issueId,
  270. CommitId: commitId, Line: line, Content: content}); err != nil {
  271. sess.Rollback()
  272. return err
  273. }
  274. // Check comment type.
  275. switch cmtType {
  276. case IT_PLAIN:
  277. rawSql := "UPDATE `issue` SET num_comments = num_comments + 1 WHERE id = ?"
  278. if _, err := sess.Exec(rawSql, issueId); err != nil {
  279. sess.Rollback()
  280. return err
  281. }
  282. case IT_REOPEN:
  283. rawSql := "UPDATE `repository` SET num_closed_issues = num_closed_issues - 1 WHERE id = ?"
  284. if _, err := sess.Exec(rawSql, repoId); err != nil {
  285. sess.Rollback()
  286. return err
  287. }
  288. case IT_CLOSE:
  289. rawSql := "UPDATE `repository` SET num_closed_issues = num_closed_issues + 1 WHERE id = ?"
  290. if _, err := sess.Exec(rawSql, repoId); err != nil {
  291. sess.Rollback()
  292. return err
  293. }
  294. }
  295. return sess.Commit()
  296. }
  297. // GetIssueComments returns list of comment by given issue id.
  298. func GetIssueComments(issueId int64) ([]Comment, error) {
  299. comments := make([]Comment, 0, 10)
  300. err := orm.Asc("created").Find(&comments, &Comment{IssueId: issueId})
  301. return comments, err
  302. }