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 19 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
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
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720
  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. "bytes"
  7. "errors"
  8. "strings"
  9. "time"
  10. "github.com/go-xorm/xorm"
  11. "github.com/gogits/gogs/modules/base"
  12. )
  13. var (
  14. ErrIssueNotExist = errors.New("Issue does not exist")
  15. ErrMilestoneNotExist = errors.New("Milestone does not exist")
  16. )
  17. // Issue represents an issue or pull request of repository.
  18. type Issue struct {
  19. Id int64
  20. RepoId int64 `xorm:"INDEX"`
  21. Index int64 // Index in one repository.
  22. Name string
  23. Repo *Repository `xorm:"-"`
  24. PosterId int64
  25. Poster *User `xorm:"-"`
  26. MilestoneId int64
  27. AssigneeId int64
  28. Assignee *User `xorm:"-"`
  29. IsRead bool `xorm:"-"`
  30. IsPull bool // Indicates whether is a pull request or not.
  31. IsClosed bool
  32. Labels string `xorm:"TEXT"`
  33. Content string `xorm:"TEXT"`
  34. RenderedContent string `xorm:"-"`
  35. Priority int
  36. NumComments int
  37. Deadline time.Time
  38. Created time.Time `xorm:"CREATED"`
  39. Updated time.Time `xorm:"UPDATED"`
  40. }
  41. func (i *Issue) GetPoster() (err error) {
  42. i.Poster, err = GetUserById(i.PosterId)
  43. if err == ErrUserNotExist {
  44. i.Poster = &User{Name: "FakeUser"}
  45. return nil
  46. }
  47. return err
  48. }
  49. func (i *Issue) GetAssignee() (err error) {
  50. if i.AssigneeId == 0 {
  51. return nil
  52. }
  53. i.Assignee, err = GetUserById(i.AssigneeId)
  54. return err
  55. }
  56. // CreateIssue creates new issue for repository.
  57. func NewIssue(issue *Issue) (err error) {
  58. sess := orm.NewSession()
  59. defer sess.Close()
  60. if err = sess.Begin(); err != nil {
  61. return err
  62. }
  63. if _, err = sess.Insert(issue); err != nil {
  64. sess.Rollback()
  65. return err
  66. }
  67. rawSql := "UPDATE `repository` SET num_issues = num_issues + 1 WHERE id = ?"
  68. if _, err = sess.Exec(rawSql, issue.RepoId); err != nil {
  69. sess.Rollback()
  70. return err
  71. }
  72. return sess.Commit()
  73. }
  74. // GetIssueByIndex returns issue by given index in repository.
  75. func GetIssueByIndex(rid, index int64) (*Issue, error) {
  76. issue := &Issue{RepoId: rid, Index: index}
  77. has, err := orm.Get(issue)
  78. if err != nil {
  79. return nil, err
  80. } else if !has {
  81. return nil, ErrIssueNotExist
  82. }
  83. return issue, nil
  84. }
  85. // GetIssueById returns an issue by ID.
  86. func GetIssueById(id int64) (*Issue, error) {
  87. issue := &Issue{Id: id}
  88. has, err := orm.Get(issue)
  89. if err != nil {
  90. return nil, err
  91. } else if !has {
  92. return nil, ErrIssueNotExist
  93. }
  94. return issue, nil
  95. }
  96. // GetIssues returns a list of issues by given conditions.
  97. func GetIssues(uid, rid, pid, mid int64, page int, isClosed bool, labels, sortType string) ([]Issue, error) {
  98. sess := orm.Limit(20, (page-1)*20)
  99. if rid > 0 {
  100. sess.Where("repo_id=?", rid).And("is_closed=?", isClosed)
  101. } else {
  102. sess.Where("is_closed=?", isClosed)
  103. }
  104. if uid > 0 {
  105. sess.And("assignee_id=?", uid)
  106. } else if pid > 0 {
  107. sess.And("poster_id=?", pid)
  108. }
  109. if mid > 0 {
  110. sess.And("milestone_id=?", mid)
  111. }
  112. if len(labels) > 0 {
  113. for _, label := range strings.Split(labels, ",") {
  114. sess.And("labels like '%$" + label + "|%'")
  115. }
  116. }
  117. switch sortType {
  118. case "oldest":
  119. sess.Asc("created")
  120. case "recentupdate":
  121. sess.Desc("updated")
  122. case "leastupdate":
  123. sess.Asc("updated")
  124. case "mostcomment":
  125. sess.Desc("num_comments")
  126. case "leastcomment":
  127. sess.Asc("num_comments")
  128. case "priority":
  129. sess.Desc("priority")
  130. default:
  131. sess.Desc("created")
  132. }
  133. var issues []Issue
  134. err := sess.Find(&issues)
  135. return issues, err
  136. }
  137. // GetIssueCountByPoster returns number of issues of repository by poster.
  138. func GetIssueCountByPoster(uid, rid int64, isClosed bool) int64 {
  139. count, _ := orm.Where("repo_id=?", rid).And("poster_id=?", uid).And("is_closed=?", isClosed).Count(new(Issue))
  140. return count
  141. }
  142. // .___ ____ ___
  143. // | | ______ ________ __ ____ | | \______ ___________
  144. // | |/ ___// ___/ | \_/ __ \| | / ___// __ \_ __ \
  145. // | |\___ \ \___ \| | /\ ___/| | /\___ \\ ___/| | \/
  146. // |___/____ >____ >____/ \___ >______//____ >\___ >__|
  147. // \/ \/ \/ \/ \/
  148. // IssueUser represents an issue-user relation.
  149. type IssueUser struct {
  150. Id int64
  151. Uid int64 // User ID.
  152. IssueId int64
  153. RepoId int64
  154. MilestoneId int64
  155. Labels string `xorm:"TEXT"`
  156. IsRead bool
  157. IsAssigned bool
  158. IsMentioned bool
  159. IsPoster bool
  160. IsClosed bool
  161. }
  162. // NewIssueUserPairs adds new issue-user pairs for new issue of repository.
  163. func NewIssueUserPairs(rid, iid, oid, pid, aid int64, repoName string) (err error) {
  164. iu := &IssueUser{IssueId: iid, RepoId: rid}
  165. us, err := GetCollaborators(repoName)
  166. if err != nil {
  167. return err
  168. }
  169. isNeedAddPoster := true
  170. for _, u := range us {
  171. iu.Uid = u.Id
  172. iu.IsPoster = iu.Uid == pid
  173. if isNeedAddPoster && iu.IsPoster {
  174. isNeedAddPoster = false
  175. }
  176. iu.IsAssigned = iu.Uid == aid
  177. if _, err = orm.Insert(iu); err != nil {
  178. return err
  179. }
  180. }
  181. if isNeedAddPoster {
  182. iu.Uid = pid
  183. iu.IsPoster = true
  184. iu.IsAssigned = iu.Uid == aid
  185. if _, err = orm.Insert(iu); err != nil {
  186. return err
  187. }
  188. }
  189. return nil
  190. }
  191. // PairsContains returns true when pairs list contains given issue.
  192. func PairsContains(ius []*IssueUser, issueId int64) int {
  193. for i := range ius {
  194. if ius[i].IssueId == issueId {
  195. return i
  196. }
  197. }
  198. return -1
  199. }
  200. // GetIssueUserPairs returns issue-user pairs by given repository and user.
  201. func GetIssueUserPairs(rid, uid int64, isClosed bool) ([]*IssueUser, error) {
  202. ius := make([]*IssueUser, 0, 10)
  203. err := orm.Where("is_closed=?", isClosed).Find(&ius, &IssueUser{RepoId: rid, Uid: uid})
  204. return ius, err
  205. }
  206. // GetIssueUserPairsByRepoIds returns issue-user pairs by given repository IDs.
  207. func GetIssueUserPairsByRepoIds(rids []int64, isClosed bool, page int) ([]*IssueUser, error) {
  208. buf := bytes.NewBufferString("")
  209. for _, rid := range rids {
  210. buf.WriteString("repo_id=")
  211. buf.WriteString(base.ToStr(rid))
  212. buf.WriteString(" OR ")
  213. }
  214. cond := strings.TrimSuffix(buf.String(), " OR ")
  215. ius := make([]*IssueUser, 0, 10)
  216. sess := orm.Limit(20, (page-1)*20).Where("is_closed=?", isClosed)
  217. if len(cond) > 0 {
  218. sess.And(cond)
  219. }
  220. err := sess.Find(&ius)
  221. return ius, err
  222. }
  223. // GetIssueUserPairsByMode returns issue-user pairs by given repository and user.
  224. func GetIssueUserPairsByMode(uid, rid int64, isClosed bool, page, filterMode int) ([]*IssueUser, error) {
  225. ius := make([]*IssueUser, 0, 10)
  226. sess := orm.Limit(20, (page-1)*20).Where("uid=?", uid).And("is_closed=?", isClosed)
  227. if rid > 0 {
  228. sess.And("repo_id=?", rid)
  229. }
  230. switch filterMode {
  231. case FM_ASSIGN:
  232. sess.And("is_assigned=?", true)
  233. case FM_CREATE:
  234. sess.And("is_poster=?", true)
  235. default:
  236. return ius, nil
  237. }
  238. err := sess.Find(&ius)
  239. return ius, err
  240. }
  241. // IssueStats represents issue statistic information.
  242. type IssueStats struct {
  243. OpenCount, ClosedCount int64
  244. AllCount int64
  245. AssignCount int64
  246. CreateCount int64
  247. MentionCount int64
  248. }
  249. // Filter modes.
  250. const (
  251. FM_ASSIGN = iota + 1
  252. FM_CREATE
  253. FM_MENTION
  254. )
  255. // GetIssueStats returns issue statistic information by given conditions.
  256. func GetIssueStats(rid, uid int64, isShowClosed bool, filterMode int) *IssueStats {
  257. stats := &IssueStats{}
  258. issue := new(Issue)
  259. tmpSess := &xorm.Session{}
  260. sess := orm.Where("repo_id=?", rid)
  261. *tmpSess = *sess
  262. stats.OpenCount, _ = tmpSess.And("is_closed=?", false).Count(issue)
  263. *tmpSess = *sess
  264. stats.ClosedCount, _ = tmpSess.And("is_closed=?", true).Count(issue)
  265. if isShowClosed {
  266. stats.AllCount = stats.ClosedCount
  267. } else {
  268. stats.AllCount = stats.OpenCount
  269. }
  270. if filterMode != FM_MENTION {
  271. sess = orm.Where("repo_id=?", rid)
  272. switch filterMode {
  273. case FM_ASSIGN:
  274. sess.And("assignee_id=?", uid)
  275. case FM_CREATE:
  276. sess.And("poster_id=?", uid)
  277. default:
  278. goto nofilter
  279. }
  280. *tmpSess = *sess
  281. stats.OpenCount, _ = tmpSess.And("is_closed=?", false).Count(issue)
  282. *tmpSess = *sess
  283. stats.ClosedCount, _ = tmpSess.And("is_closed=?", true).Count(issue)
  284. } else {
  285. sess := orm.Where("repo_id=?", rid).And("uid=?", uid).And("is_mentioned=?", true)
  286. *tmpSess = *sess
  287. stats.OpenCount, _ = tmpSess.And("is_closed=?", false).Count(new(IssueUser))
  288. *tmpSess = *sess
  289. stats.ClosedCount, _ = tmpSess.And("is_closed=?", true).Count(new(IssueUser))
  290. }
  291. nofilter:
  292. stats.AssignCount, _ = orm.Where("repo_id=?", rid).And("is_closed=?", isShowClosed).And("assignee_id=?", uid).Count(issue)
  293. stats.CreateCount, _ = orm.Where("repo_id=?", rid).And("is_closed=?", isShowClosed).And("poster_id=?", uid).Count(issue)
  294. stats.MentionCount, _ = orm.Where("repo_id=?", rid).And("uid=?", uid).And("is_closed=?", isShowClosed).And("is_mentioned=?", true).Count(new(IssueUser))
  295. return stats
  296. }
  297. // GetUserIssueStats returns issue statistic information for dashboard by given conditions.
  298. func GetUserIssueStats(uid int64, filterMode int) *IssueStats {
  299. stats := &IssueStats{}
  300. issue := new(Issue)
  301. stats.AssignCount, _ = orm.Where("assignee_id=?", uid).And("is_closed=?", false).Count(issue)
  302. stats.CreateCount, _ = orm.Where("poster_id=?", uid).And("is_closed=?", false).Count(issue)
  303. return stats
  304. }
  305. // UpdateIssue updates information of issue.
  306. func UpdateIssue(issue *Issue) error {
  307. _, err := orm.Id(issue.Id).AllCols().Update(issue)
  308. return err
  309. }
  310. // UpdateIssueUserByStatus updates issue-user pairs by issue status.
  311. func UpdateIssueUserPairsByStatus(iid int64, isClosed bool) error {
  312. rawSql := "UPDATE `issue_user` SET is_closed = ? WHERE issue_id = ?"
  313. _, err := orm.Exec(rawSql, isClosed, iid)
  314. return err
  315. }
  316. // UpdateIssueUserPairByAssignee updates issue-user pair for assigning.
  317. func UpdateIssueUserPairByAssignee(aid, iid int64) error {
  318. rawSql := "UPDATE `issue_user` SET is_assigned = ? WHERE issue_id = ?"
  319. if _, err := orm.Exec(rawSql, false, iid); err != nil {
  320. return err
  321. }
  322. // Assignee ID equals to 0 means clear assignee.
  323. if aid == 0 {
  324. return nil
  325. }
  326. rawSql = "UPDATE `issue_user` SET is_assigned = true WHERE uid = ? AND issue_id = ?"
  327. _, err := orm.Exec(rawSql, aid, iid)
  328. return err
  329. }
  330. // UpdateIssueUserPairByRead updates issue-user pair for reading.
  331. func UpdateIssueUserPairByRead(uid, iid int64) error {
  332. rawSql := "UPDATE `issue_user` SET is_read = ? WHERE uid = ? AND issue_id = ?"
  333. _, err := orm.Exec(rawSql, true, uid, iid)
  334. return err
  335. }
  336. // UpdateIssueUserPairsByMentions updates issue-user pairs by mentioning.
  337. func UpdateIssueUserPairsByMentions(uids []int64, iid int64) error {
  338. for _, uid := range uids {
  339. iu := &IssueUser{Uid: uid, IssueId: iid}
  340. has, err := orm.Get(iu)
  341. if err != nil {
  342. return err
  343. }
  344. iu.IsMentioned = true
  345. if has {
  346. _, err = orm.Id(iu.Id).AllCols().Update(iu)
  347. } else {
  348. _, err = orm.Insert(iu)
  349. }
  350. if err != nil {
  351. return err
  352. }
  353. }
  354. return nil
  355. }
  356. // _____ .__.__ __
  357. // / \ |__| | ____ _______/ |_ ____ ____ ____
  358. // / \ / \| | | _/ __ \ / ___/\ __\/ _ \ / \_/ __ \
  359. // / Y \ | |_\ ___/ \___ \ | | ( <_> ) | \ ___/
  360. // \____|__ /__|____/\___ >____ > |__| \____/|___| /\___ >
  361. // \/ \/ \/ \/ \/
  362. // Milestone represents a milestone of repository.
  363. type Milestone struct {
  364. Id int64
  365. RepoId int64 `xorm:"INDEX"`
  366. Index int64
  367. Name string
  368. Content string
  369. RenderedContent string `xorm:"-"`
  370. IsClosed bool
  371. NumIssues int
  372. NumClosedIssues int
  373. NumOpenIssues int `xorm:"-"`
  374. Completeness int // Percentage(1-100).
  375. Deadline time.Time
  376. DeadlineString string `xorm:"-"`
  377. ClosedDate time.Time
  378. }
  379. // CalOpenIssues calculates the open issues of milestone.
  380. func (m *Milestone) CalOpenIssues() {
  381. m.NumOpenIssues = m.NumIssues - m.NumClosedIssues
  382. }
  383. // NewMilestone creates new milestone of repository.
  384. func NewMilestone(m *Milestone) (err error) {
  385. sess := orm.NewSession()
  386. defer sess.Close()
  387. if err = sess.Begin(); err != nil {
  388. return err
  389. }
  390. if _, err = sess.Insert(m); err != nil {
  391. sess.Rollback()
  392. return err
  393. }
  394. rawSql := "UPDATE `repository` SET num_milestones = num_milestones + 1 WHERE id = ?"
  395. if _, err = sess.Exec(rawSql, m.RepoId); err != nil {
  396. sess.Rollback()
  397. return err
  398. }
  399. return sess.Commit()
  400. }
  401. // GetMilestoneById returns the milestone by given ID.
  402. func GetMilestoneById(id int64) (*Milestone, error) {
  403. m := &Milestone{Id: id}
  404. has, err := orm.Get(m)
  405. if err != nil {
  406. return nil, err
  407. } else if !has {
  408. return nil, ErrMilestoneNotExist
  409. }
  410. return m, nil
  411. }
  412. // GetMilestoneByIndex returns the milestone of given repository and index.
  413. func GetMilestoneByIndex(repoId, idx int64) (*Milestone, error) {
  414. m := &Milestone{RepoId: repoId, Index: idx}
  415. has, err := orm.Get(m)
  416. if err != nil {
  417. return nil, err
  418. } else if !has {
  419. return nil, ErrMilestoneNotExist
  420. }
  421. return m, nil
  422. }
  423. // GetMilestones returns a list of milestones of given repository and status.
  424. func GetMilestones(repoId int64, isClosed bool) ([]*Milestone, error) {
  425. miles := make([]*Milestone, 0, 10)
  426. err := orm.Where("repo_id=?", repoId).And("is_closed=?", isClosed).Find(&miles)
  427. return miles, err
  428. }
  429. // UpdateMilestone updates information of given milestone.
  430. func UpdateMilestone(m *Milestone) error {
  431. _, err := orm.Id(m.Id).Update(m)
  432. return err
  433. }
  434. // ChangeMilestoneStatus changes the milestone open/closed status.
  435. func ChangeMilestoneStatus(m *Milestone, isClosed bool) (err error) {
  436. repo, err := GetRepositoryById(m.RepoId)
  437. if err != nil {
  438. return err
  439. }
  440. sess := orm.NewSession()
  441. defer sess.Close()
  442. if err = sess.Begin(); err != nil {
  443. return err
  444. }
  445. m.IsClosed = isClosed
  446. if _, err = sess.Id(m.Id).AllCols().Update(m); err != nil {
  447. sess.Rollback()
  448. return err
  449. }
  450. if isClosed {
  451. repo.NumClosedMilestones++
  452. } else {
  453. repo.NumClosedMilestones--
  454. }
  455. if _, err = sess.Id(repo.Id).Update(repo); err != nil {
  456. sess.Rollback()
  457. return err
  458. }
  459. return sess.Commit()
  460. }
  461. // ChangeMilestoneAssign changes assignment of milestone for issue.
  462. func ChangeMilestoneAssign(oldMid, mid int64, issue *Issue) (err error) {
  463. sess := orm.NewSession()
  464. defer sess.Close()
  465. if err = sess.Begin(); err != nil {
  466. return err
  467. }
  468. if oldMid > 0 {
  469. m, err := GetMilestoneById(oldMid)
  470. if err != nil {
  471. return err
  472. }
  473. m.NumIssues--
  474. if issue.IsClosed {
  475. m.NumClosedIssues--
  476. }
  477. if m.NumIssues > 0 {
  478. m.Completeness = m.NumClosedIssues * 100 / m.NumIssues
  479. } else {
  480. m.Completeness = 0
  481. }
  482. if _, err = sess.Id(m.Id).Update(m); err != nil {
  483. sess.Rollback()
  484. return err
  485. }
  486. rawSql := "UPDATE `issue_user` SET milestone_id = 0 WHERE issue_id = ?"
  487. if _, err = sess.Exec(rawSql, issue.Id); err != nil {
  488. sess.Rollback()
  489. return err
  490. }
  491. }
  492. if mid > 0 {
  493. m, err := GetMilestoneById(mid)
  494. if err != nil {
  495. return err
  496. }
  497. m.NumIssues++
  498. if issue.IsClosed {
  499. m.NumClosedIssues++
  500. }
  501. m.Completeness = m.NumClosedIssues * 100 / m.NumIssues
  502. if _, err = sess.Id(m.Id).Update(m); err != nil {
  503. sess.Rollback()
  504. return err
  505. }
  506. rawSql := "UPDATE `issue_user` SET milestone_id = ? WHERE issue_id = ?"
  507. if _, err = sess.Exec(rawSql, m.Id, issue.Id); err != nil {
  508. sess.Rollback()
  509. return err
  510. }
  511. }
  512. return sess.Commit()
  513. }
  514. // DeleteMilestone deletes a milestone.
  515. func DeleteMilestone(m *Milestone) (err error) {
  516. sess := orm.NewSession()
  517. defer sess.Close()
  518. if err = sess.Begin(); err != nil {
  519. return err
  520. }
  521. if _, err = sess.Delete(m); err != nil {
  522. sess.Rollback()
  523. return err
  524. }
  525. rawSql := "UPDATE `repository` SET num_milestones = num_milestones - 1 WHERE id = ?"
  526. if _, err = sess.Exec(rawSql, m.RepoId); err != nil {
  527. sess.Rollback()
  528. return err
  529. }
  530. rawSql = "UPDATE `issue` SET milestone_id = 0 WHERE milestone_id = ?"
  531. if _, err = sess.Exec(rawSql, m.Id); err != nil {
  532. sess.Rollback()
  533. return err
  534. }
  535. rawSql = "UPDATE `issue_user` SET milestone_id = 0 WHERE milestone_id = ?"
  536. if _, err = sess.Exec(rawSql, m.Id); err != nil {
  537. sess.Rollback()
  538. return err
  539. }
  540. return sess.Commit()
  541. }
  542. // .____ ___. .__
  543. // | | _____ \_ |__ ____ | |
  544. // | | \__ \ | __ \_/ __ \| |
  545. // | |___ / __ \| \_\ \ ___/| |__
  546. // |_______ (____ /___ /\___ >____/
  547. // \/ \/ \/ \/
  548. // Label represents a label of repository for issues.
  549. type Label struct {
  550. Id int64
  551. RepoId int64 `xorm:"INDEX"`
  552. Name string
  553. Color string `xorm:"VARCHAR(7)"`
  554. NumIssues int
  555. NumClosedIssues int
  556. NumOpenIssues int `xorm:"-"`
  557. }
  558. // CalOpenIssues calculates the open issues of label.
  559. func (m *Label) CalOpenIssues() {
  560. m.NumOpenIssues = m.NumIssues - m.NumClosedIssues
  561. }
  562. // NewLabel creates new label of repository.
  563. func NewLabel(l *Label) error {
  564. _, err := orm.Insert(l)
  565. return err
  566. }
  567. // GetLabels returns a list of labels of given repository ID.
  568. func GetLabels(repoId int64) ([]*Label, error) {
  569. labels := make([]*Label, 0, 10)
  570. err := orm.Where("repo_id=?", repoId).Find(&labels)
  571. return labels, err
  572. }
  573. // _________ __
  574. // \_ ___ \ ____ _____ _____ ____ _____/ |_
  575. // / \ \/ / _ \ / \ / \_/ __ \ / \ __\
  576. // \ \___( <_> ) Y Y \ Y Y \ ___/| | \ |
  577. // \______ /\____/|__|_| /__|_| /\___ >___| /__|
  578. // \/ \/ \/ \/ \/
  579. // Issue types.
  580. const (
  581. IT_PLAIN = iota // Pure comment.
  582. IT_REOPEN // Issue reopen status change prompt.
  583. IT_CLOSE // Issue close status change prompt.
  584. )
  585. // Comment represents a comment in commit and issue page.
  586. type Comment struct {
  587. Id int64
  588. Type int
  589. PosterId int64
  590. Poster *User `xorm:"-"`
  591. IssueId int64
  592. CommitId int64
  593. Line int64
  594. Content string
  595. Created time.Time `xorm:"CREATED"`
  596. }
  597. // CreateComment creates comment of issue or commit.
  598. func CreateComment(userId, repoId, issueId, commitId, line int64, cmtType int, content string) error {
  599. sess := orm.NewSession()
  600. defer sess.Close()
  601. if err := sess.Begin(); err != nil {
  602. return err
  603. }
  604. if _, err := sess.Insert(&Comment{PosterId: userId, Type: cmtType, IssueId: issueId,
  605. CommitId: commitId, Line: line, Content: content}); err != nil {
  606. sess.Rollback()
  607. return err
  608. }
  609. // Check comment type.
  610. switch cmtType {
  611. case IT_PLAIN:
  612. rawSql := "UPDATE `issue` SET num_comments = num_comments + 1 WHERE id = ?"
  613. if _, err := sess.Exec(rawSql, issueId); err != nil {
  614. sess.Rollback()
  615. return err
  616. }
  617. case IT_REOPEN:
  618. rawSql := "UPDATE `repository` SET num_closed_issues = num_closed_issues - 1 WHERE id = ?"
  619. if _, err := sess.Exec(rawSql, repoId); err != nil {
  620. sess.Rollback()
  621. return err
  622. }
  623. case IT_CLOSE:
  624. rawSql := "UPDATE `repository` SET num_closed_issues = num_closed_issues + 1 WHERE id = ?"
  625. if _, err := sess.Exec(rawSql, repoId); err != nil {
  626. sess.Rollback()
  627. return err
  628. }
  629. }
  630. return sess.Commit()
  631. }
  632. // GetIssueComments returns list of comment by given issue id.
  633. func GetIssueComments(issueId int64) ([]Comment, error) {
  634. comments := make([]Comment, 0, 10)
  635. err := orm.Asc("created").Find(&comments, &Comment{IssueId: issueId})
  636. return comments, err
  637. }