| @@ -25,7 +25,7 @@ import ( | |||
| ) | |||
| var ( | |||
| ErrMissingIssueNumber = errors.New("No issue number specified") | |||
| errMissingIssueNumber = errors.New("No issue number specified") | |||
| ) | |||
| // Issue represents an issue or pull request of repository. | |||
| @@ -62,16 +62,19 @@ type Issue struct { | |||
| Comments []*Comment `xorm:"-"` | |||
| } | |||
| // BeforeInsert ... | |||
| func (issue *Issue) BeforeInsert() { | |||
| issue.CreatedUnix = time.Now().Unix() | |||
| issue.UpdatedUnix = issue.CreatedUnix | |||
| } | |||
| // BeforeUpdate ... | |||
| func (issue *Issue) BeforeUpdate() { | |||
| issue.UpdatedUnix = time.Now().Unix() | |||
| issue.DeadlineUnix = issue.Deadline.Unix() | |||
| } | |||
| // AfterSet ... | |||
| func (issue *Issue) AfterSet(colName string, _ xorm.Cell) { | |||
| switch colName { | |||
| case "deadline_unix": | |||
| @@ -83,6 +86,7 @@ func (issue *Issue) AfterSet(colName string, _ xorm.Cell) { | |||
| } | |||
| } | |||
| // loadAttributes ... | |||
| func (issue *Issue) loadAttributes(e Engine) (err error) { | |||
| if issue.Repo == nil { | |||
| issue.Repo, err = getRepositoryByID(e, issue.RepoID) | |||
| @@ -150,10 +154,12 @@ func (issue *Issue) loadAttributes(e Engine) (err error) { | |||
| return nil | |||
| } | |||
| // LoadAttributes ... | |||
| func (issue *Issue) LoadAttributes() error { | |||
| return issue.loadAttributes(x) | |||
| } | |||
| // HTMLURL ... | |||
| func (issue *Issue) HTMLURL() string { | |||
| var path string | |||
| if issue.IsPull { | |||
| @@ -165,14 +171,14 @@ func (issue *Issue) HTMLURL() string { | |||
| } | |||
| // State returns string representation of issue status. | |||
| func (i *Issue) State() api.StateType { | |||
| if i.IsClosed { | |||
| func (issue *Issue) State() api.StateType { | |||
| if issue.IsClosed { | |||
| return api.STATE_CLOSED | |||
| } | |||
| return api.STATE_OPEN | |||
| } | |||
| // This method assumes some fields assigned with values: | |||
| // APIFormat assumes some fields assigned with values: | |||
| // Required - Poster, Labels, | |||
| // Optional - Milestone, Assignee, PullRequest | |||
| func (issue *Issue) APIFormat() *api.Issue { | |||
| @@ -213,24 +219,25 @@ func (issue *Issue) APIFormat() *api.Issue { | |||
| } | |||
| // HashTag returns unique hash tag for issue. | |||
| func (i *Issue) HashTag() string { | |||
| return "issue-" + com.ToStr(i.ID) | |||
| func (issue *Issue) HashTag() string { | |||
| return "issue-" + com.ToStr(issue.ID) | |||
| } | |||
| // IsPoster returns true if given user by ID is the poster. | |||
| func (i *Issue) IsPoster(uid int64) bool { | |||
| return i.PosterID == uid | |||
| func (issue *Issue) IsPoster(uid int64) bool { | |||
| return issue.PosterID == uid | |||
| } | |||
| func (i *Issue) hasLabel(e Engine, labelID int64) bool { | |||
| return hasIssueLabel(e, i.ID, labelID) | |||
| func (issue *Issue) hasLabel(e Engine, labelID int64) bool { | |||
| return hasIssueLabel(e, issue.ID, labelID) | |||
| } | |||
| // HasLabel returns true if issue has been labeled by given ID. | |||
| func (i *Issue) HasLabel(labelID int64) bool { | |||
| return i.hasLabel(x, labelID) | |||
| func (issue *Issue) HasLabel(labelID int64) bool { | |||
| return issue.hasLabel(x, labelID) | |||
| } | |||
| // sendLabelUpdatedWebhook ... | |||
| func (issue *Issue) sendLabelUpdatedWebhook(doer *User) { | |||
| var err error | |||
| if issue.IsPull { | |||
| @@ -254,8 +261,9 @@ func (issue *Issue) sendLabelUpdatedWebhook(doer *User) { | |||
| } | |||
| } | |||
| func (i *Issue) addLabel(e *xorm.Session, label *Label) error { | |||
| return newIssueLabel(e, i, label) | |||
| // addLabel ... | |||
| func (issue *Issue) addLabel(e *xorm.Session, label *Label) error { | |||
| return newIssueLabel(e, issue, label) | |||
| } | |||
| // AddLabel adds a new label to the issue. | |||
| @@ -268,6 +276,7 @@ func (issue *Issue) AddLabel(doer *User, label *Label) error { | |||
| return nil | |||
| } | |||
| // addLabels ... | |||
| func (issue *Issue) addLabels(e *xorm.Session, labels []*Label) error { | |||
| return newIssueLabels(e, issue, labels) | |||
| } | |||
| @@ -282,6 +291,7 @@ func (issue *Issue) AddLabels(doer *User, labels []*Label) error { | |||
| return nil | |||
| } | |||
| // getLabels ... | |||
| func (issue *Issue) getLabels(e Engine) (err error) { | |||
| if len(issue.Labels) > 0 { | |||
| return nil | |||
| @@ -294,6 +304,7 @@ func (issue *Issue) getLabels(e Engine) (err error) { | |||
| return nil | |||
| } | |||
| // removeLabel ... | |||
| func (issue *Issue) removeLabel(e *xorm.Session, label *Label) error { | |||
| return deleteIssueLabel(e, issue, label) | |||
| } | |||
| @@ -308,6 +319,7 @@ func (issue *Issue) RemoveLabel(doer *User, label *Label) error { | |||
| return nil | |||
| } | |||
| // clearLabels ... | |||
| func (issue *Issue) clearLabels(e *xorm.Session) (err error) { | |||
| if err = issue.getLabels(e); err != nil { | |||
| return fmt.Errorf("getLabels: %v", err) | |||
| @@ -322,6 +334,7 @@ func (issue *Issue) clearLabels(e *xorm.Session) (err error) { | |||
| return nil | |||
| } | |||
| // ClearLabels ... | |||
| func (issue *Issue) ClearLabels(doer *User) (err error) { | |||
| sess := x.NewSession() | |||
| defer sessionRelease(sess) | |||
| @@ -377,12 +390,13 @@ func (issue *Issue) ReplaceLabels(labels []*Label) (err error) { | |||
| return sess.Commit() | |||
| } | |||
| func (i *Issue) GetAssignee() (err error) { | |||
| if i.AssigneeID == 0 || i.Assignee != nil { | |||
| // GetAssignee ... | |||
| func (issue *Issue) GetAssignee() (err error) { | |||
| if issue.AssigneeID == 0 || issue.Assignee != nil { | |||
| return nil | |||
| } | |||
| i.Assignee, err = GetUserByID(i.AssigneeID) | |||
| issue.Assignee, err = GetUserByID(issue.AssigneeID) | |||
| if IsErrUserNotExist(err) { | |||
| return nil | |||
| } | |||
| @@ -390,8 +404,8 @@ func (i *Issue) GetAssignee() (err error) { | |||
| } | |||
| // ReadBy sets issue to be read by given user. | |||
| func (i *Issue) ReadBy(uid int64) error { | |||
| return UpdateIssueUserByRead(uid, i.ID) | |||
| func (issue *Issue) ReadBy(uid int64) error { | |||
| return UpdateIssueUserByRead(uid, issue.ID) | |||
| } | |||
| func updateIssueCols(e Engine, issue *Issue, cols ...string) error { | |||
| @@ -404,41 +418,42 @@ func UpdateIssueCols(issue *Issue, cols ...string) error { | |||
| return updateIssueCols(x, issue, cols...) | |||
| } | |||
| func (i *Issue) changeStatus(e *xorm.Session, doer *User, repo *Repository, isClosed bool) (err error) { | |||
| // changeStatus ... | |||
| func (issue *Issue) changeStatus(e *xorm.Session, doer *User, repo *Repository, isClosed bool) (err error) { | |||
| // Nothing should be performed if current status is same as target status | |||
| if i.IsClosed == isClosed { | |||
| if issue.IsClosed == isClosed { | |||
| return nil | |||
| } | |||
| i.IsClosed = isClosed | |||
| issue.IsClosed = isClosed | |||
| if err = updateIssueCols(e, i, "is_closed"); err != nil { | |||
| if err = updateIssueCols(e, issue, "is_closed"); err != nil { | |||
| return err | |||
| } else if err = updateIssueUsersByStatus(e, i.ID, isClosed); err != nil { | |||
| } else if err = updateIssueUsersByStatus(e, issue.ID, isClosed); err != nil { | |||
| return err | |||
| } | |||
| // Update issue count of labels | |||
| if err = i.getLabels(e); err != nil { | |||
| if err = issue.getLabels(e); err != nil { | |||
| return err | |||
| } | |||
| for idx := range i.Labels { | |||
| if i.IsClosed { | |||
| i.Labels[idx].NumClosedIssues++ | |||
| for idx := range issue.Labels { | |||
| if issue.IsClosed { | |||
| issue.Labels[idx].NumClosedIssues++ | |||
| } else { | |||
| i.Labels[idx].NumClosedIssues-- | |||
| issue.Labels[idx].NumClosedIssues-- | |||
| } | |||
| if err = updateLabel(e, i.Labels[idx]); err != nil { | |||
| if err = updateLabel(e, issue.Labels[idx]); err != nil { | |||
| return err | |||
| } | |||
| } | |||
| // Update issue count of milestone | |||
| if err = changeMilestoneIssueStats(e, i); err != nil { | |||
| if err = changeMilestoneIssueStats(e, issue); err != nil { | |||
| return err | |||
| } | |||
| // New action comment | |||
| if _, err = createStatusComment(e, doer, repo, i); err != nil { | |||
| if _, err = createStatusComment(e, doer, repo, issue); err != nil { | |||
| return err | |||
| } | |||
| @@ -486,6 +501,7 @@ func (issue *Issue) ChangeStatus(doer *User, repo *Repository, isClosed bool) (e | |||
| return nil | |||
| } | |||
| // ChangeTitle ... | |||
| func (issue *Issue) ChangeTitle(doer *User, title string) (err error) { | |||
| oldTitle := issue.Title | |||
| issue.Title = title | |||
| @@ -517,6 +533,7 @@ func (issue *Issue) ChangeTitle(doer *User, title string) (err error) { | |||
| return nil | |||
| } | |||
| // ChangeContent ... | |||
| func (issue *Issue) ChangeContent(doer *User, content string) (err error) { | |||
| oldContent := issue.Content | |||
| issue.Content = content | |||
| @@ -548,6 +565,7 @@ func (issue *Issue) ChangeContent(doer *User, content string) (err error) { | |||
| return nil | |||
| } | |||
| // ChangeAssignee ... | |||
| func (issue *Issue) ChangeAssignee(doer *User, assigneeID int64) (err error) { | |||
| issue.AssigneeID = assigneeID | |||
| if err = UpdateIssueUserByAssignee(issue); err != nil { | |||
| @@ -586,6 +604,7 @@ func (issue *Issue) ChangeAssignee(doer *User, assigneeID int64) (err error) { | |||
| return nil | |||
| } | |||
| // NewIssueOptions ... | |||
| type NewIssueOptions struct { | |||
| Repo *Repository | |||
| Issue *Issue | |||
| @@ -735,7 +754,7 @@ func NewIssue(repo *Repository, issue *Issue, labelIDs []int64, uuids []string) | |||
| func GetIssueByRef(ref string) (*Issue, error) { | |||
| n := strings.IndexByte(ref, byte('#')) | |||
| if n == -1 { | |||
| return nil, ErrMissingIssueNumber | |||
| return nil, errMissingIssueNumber | |||
| } | |||
| index, err := com.StrTo(ref[n+1:]).Int64() | |||
| @@ -756,7 +775,7 @@ func GetIssueByRef(ref string) (*Issue, error) { | |||
| return issue, issue.LoadAttributes() | |||
| } | |||
| // GetIssueByIndex returns raw issue without loading attributes by index in a repository. | |||
| // GetRawIssueByIndex returns raw issue without loading attributes by index in a repository. | |||
| func GetRawIssueByIndex(repoID, index int64) (*Issue, error) { | |||
| issue := &Issue{ | |||
| RepoID: repoID, | |||
| @@ -796,6 +815,7 @@ func GetIssueByID(id int64) (*Issue, error) { | |||
| return getIssueByID(x, id) | |||
| } | |||
| // IssuesOptions ... | |||
| type IssuesOptions struct { | |||
| UserID int64 | |||
| AssigneeID int64 | |||
| @@ -967,9 +987,9 @@ func NewIssueUsers(repo *Repository, issue *Issue) (err error) { | |||
| } | |||
| // PairsContains returns true when pairs list contains given issue. | |||
| func PairsContains(ius []*IssueUser, issueId, uid int64) int { | |||
| func PairsContains(ius []*IssueUser, issueID, uid int64) int { | |||
| for i := range ius { | |||
| if ius[i].IssueID == issueId && | |||
| if ius[i].IssueID == issueID && | |||
| ius[i].UID == uid { | |||
| return i | |||
| } | |||
| @@ -1092,6 +1112,7 @@ func parseCountResult(results []map[string][]byte) int64 { | |||
| return 0 | |||
| } | |||
| // IssueStatsOptions ... | |||
| type IssueStatsOptions struct { | |||
| RepoID int64 | |||
| UserID int64 | |||
| @@ -1350,10 +1371,12 @@ type Milestone struct { | |||
| ClosedDateUnix int64 | |||
| } | |||
| // BeforeInsert ... | |||
| func (m *Milestone) BeforeInsert() { | |||
| m.DeadlineUnix = m.Deadline.Unix() | |||
| } | |||
| // BeforeUpdate ... | |||
| func (m *Milestone) BeforeUpdate() { | |||
| if m.NumIssues > 0 { | |||
| m.Completeness = m.NumClosedIssues * 100 / m.NumIssues | |||
| @@ -1365,6 +1388,7 @@ func (m *Milestone) BeforeUpdate() { | |||
| m.ClosedDateUnix = m.ClosedDate.Unix() | |||
| } | |||
| // AfterSet ... | |||
| func (m *Milestone) AfterSet(colName string, _ xorm.Cell) { | |||
| switch colName { | |||
| case "num_closed_issues": | |||
| @@ -1394,6 +1418,7 @@ func (m *Milestone) State() api.StateType { | |||
| return api.STATE_OPEN | |||
| } | |||
| // APIFormat ... | |||
| func (m *Milestone) APIFormat() *api.Milestone { | |||
| apiMilestone := &api.Milestone{ | |||
| ID: m.ID, | |||
| @@ -1444,7 +1469,7 @@ func getMilestoneByRepoID(e Engine, repoID, id int64) (*Milestone, error) { | |||
| return m, nil | |||
| } | |||
| // GetWebhookByRepoID returns the milestone in a repository. | |||
| // GetMilestoneByRepoID returns the milestone in a repository. | |||
| func GetMilestoneByRepoID(repoID, id int64) (*Milestone, error) { | |||
| return getMilestoneByRepoID(x, repoID, id) | |||
| } | |||
| @@ -1676,10 +1701,12 @@ type Attachment struct { | |||
| CreatedUnix int64 | |||
| } | |||
| // BeforeInsert ... | |||
| func (a *Attachment) BeforeInsert() { | |||
| a.CreatedUnix = time.Now().Unix() | |||
| } | |||
| // AfterSet ... | |||
| func (a *Attachment) AfterSet(colName string, _ xorm.Cell) { | |||
| switch colName { | |||
| case "created_unix": | |||
| @@ -1693,8 +1720,8 @@ func AttachmentLocalPath(uuid string) string { | |||
| } | |||
| // LocalPath returns where attachment is stored in local file system. | |||
| func (attach *Attachment) LocalPath() string { | |||
| return AttachmentLocalPath(attach.UUID) | |||
| func (a *Attachment) LocalPath() string { | |||
| return AttachmentLocalPath(a.UUID) | |||
| } | |||
| // NewAttachment creates a new attachment object. | |||
| @@ -1794,8 +1821,8 @@ func DeleteAttachments(attachments []*Attachment, remove bool) (int, error) { | |||
| } | |||
| // DeleteAttachmentsByIssue deletes all attachments associated with the given issue. | |||
| func DeleteAttachmentsByIssue(issueId int64, remove bool) (int, error) { | |||
| attachments, err := GetAttachmentsByIssueID(issueId) | |||
| func DeleteAttachmentsByIssue(issueID int64, remove bool) (int, error) { | |||
| attachments, err := GetAttachmentsByIssueID(issueID) | |||
| if err != nil { | |||
| return 0, err | |||
| @@ -1805,8 +1832,8 @@ func DeleteAttachmentsByIssue(issueId int64, remove bool) (int, error) { | |||
| } | |||
| // DeleteAttachmentsByComment deletes all attachments associated with the given comment. | |||
| func DeleteAttachmentsByComment(commentId int64, remove bool) (int, error) { | |||
| attachments, err := GetAttachmentsByCommentID(commentId) | |||
| func DeleteAttachmentsByComment(commentID int64, remove bool) (int, error) { | |||
| attachments, err := GetAttachmentsByCommentID(commentID) | |||
| if err != nil { | |||
| return 0, err | |||