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