diff options
Diffstat (limited to 'models/issue.go')
-rw-r--r-- | models/issue.go | 525 |
1 files changed, 342 insertions, 183 deletions
diff --git a/models/issue.go b/models/issue.go index 1e528d86..79363ce5 100644 --- a/models/issue.go +++ b/models/issue.go @@ -7,6 +7,7 @@ package models import ( "bytes" "errors" + "fmt" "html/template" "os" "strconv" @@ -16,13 +17,13 @@ import ( "github.com/Unknwon/com" "github.com/go-xorm/xorm" + "github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/log" "github.com/gogits/gogs/modules/setting" ) var ( ErrIssueNotExist = errors.New("Issue does not exist") - ErrLabelNotExist = errors.New("Label does not exist") ErrWrongIssueCounter = errors.New("Invalid number of issues for this milestone") ErrAttachmentNotExist = errors.New("Attachment does not exist") ErrAttachmentNotLinked = errors.New("Attachment does not belong to this issue") @@ -38,7 +39,6 @@ type Issue struct { Repo *Repository `xorm:"-"` PosterID int64 Poster *User `xorm:"-"` - LabelIds string `xorm:"TEXT"` Labels []*Label `xorm:"-"` MilestoneID int64 Milestone *Milestone `xorm:"-"` @@ -60,15 +60,28 @@ func (i *Issue) AfterSet(colName string, _ xorm.Cell) { var err error switch colName { case "milestone_id": + if i.MilestoneID == 0 { + return + } + i.Milestone, err = GetMilestoneByID(i.MilestoneID) if err != nil { log.Error(3, "GetMilestoneById: %v", err) } + case "assignee_id": + if i.AssigneeID == 0 { + return + } + + i.Assignee, err = GetUserByID(i.AssigneeID) + if err != nil { + log.Error(3, "GetUserByID: %v", err) + } } } func (i *Issue) GetPoster() (err error) { - i.Poster, err = GetUserById(i.PosterID) + i.Poster, err = GetUserByID(i.PosterID) if IsErrUserNotExist(err) { i.Poster = &User{Name: "FakeUser"} return nil @@ -76,35 +89,56 @@ func (i *Issue) GetPoster() (err error) { return err } -func (i *Issue) GetLabels() error { - if len(i.LabelIds) < 3 { +func (i *Issue) hasLabel(e Engine, labelID int64) bool { + return hasIssueLabel(e, i.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 (i *Issue) addLabel(e Engine, labelID int64) error { + return newIssueLabel(e, i.ID, labelID) +} + +// AddLabel adds new label to issue by given ID. +func (i *Issue) AddLabel(labelID int64) error { + return i.addLabel(x, labelID) +} + +func (i *Issue) getLabels(e Engine) (err error) { + if len(i.Labels) > 0 { return nil } - strIds := strings.Split(strings.TrimSuffix(i.LabelIds[1:], "|"), "|$") - i.Labels = make([]*Label, 0, len(strIds)) - for _, strId := range strIds { - id := com.StrTo(strId).MustInt64() - if id > 0 { - l, err := GetLabelById(id) - if err != nil { - if err == ErrLabelNotExist { - continue - } - return err - } - i.Labels = append(i.Labels, l) - } + i.Labels, err = getLabelsByIssueID(e, i.ID) + if err != nil { + return fmt.Errorf("getLabelsByIssueID: %v", err) } return nil } +// GetLabels retrieves all labels of issue and assign to corresponding field. +func (i *Issue) GetLabels() error { + return i.getLabels(x) +} + +func (i *Issue) removeLabel(e Engine, labelID int64) error { + return deleteIssueLabel(e, i.ID, labelID) +} + +// RemoveLabel removes a label from issue by given ID. +func (i *Issue) RemoveLabel(labelID int64) error { + return i.removeLabel(x, labelID) +} + func (i *Issue) GetAssignee() (err error) { - if i.AssigneeID == 0 { + if i.AssigneeID == 0 || i.Assignee != nil { return nil } - i.Assignee, err = GetUserById(i.AssigneeID) + i.Assignee, err = GetUserByID(i.AssigneeID) if IsErrUserNotExist(err) { return nil } @@ -124,8 +158,8 @@ func (i *Issue) AfterDelete() { } } -// CreateIssue creates new issue for repository. -func NewIssue(issue *Issue) (err error) { +// CreateIssue creates new issue with labels for repository. +func NewIssue(repo *Repository, issue *Issue, labelIDs []int64) (err error) { sess := x.NewSession() defer sessionRelease(sess) if err = sess.Begin(); err != nil { @@ -134,20 +168,27 @@ func NewIssue(issue *Issue) (err error) { if _, err = sess.Insert(issue); err != nil { return err - } else if _, err = sess.Exec("UPDATE `repository` SET num_issues = num_issues + 1 WHERE id = ?", issue.RepoID); err != nil { + } else if _, err = sess.Exec("UPDATE `repository` SET num_issues=num_issues+1 WHERE id=?", issue.RepoID); err != nil { return err } - if err = sess.Commit(); err != nil { - return err + for _, id := range labelIDs { + if err = issue.addLabel(sess, id); err != nil { + return fmt.Errorf("addLabel: %v", err) + } } if issue.MilestoneID > 0 { - // FIXES(280): Update milestone counter. - return ChangeMilestoneAssign(0, issue.MilestoneID, issue) + if err = changeMilestoneAssign(sess, 0, issue); err != nil { + return err + } } - return + if err = newIssueUsers(sess, repo, issue); err != nil { + return err + } + + return sess.Commit() } // GetIssueByRef returns an Issue specified by a GFM reference. @@ -170,7 +211,7 @@ func GetIssueByRef(ref string) (issue *Issue, err error) { return } - return GetIssueByIndex(repo.Id, issueNumber) + return GetIssueByIndex(repo.ID, issueNumber) } // GetIssueByIndex returns issue by given index in repository. @@ -198,7 +239,7 @@ func GetIssueById(id int64) (*Issue, error) { } // Issues returns a list of issues by given conditions. -func Issues(uid, assigneeID, repoID, posterID, milestoneID int64, page int, isClosed, isMention bool, labelIds, sortType string) ([]*Issue, error) { +func Issues(uid, assigneeID, repoID, posterID, milestoneID int64, page int, isClosed, isMention bool, labels, sortType string) ([]*Issue, error) { sess := x.Limit(setting.IssuePagingNum, (page-1)*setting.IssuePagingNum) if repoID > 0 { @@ -217,14 +258,6 @@ func Issues(uid, assigneeID, repoID, posterID, milestoneID int64, page int, isCl sess.And("issue.milestone_id=?", milestoneID) } - if len(labelIds) > 0 { - for _, label := range strings.Split(labelIds, ",") { - if com.StrTo(label).MustInt() > 0 { - sess.And("label_ids like ?", "%$"+label+"|%") - } - } - } - switch sortType { case "oldest": sess.Asc("created") @@ -242,10 +275,26 @@ func Issues(uid, assigneeID, repoID, posterID, milestoneID int64, page int, isCl sess.Desc("created") } + labelIDs := base.StringsToInt64s(strings.Split(labels, ",")) + if len(labelIDs) > 0 { + validJoin := false + queryStr := "issue.id=issue_label.issue_id" + for _, id := range labelIDs { + if id == 0 { + continue + } + validJoin = true + queryStr += " AND issue_label.label_id=" + com.ToStr(id) + } + if validJoin { + sess.Join("INNER", "issue_label", queryStr) + } + } + if isMention { - queryStr := "issue.id = issue_user.issue_id AND issue_user.is_mentioned=1" + queryStr := "issue.id=issue_user.issue_id AND issue_user.is_mentioned=1" if uid > 0 { - queryStr += " AND issue_user.uid = " + com.ToStr(uid) + queryStr += " AND issue_user.uid=" + com.ToStr(uid) } sess.Join("INNER", "issue_user", queryStr) } @@ -261,12 +310,6 @@ const ( IS_CLOSE ) -// GetIssuesByLabel returns a list of issues by given label and repository. -func GetIssuesByLabel(repoID, labelID int64) ([]*Issue, error) { - issues := make([]*Issue, 0, 10) - return issues, x.Where("repo_id=?", repoID).And("label_ids like '%$" + com.ToStr(labelID) + "|%'").Find(&issues) -} - // GetIssueCountByPoster returns number of issues of repository by poster. func GetIssueCountByPoster(uid, rid int64, isClosed bool) int64 { count, _ := x.Where("repo_id=?", rid).And("poster_id=?", uid).And("is_closed=?", isClosed).Count(new(Issue)) @@ -282,11 +325,11 @@ func GetIssueCountByPoster(uid, rid int64, isClosed bool) int64 { // IssueUser represents an issue-user relation. type IssueUser struct { - Id int64 - Uid int64 `xorm:"INDEX"` // User ID. - IssueId int64 - RepoId int64 `xorm:"INDEX"` - MilestoneId int64 + ID int64 `xorm:"pk autoincr"` + UID int64 `xorm:"uid INDEX"` // User ID. + IssueID int64 + RepoID int64 `xorm:"INDEX"` + MilestoneID int64 IsRead bool IsAssigned bool IsMentioned bool @@ -295,69 +338,72 @@ type IssueUser struct { } // FIXME: organization -// NewIssueUserPairs adds new issue-user pairs for new issue of repository. -func NewIssueUserPairs(repo *Repository, issueID, orgID, posterID, assigneeID int64) error { - users, err := repo.GetCollaborators() +func newIssueUsers(e *xorm.Session, repo *Repository, issue *Issue) error { + users, err := repo.GetAssignees() if err != nil { return err } iu := &IssueUser{ - IssueId: issueID, - RepoId: repo.Id, + IssueID: issue.ID, + RepoID: repo.ID, } + // Poster can be anyone. isNeedAddPoster := true for _, u := range users { - iu.Id = 0 - iu.Uid = u.Id - iu.IsPoster = iu.Uid == posterID + iu.ID = 0 + iu.UID = u.Id + iu.IsPoster = iu.UID == issue.PosterID if isNeedAddPoster && iu.IsPoster { isNeedAddPoster = false } - iu.IsAssigned = iu.Uid == assigneeID - if _, err = x.Insert(iu); err != nil { + iu.IsAssigned = iu.UID == issue.AssigneeID + if _, err = e.Insert(iu); err != nil { return err } } if isNeedAddPoster { - iu.Id = 0 - iu.Uid = posterID + iu.ID = 0 + iu.UID = issue.PosterID iu.IsPoster = true - iu.IsAssigned = iu.Uid == assigneeID - if _, err = x.Insert(iu); err != nil { + if _, err = e.Insert(iu); err != nil { return err } } + return nil +} - // Add owner's as well. - if repo.OwnerId != posterID { - iu.Id = 0 - iu.Uid = repo.OwnerId - iu.IsAssigned = iu.Uid == assigneeID - if _, err = x.Insert(iu); err != nil { - return err - } +// NewIssueUsers adds new issue-user relations for new issue of repository. +func NewIssueUsers(repo *Repository, issue *Issue) (err error) { + sess := x.NewSession() + defer sessionRelease(sess) + if err = sess.Begin(); err != nil { + return err } - return nil + if err = newIssueUsers(sess, repo, issue); err != nil { + return err + } + + return sess.Commit() } // PairsContains returns true when pairs list contains given issue. func PairsContains(ius []*IssueUser, issueId, uid int64) int { for i := range ius { - if ius[i].IssueId == issueId && - ius[i].Uid == uid { + if ius[i].IssueID == issueId && + ius[i].UID == uid { return i } } return -1 } -// GetIssueUserPairs returns issue-user pairs by given repository and user. -func GetIssueUserPairs(rid, uid int64, isClosed bool) ([]*IssueUser, error) { +// GetIssueUsers returns issue-user pairs by given repository and user. +func GetIssueUsers(rid, uid int64, isClosed bool) ([]*IssueUser, error) { ius := make([]*IssueUser, 0, 10) - err := x.Where("is_closed=?", isClosed).Find(&ius, &IssueUser{RepoId: rid, Uid: uid}) + err := x.Where("is_closed=?", isClosed).Find(&ius, &IssueUser{RepoID: rid, UID: uid}) return ius, err } @@ -420,50 +466,58 @@ const ( FM_MENTION ) +func parseCountResult(results []map[string][]byte) int64 { + if len(results) == 0 { + return 0 + } + for _, result := range results[0] { + return com.StrTo(string(result)).MustInt64() + } + return 0 +} + // GetIssueStats returns issue statistic information by given conditions. func GetIssueStats(repoID, uid, labelID, milestoneID int64, isShowClosed bool, filterMode int) *IssueStats { stats := &IssueStats{} - issue := new(Issue) + // issue := new(Issue) - queryStr := "issue.repo_id=? AND issue.is_closed=?" + queryStr := "SELECT COUNT(*) FROM `issue` " if labelID > 0 { - queryStr += " AND issue.label_ids like '%$" + com.ToStr(labelID) + "|%'" + queryStr += "INNER JOIN `issue_label` ON `issue`.id=`issue_label`.issue_id AND `issue_label`.label_id=" + com.ToStr(labelID) } + + baseCond := " WHERE issue.repo_id=? AND issue.is_closed=?" if milestoneID > 0 { - queryStr += " AND milestone_id=" + com.ToStr(milestoneID) + baseCond += " AND issue.milestone_id=" + com.ToStr(milestoneID) } switch filterMode { case FM_ALL: - stats.OpenCount, _ = x.Where(queryStr, repoID, false).Count(issue) - stats.ClosedCount, _ = x.Where(queryStr, repoID, true).Count(issue) - return stats + resutls, _ := x.Query(queryStr+baseCond, repoID, false) + stats.OpenCount = parseCountResult(resutls) + resutls, _ = x.Query(queryStr+baseCond, repoID, true) + stats.ClosedCount = parseCountResult(resutls) case FM_ASSIGN: - queryStr += " AND assignee_id=?" - stats.OpenCount, _ = x.Where(queryStr, repoID, false, uid).Count(issue) - stats.ClosedCount, _ = x.Where(queryStr, repoID, true, uid).Count(issue) - return stats + baseCond += " AND assignee_id=?" + resutls, _ := x.Query(queryStr+baseCond, repoID, false, uid) + stats.OpenCount = parseCountResult(resutls) + resutls, _ = x.Query(queryStr+baseCond, repoID, true, uid) + stats.ClosedCount = parseCountResult(resutls) case FM_CREATE: - queryStr += " AND poster_id=?" - stats.OpenCount, _ = x.Where(queryStr, repoID, false, uid).Count(issue) - stats.ClosedCount, _ = x.Where(queryStr, repoID, true, uid).Count(issue) - return stats + baseCond += " AND poster_id=?" + resutls, _ := x.Query(queryStr+baseCond, repoID, false, uid) + stats.OpenCount = parseCountResult(resutls) + resutls, _ = x.Query(queryStr+baseCond, repoID, true, uid) + stats.ClosedCount = parseCountResult(resutls) case FM_MENTION: - queryStr += " AND uid=? AND is_mentioned=?" - if labelID > 0 { - stats.OpenCount, _ = x.Where(queryStr, repoID, false, uid, true). - Join("INNER", "issue", "issue.id = issue_id").Count(new(IssueUser)) - stats.ClosedCount, _ = x.Where(queryStr, repoID, true, uid, true). - Join("INNER", "issue", "issue.id = issue_id").Count(new(IssueUser)) - return stats - } - - queryStr = strings.Replace(queryStr, "issue.", "", 2) - stats.OpenCount, _ = x.Where(queryStr, repoID, false, uid, true).Count(new(IssueUser)) - stats.ClosedCount, _ = x.Where(queryStr, repoID, true, uid, true).Count(new(IssueUser)) - return stats + queryStr += " INNER JOIN `issue_user` ON `issue`.id=`issue_user`.issue_id" + baseCond += " AND `issue_user`.uid=? AND `issue_user`.is_mentioned=?" + resutls, _ := x.Query(queryStr+baseCond, repoID, false, uid, true) + stats.OpenCount = parseCountResult(resutls) + resutls, _ = x.Query(queryStr+baseCond, repoID, true, uid, true) + stats.ClosedCount = parseCountResult(resutls) } return stats } @@ -477,15 +531,14 @@ func GetUserIssueStats(uid int64, filterMode int) *IssueStats { return stats } +func updateIssue(e Engine, issue *Issue) error { + _, err := e.Id(issue.ID).AllCols().Update(issue) + return err +} + // UpdateIssue updates information of issue. func UpdateIssue(issue *Issue) error { - _, err := x.Id(issue.ID).AllCols().Update(issue) - - if err != nil { - return err - } - - return err + return updateIssue(x, issue) } // UpdateIssueUserByStatus updates issue-user pairs by issue status. @@ -495,22 +548,34 @@ func UpdateIssueUserPairsByStatus(iid int64, isClosed bool) error { return err } -// UpdateIssueUserPairByAssignee updates issue-user pair for assigning. -func UpdateIssueUserPairByAssignee(aid, iid int64) error { - rawSql := "UPDATE `issue_user` SET is_assigned = ? WHERE issue_id = ?" - if _, err := x.Exec(rawSql, false, iid); err != nil { +func updateIssueUserByAssignee(e *xorm.Session, issueID, assigneeID int64) (err error) { + if _, err = e.Exec("UPDATE `issue_user` SET is_assigned=? WHERE issue_id=?", false, issueID); err != nil { return err } // Assignee ID equals to 0 means clear assignee. - if aid == 0 { + if assigneeID == 0 { return nil } - rawSql = "UPDATE `issue_user` SET is_assigned = ? WHERE uid = ? AND issue_id = ?" - _, err := x.Exec(rawSql, true, aid, iid) + _, err = e.Exec("UPDATE `issue_user` SET is_assigned=? WHERE uid=? AND issue_id=?", true, assigneeID, issueID) return err } +// UpdateIssueUserByAssignee updates issue-user relation for assignee. +func UpdateIssueUserByAssignee(issueID, assigneeID int64) (err error) { + sess := x.NewSession() + defer sessionRelease(sess) + if err = sess.Begin(); err != nil { + return err + } + + if err = updateIssueUserByAssignee(sess, issueID, assigneeID); err != nil { + return err + } + + return sess.Commit() +} + // UpdateIssueUserPairByRead updates issue-user pair for reading. func UpdateIssueUserPairByRead(uid, iid int64) error { rawSql := "UPDATE `issue_user` SET is_read = ? WHERE uid = ? AND issue_id = ?" @@ -521,7 +586,7 @@ func UpdateIssueUserPairByRead(uid, iid int64) error { // UpdateIssueUserPairsByMentions updates issue-user pairs by mentioning. func UpdateIssueUserPairsByMentions(uids []int64, iid int64) error { for _, uid := range uids { - iu := &IssueUser{Uid: uid, IssueId: iid} + iu := &IssueUser{UID: uid, IssueID: iid} has, err := x.Get(iu) if err != nil { return err @@ -529,7 +594,7 @@ func UpdateIssueUserPairsByMentions(uids []int64, iid int64) error { iu.IsMentioned = true if has { - _, err = x.Id(iu.Id).AllCols().Update(iu) + _, err = x.Id(iu.ID).AllCols().Update(iu) } else { _, err = x.Insert(iu) } @@ -550,7 +615,7 @@ func UpdateIssueUserPairsByMentions(uids []int64, iid int64) error { // Label represents a label of repository for issues. type Label struct { ID int64 `xorm:"pk autoincr"` - RepoId int64 `xorm:"INDEX"` + RepoID int64 `xorm:"INDEX"` Name string Color string `xorm:"VARCHAR(7)"` NumIssues int @@ -570,10 +635,9 @@ func NewLabel(l *Label) error { return err } -// GetLabelById returns a label by given ID. -func GetLabelById(id int64) (*Label, error) { +func getLabelByID(e Engine, id int64) (*Label, error) { if id <= 0 { - return nil, ErrLabelNotExist + return nil, ErrLabelNotExist{id} } l := &Label{ID: id} @@ -581,16 +645,43 @@ func GetLabelById(id int64) (*Label, error) { if err != nil { return nil, err } else if !has { - return nil, ErrLabelNotExist + return nil, ErrLabelNotExist{l.ID} } return l, nil } -// GetLabels returns a list of labels of given repository ID. -func GetLabels(repoId int64) ([]*Label, error) { +// GetLabelByID returns a label by given ID. +func GetLabelByID(id int64) (*Label, error) { + return getLabelByID(x, id) +} + +// GetLabelsByRepoID returns all labels that belong to given repository by ID. +func GetLabelsByRepoID(repoID int64) ([]*Label, error) { labels := make([]*Label, 0, 10) - err := x.Where("repo_id=?", repoId).Find(&labels) - return labels, err + return labels, x.Where("repo_id=?", repoID).Find(&labels) +} + +func getLabelsByIssueID(e Engine, issueID int64) ([]*Label, error) { + issueLabels, err := getIssueLabels(e, issueID) + if err != nil { + return nil, fmt.Errorf("getIssueLabels: %v", err) + } + + var label *Label + labels := make([]*Label, 0, len(issueLabels)) + for idx := range issueLabels { + label, err = getLabelByID(e, issueLabels[idx].LabelID) + if err != nil && !IsErrLabelNotExist(err) { + return nil, fmt.Errorf("getLabelByID: %v", err) + } + labels = append(labels, label) + } + return labels, nil +} + +// GetLabelsByIssueID returns all labels that belong to given issue by ID. +func GetLabelsByIssueID(issueID int64) ([]*Label, error) { + return getLabelsByIssueID(x, issueID) } // UpdateLabel updates label information. @@ -601,38 +692,92 @@ func UpdateLabel(l *Label) error { // DeleteLabel delete a label of given repository. func DeleteLabel(repoID, labelID int64) error { - l, err := GetLabelById(labelID) + l, err := GetLabelByID(labelID) if err != nil { - if err == ErrLabelNotExist { + if IsErrLabelNotExist(err) { return nil } return err } - issues, err := GetIssuesByLabel(repoID, labelID) - if err != nil { - return err - } - sess := x.NewSession() defer sessionRelease(sess) if err = sess.Begin(); err != nil { return err } - for _, issue := range issues { - issue.LabelIds = strings.Replace(issue.LabelIds, "$"+com.ToStr(labelID)+"|", "", -1) - if _, err = sess.Id(issue.ID).AllCols().Update(issue); err != nil { - return err - } - } - - if _, err = sess.Delete(l); err != nil { + if _, err = x.Where("label_id=?", labelID).Delete(new(IssueLabel)); err != nil { + return err + } else if _, err = sess.Delete(l); err != nil { return err } return sess.Commit() } +// .___ .____ ___. .__ +// | | ______ ________ __ ____ | | _____ \_ |__ ____ | | +// | |/ ___// ___/ | \_/ __ \| | \__ \ | __ \_/ __ \| | +// | |\___ \ \___ \| | /\ ___/| |___ / __ \| \_\ \ ___/| |__ +// |___/____ >____ >____/ \___ >_______ (____ /___ /\___ >____/ +// \/ \/ \/ \/ \/ \/ \/ + +// IssueLabel represetns an issue-lable relation. +type IssueLabel struct { + ID int64 `xorm:"pk autoincr"` + IssueID int64 `xorm:"UNIQUE(s)"` + LabelID int64 `xorm:"UNIQUE(s)"` +} + +func hasIssueLabel(e Engine, issueID, labelID int64) bool { + has, _ := e.Where("issue_id=? AND label_id=?", issueID, labelID).Get(new(IssueLabel)) + return has +} + +// HasIssueLabel returns true if issue has been labeled. +func HasIssueLabel(issueID, labelID int64) bool { + return hasIssueLabel(x, issueID, labelID) +} + +func newIssueLabel(e Engine, issueID, labelID int64) error { + if issueID == 0 || labelID == 0 { + return nil + } + + _, err := e.Insert(&IssueLabel{ + IssueID: issueID, + LabelID: labelID, + }) + return err +} + +// NewIssueLabel creates a new issue-label relation. +func NewIssueLabel(issueID, labelID int64) error { + return newIssueLabel(x, issueID, labelID) +} + +func getIssueLabels(e Engine, issueID int64) ([]*IssueLabel, error) { + issueLabels := make([]*IssueLabel, 0, 10) + return issueLabels, e.Where("issue_id=?", issueID).Asc("label_id").Find(&issueLabels) +} + +// GetIssueLabels returns all issue-label relations of given issue by ID. +func GetIssueLabels(issueID int64) ([]*IssueLabel, error) { + return getIssueLabels(x, issueID) +} + +func deleteIssueLabel(e Engine, issueID, labelID int64) error { + _, err := e.Delete(&IssueLabel{ + IssueID: issueID, + LabelID: labelID, + }) + return err +} + +// DeleteIssueLabel deletes issue-label relation. +func DeleteIssueLabel(issueID, labelID int64) error { + return deleteIssueLabel(x, issueID, labelID) +} + // _____ .__.__ __ // / \ |__| | ____ _______/ |_ ____ ____ ____ // / \ / \| | | _/ __ \ / ___/\ __\/ _ \ / \_/ __ \ @@ -702,14 +847,30 @@ func NewMilestone(m *Milestone) (err error) { return sess.Commit() } +func getMilestoneByID(e Engine, id int64) (*Milestone, error) { + m := &Milestone{ID: id} + has, err := x.Get(m) + if err != nil { + return nil, err + } else if !has { + return nil, ErrMilestoneNotExist{id, 0} + } + return m, nil +} + // GetMilestoneByID returns the milestone of given ID. func GetMilestoneByID(id int64) (*Milestone, error) { - m := &Milestone{ID: id} + return getMilestoneByID(x, id) +} + +// GetRepoMilestoneByID returns the milestone of given ID and repository. +func GetRepoMilestoneByID(repoID, milestoneID int64) (*Milestone, error) { + m := &Milestone{ID: milestoneID, RepoID: repoID} has, err := x.Get(m) if err != nil { return nil, err } else if !has { - return nil, ErrMilestoneNotExist{id} + return nil, ErrMilestoneNotExist{milestoneID, repoID} } return m, nil } @@ -768,7 +929,7 @@ func MilestoneStats(repoID int64) (open int64, closed int64) { // ChangeMilestoneStatus changes the milestone open/closed status. func ChangeMilestoneStatus(m *Milestone, isClosed bool) (err error) { - repo, err := GetRepositoryById(m.RepoID) + repo, err := GetRepositoryByID(m.RepoID) if err != nil { return err } @@ -784,9 +945,9 @@ func ChangeMilestoneStatus(m *Milestone, isClosed bool) (err error) { return err } - repo.NumMilestones = int(countRepoMilestones(sess, repo.Id)) - repo.NumClosedMilestones = int(countRepoClosedMilestones(sess, repo.Id)) - if _, err = sess.Id(repo.Id).AllCols().Update(repo); err != nil { + repo.NumMilestones = int(countRepoMilestones(sess, repo.ID)) + repo.NumClosedMilestones = int(countRepoClosedMilestones(sess, repo.ID)) + if _, err = sess.Id(repo.ID).AllCols().Update(repo); err != nil { return err } return sess.Commit() @@ -815,16 +976,9 @@ func ChangeMilestoneIssueStats(issue *Issue) error { return UpdateMilestone(m) } -// ChangeMilestoneAssign changes assignment of milestone for issue. -func ChangeMilestoneAssign(oldMid, mid int64, issue *Issue) (err error) { - sess := x.NewSession() - defer sess.Close() - if err = sess.Begin(); err != nil { - return err - } - +func changeMilestoneAssign(e *xorm.Session, oldMid int64, issue *Issue) error { if oldMid > 0 { - m, err := GetMilestoneByID(oldMid) + m, err := getMilestoneByID(e, oldMid) if err != nil { return err } @@ -834,20 +988,15 @@ func ChangeMilestoneAssign(oldMid, mid int64, issue *Issue) (err error) { m.NumClosedIssues-- } - if _, err = sess.Id(m.ID).AllCols().Update(m); err != nil { - sess.Rollback() + if err = updateMilestone(e, m); err != nil { return err - } - - rawSql := "UPDATE `issue_user` SET milestone_id = 0 WHERE issue_id = ?" - if _, err = sess.Exec(rawSql, issue.ID); err != nil { - sess.Rollback() + } else if _, err = e.Exec("UPDATE `issue_user` SET milestone_id=0 WHERE issue_id=?", issue.ID); err != nil { return err } } - if mid > 0 { - m, err := GetMilestoneByID(mid) + if issue.MilestoneID > 0 { + m, err := GetMilestoneByID(issue.MilestoneID) if err != nil { return err } @@ -861,18 +1010,28 @@ func ChangeMilestoneAssign(oldMid, mid int64, issue *Issue) (err error) { return ErrWrongIssueCounter } - if _, err = sess.Id(m.ID).AllCols().Update(m); err != nil { - sess.Rollback() + if err = updateMilestone(e, m); err != nil { return err - } - - rawSql := "UPDATE `issue_user` SET milestone_id = ? WHERE issue_id = ?" - if _, err = sess.Exec(rawSql, m.ID, issue.ID); err != nil { - sess.Rollback() + } else if _, err = e.Exec("UPDATE `issue_user` SET milestone_id=? WHERE issue_id=?", m.ID, issue.ID); err != nil { return err } } + return nil +} + +// ChangeMilestoneAssign changes assignment of milestone for issue. +func ChangeMilestoneAssign(oldMid int64, issue *Issue) (err error) { + sess := x.NewSession() + defer sess.Close() + if err = sess.Begin(); err != nil { + return err + } + + if err = changeMilestoneAssign(sess, oldMid, issue); err != nil { + return err + } + return sess.Commit() } @@ -886,7 +1045,7 @@ func DeleteMilestoneByID(mid int64) error { return err } - repo, err := GetRepositoryById(m.RepoID) + repo, err := GetRepositoryByID(m.RepoID) if err != nil { return err } @@ -901,9 +1060,9 @@ func DeleteMilestoneByID(mid int64) error { return err } - repo.NumMilestones = int(countRepoMilestones(sess, repo.Id)) - repo.NumClosedMilestones = int(countRepoClosedMilestones(sess, repo.Id)) - if _, err = sess.Id(repo.Id).AllCols().Update(repo); err != nil { + repo.NumMilestones = int(countRepoMilestones(sess, repo.ID)) + repo.NumClosedMilestones = int(countRepoClosedMilestones(sess, repo.ID)) + if _, err = sess.Id(repo.ID).AllCols().Update(repo); err != nil { return err } |