diff options
author | Unknwon <u@gogs.io> | 2017-02-10 16:51:09 -0500 |
---|---|---|
committer | Unknwon <u@gogs.io> | 2017-02-10 16:51:09 -0500 |
commit | 6b6f54b79b54a59d57e6566cb29151c15b455431 (patch) | |
tree | 17c3070ac23b16aec666989adb87f6d27b41feca /models | |
parent | b67ec01d41ad9d504d80ffb950e161b9ce0841fc (diff) |
Some file refactoring
Diffstat (limited to 'models')
-rw-r--r-- | models/attachment.go | 175 | ||||
-rw-r--r-- | models/comment.go (renamed from models/issue_comment.go) | 0 | ||||
-rw-r--r-- | models/issue.go | 502 | ||||
-rw-r--r-- | models/milestone.go | 349 | ||||
-rw-r--r-- | models/mirror.go (renamed from models/repo_mirror.go) | 0 |
5 files changed, 524 insertions, 502 deletions
diff --git a/models/attachment.go b/models/attachment.go new file mode 100644 index 00000000..a270b751 --- /dev/null +++ b/models/attachment.go @@ -0,0 +1,175 @@ +// Copyright 2017 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 ( + "fmt" + "io" + "mime/multipart" + "os" + "path" + "time" + + "github.com/go-xorm/xorm" + gouuid "github.com/satori/go.uuid" + + "github.com/gogits/gogs/modules/setting" +) + +// Attachment represent a attachment of issue/comment/release. +type Attachment struct { + ID int64 `xorm:"pk autoincr"` + UUID string `xorm:"uuid UNIQUE"` + IssueID int64 `xorm:"INDEX"` + CommentID int64 + ReleaseID int64 `xorm:"INDEX"` + Name string + + Created time.Time `xorm:"-"` + CreatedUnix int64 +} + +func (a *Attachment) BeforeInsert() { + a.CreatedUnix = time.Now().Unix() +} + +func (a *Attachment) AfterSet(colName string, _ xorm.Cell) { + switch colName { + case "created_unix": + a.Created = time.Unix(a.CreatedUnix, 0).Local() + } +} + +// AttachmentLocalPath returns where attachment is stored in local file system based on given UUID. +func AttachmentLocalPath(uuid string) string { + return path.Join(setting.AttachmentPath, uuid[0:1], uuid[1:2], uuid) +} + +// LocalPath returns where attachment is stored in local file system. +func (attach *Attachment) LocalPath() string { + return AttachmentLocalPath(attach.UUID) +} + +// NewAttachment creates a new attachment object. +func NewAttachment(name string, buf []byte, file multipart.File) (_ *Attachment, err error) { + attach := &Attachment{ + UUID: gouuid.NewV4().String(), + Name: name, + } + + localPath := attach.LocalPath() + if err = os.MkdirAll(path.Dir(localPath), os.ModePerm); err != nil { + return nil, fmt.Errorf("MkdirAll: %v", err) + } + + fw, err := os.Create(localPath) + if err != nil { + return nil, fmt.Errorf("Create: %v", err) + } + defer fw.Close() + + if _, err = fw.Write(buf); err != nil { + return nil, fmt.Errorf("Write: %v", err) + } else if _, err = io.Copy(fw, file); err != nil { + return nil, fmt.Errorf("Copy: %v", err) + } + + if _, err := x.Insert(attach); err != nil { + return nil, err + } + + return attach, nil +} + +func getAttachmentByUUID(e Engine, uuid string) (*Attachment, error) { + attach := &Attachment{UUID: uuid} + has, err := x.Get(attach) + if err != nil { + return nil, err + } else if !has { + return nil, ErrAttachmentNotExist{0, uuid} + } + return attach, nil +} + +func getAttachmentsByUUIDs(e Engine, uuids []string) ([]*Attachment, error) { + if len(uuids) == 0 { + return []*Attachment{}, nil + } + + // Silently drop invalid uuids. + attachments := make([]*Attachment, 0, len(uuids)) + return attachments, e.In("uuid", uuids).Find(&attachments) +} + +// GetAttachmentByUUID returns attachment by given UUID. +func GetAttachmentByUUID(uuid string) (*Attachment, error) { + return getAttachmentByUUID(x, uuid) +} + +func getAttachmentsByIssueID(e Engine, issueID int64) ([]*Attachment, error) { + attachments := make([]*Attachment, 0, 10) + return attachments, e.Where("issue_id = ? AND comment_id = 0", issueID).Find(&attachments) +} + +// GetAttachmentsByIssueID returns all attachments of an issue. +func GetAttachmentsByIssueID(issueID int64) ([]*Attachment, error) { + return getAttachmentsByIssueID(x, issueID) +} + +func getAttachmentsByCommentID(e Engine, commentID int64) ([]*Attachment, error) { + attachments := make([]*Attachment, 0, 10) + return attachments, e.Where("comment_id=?", commentID).Find(&attachments) +} + +// GetAttachmentsByCommentID returns all attachments if comment by given ID. +func GetAttachmentsByCommentID(commentID int64) ([]*Attachment, error) { + return getAttachmentsByCommentID(x, commentID) +} + +// DeleteAttachment deletes the given attachment and optionally the associated file. +func DeleteAttachment(a *Attachment, remove bool) error { + _, err := DeleteAttachments([]*Attachment{a}, remove) + return err +} + +// DeleteAttachments deletes the given attachments and optionally the associated files. +func DeleteAttachments(attachments []*Attachment, remove bool) (int, error) { + for i, a := range attachments { + if remove { + if err := os.Remove(a.LocalPath()); err != nil { + return i, err + } + } + + if _, err := x.Delete(a); err != nil { + return i, err + } + } + + return len(attachments), nil +} + +// DeleteAttachmentsByIssue deletes all attachments associated with the given issue. +func DeleteAttachmentsByIssue(issueId int64, remove bool) (int, error) { + attachments, err := GetAttachmentsByIssueID(issueId) + + if err != nil { + return 0, err + } + + return DeleteAttachments(attachments, remove) +} + +// DeleteAttachmentsByComment deletes all attachments associated with the given comment. +func DeleteAttachmentsByComment(commentId int64, remove bool) (int, error) { + attachments, err := GetAttachmentsByCommentID(commentId) + + if err != nil { + return 0, err + } + + return DeleteAttachments(attachments, remove) +} diff --git a/models/issue_comment.go b/models/comment.go index e166efd9..e166efd9 100644 --- a/models/issue_comment.go +++ b/models/comment.go diff --git a/models/issue.go b/models/issue.go index dbe01f6f..0997f3bb 100644 --- a/models/issue.go +++ b/models/issue.go @@ -7,17 +7,12 @@ package models import ( "errors" "fmt" - "io" - "mime/multipart" - "os" - "path" "strings" "time" "github.com/Unknwon/com" "github.com/go-xorm/xorm" api "github.com/gogits/go-gogs-client" - gouuid "github.com/satori/go.uuid" log "gopkg.in/clog.v1" "github.com/gogits/gogs/modules/base" @@ -1332,500 +1327,3 @@ func updateIssueUsersByMentions(e Engine, issueID int64, uids []int64) error { } return nil } - -// _____ .__.__ __ -// / \ |__| | ____ _______/ |_ ____ ____ ____ -// / \ / \| | | _/ __ \ / ___/\ __\/ _ \ / \_/ __ \ -// / Y \ | |_\ ___/ \___ \ | | ( <_> ) | \ ___/ -// \____|__ /__|____/\___ >____ > |__| \____/|___| /\___ > -// \/ \/ \/ \/ \/ - -// Milestone represents a milestone of repository. -type Milestone struct { - ID int64 `xorm:"pk autoincr"` - RepoID int64 `xorm:"INDEX"` - Name string - Content string `xorm:"TEXT"` - RenderedContent string `xorm:"-"` - IsClosed bool - NumIssues int - NumClosedIssues int - NumOpenIssues int `xorm:"-"` - Completeness int // Percentage(1-100). - IsOverDue bool `xorm:"-"` - - DeadlineString string `xorm:"-"` - Deadline time.Time `xorm:"-"` - DeadlineUnix int64 - ClosedDate time.Time `xorm:"-"` - ClosedDateUnix int64 -} - -func (m *Milestone) BeforeInsert() { - m.DeadlineUnix = m.Deadline.Unix() -} - -func (m *Milestone) BeforeUpdate() { - if m.NumIssues > 0 { - m.Completeness = m.NumClosedIssues * 100 / m.NumIssues - } else { - m.Completeness = 0 - } - - m.DeadlineUnix = m.Deadline.Unix() - m.ClosedDateUnix = m.ClosedDate.Unix() -} - -func (m *Milestone) AfterSet(colName string, _ xorm.Cell) { - switch colName { - case "num_closed_issues": - m.NumOpenIssues = m.NumIssues - m.NumClosedIssues - - case "deadline_unix": - m.Deadline = time.Unix(m.DeadlineUnix, 0).Local() - if m.Deadline.Year() == 9999 { - return - } - - m.DeadlineString = m.Deadline.Format("2006-01-02") - if time.Now().Local().After(m.Deadline) { - m.IsOverDue = true - } - - case "closed_date_unix": - m.ClosedDate = time.Unix(m.ClosedDateUnix, 0).Local() - } -} - -// State returns string representation of milestone status. -func (m *Milestone) State() api.StateType { - if m.IsClosed { - return api.STATE_CLOSED - } - return api.STATE_OPEN -} - -func (m *Milestone) ChangeStatus(isClosed bool) error { - return ChangeMilestoneStatus(m, isClosed) -} - -func (m *Milestone) APIFormat() *api.Milestone { - apiMilestone := &api.Milestone{ - ID: m.ID, - State: m.State(), - Title: m.Name, - Description: m.Content, - OpenIssues: m.NumOpenIssues, - ClosedIssues: m.NumClosedIssues, - } - if m.IsClosed { - apiMilestone.Closed = &m.ClosedDate - } - if m.Deadline.Year() < 9999 { - apiMilestone.Deadline = &m.Deadline - } - return apiMilestone -} - -// NewMilestone creates new milestone of repository. -func NewMilestone(m *Milestone) (err error) { - sess := x.NewSession() - defer sessionRelease(sess) - if err = sess.Begin(); err != nil { - return err - } - - if _, err = sess.Insert(m); err != nil { - return err - } - - if _, err = sess.Exec("UPDATE `repository` SET num_milestones = num_milestones + 1 WHERE id = ?", m.RepoID); err != nil { - return err - } - return sess.Commit() -} - -func getMilestoneByRepoID(e Engine, repoID, id int64) (*Milestone, error) { - m := &Milestone{ - ID: id, - RepoID: repoID, - } - has, err := e.Get(m) - if err != nil { - return nil, err - } else if !has { - return nil, ErrMilestoneNotExist{id, repoID} - } - return m, nil -} - -// GetWebhookByRepoID returns the milestone in a repository. -func GetMilestoneByRepoID(repoID, id int64) (*Milestone, error) { - return getMilestoneByRepoID(x, repoID, id) -} - -// GetMilestonesByRepoID returns all milestones of a repository. -func GetMilestonesByRepoID(repoID int64) ([]*Milestone, error) { - miles := make([]*Milestone, 0, 10) - return miles, x.Where("repo_id = ?", repoID).Find(&miles) -} - -// GetMilestones returns a list of milestones of given repository and status. -func GetMilestones(repoID int64, page int, isClosed bool) ([]*Milestone, error) { - miles := make([]*Milestone, 0, setting.UI.IssuePagingNum) - sess := x.Where("repo_id = ? AND is_closed = ?", repoID, isClosed) - if page > 0 { - sess = sess.Limit(setting.UI.IssuePagingNum, (page-1)*setting.UI.IssuePagingNum) - } - return miles, sess.Find(&miles) -} - -func updateMilestone(e Engine, m *Milestone) error { - _, err := e.Id(m.ID).AllCols().Update(m) - return err -} - -// UpdateMilestone updates information of given milestone. -func UpdateMilestone(m *Milestone) error { - return updateMilestone(x, m) -} - -func countRepoMilestones(e Engine, repoID int64) int64 { - count, _ := e.Where("repo_id=?", repoID).Count(new(Milestone)) - return count -} - -// CountRepoMilestones returns number of milestones in given repository. -func CountRepoMilestones(repoID int64) int64 { - return countRepoMilestones(x, repoID) -} - -func countRepoClosedMilestones(e Engine, repoID int64) int64 { - closed, _ := e.Where("repo_id=? AND is_closed=?", repoID, true).Count(new(Milestone)) - return closed -} - -// CountRepoClosedMilestones returns number of closed milestones in given repository. -func CountRepoClosedMilestones(repoID int64) int64 { - return countRepoClosedMilestones(x, repoID) -} - -// MilestoneStats returns number of open and closed milestones of given repository. -func MilestoneStats(repoID int64) (open int64, closed int64) { - open, _ = x.Where("repo_id=? AND is_closed=?", repoID, false).Count(new(Milestone)) - return open, CountRepoClosedMilestones(repoID) -} - -// ChangeMilestoneStatus changes the milestone open/closed status. -// If milestone passes with changed values, those values will be -// updated to database as well. -func ChangeMilestoneStatus(m *Milestone, isClosed bool) (err error) { - repo, err := GetRepositoryByID(m.RepoID) - if err != nil { - return err - } - - sess := x.NewSession() - defer sessionRelease(sess) - if err = sess.Begin(); err != nil { - return err - } - - m.IsClosed = isClosed - if err = updateMilestone(sess, m); err != nil { - 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 { - return err - } - return sess.Commit() -} - -func changeMilestoneIssueStats(e *xorm.Session, issue *Issue) error { - if issue.MilestoneID == 0 { - return nil - } - - m, err := getMilestoneByRepoID(e, issue.RepoID, issue.MilestoneID) - if err != nil { - return err - } - - if issue.IsClosed { - m.NumOpenIssues-- - m.NumClosedIssues++ - } else { - m.NumOpenIssues++ - m.NumClosedIssues-- - } - - return updateMilestone(e, m) -} - -// ChangeMilestoneIssueStats updates the open/closed issues counter and progress -// for the milestone associated with the given issue. -func ChangeMilestoneIssueStats(issue *Issue) (err error) { - sess := x.NewSession() - defer sessionRelease(sess) - if err = sess.Begin(); err != nil { - return err - } - - if err = changeMilestoneIssueStats(sess, issue); err != nil { - return err - } - - return sess.Commit() -} - -func changeMilestoneAssign(e *xorm.Session, issue *Issue, oldMilestoneID int64) error { - if oldMilestoneID > 0 { - m, err := getMilestoneByRepoID(e, issue.RepoID, oldMilestoneID) - if err != nil { - return err - } - - m.NumIssues-- - if issue.IsClosed { - m.NumClosedIssues-- - } - - if err = updateMilestone(e, m); err != nil { - return err - } else if _, err = e.Exec("UPDATE `issue_user` SET milestone_id = 0 WHERE issue_id = ?", issue.ID); err != nil { - return err - } - } - - if issue.MilestoneID > 0 { - m, err := getMilestoneByRepoID(e, issue.RepoID, issue.MilestoneID) - if err != nil { - return err - } - - m.NumIssues++ - if issue.IsClosed { - m.NumClosedIssues++ - } - - if err = updateMilestone(e, m); err != nil { - return err - } else if _, err = e.Exec("UPDATE `issue_user` SET milestone_id = ? WHERE issue_id = ?", m.ID, issue.ID); err != nil { - return err - } - } - - return updateIssue(e, issue) -} - -// ChangeMilestoneAssign changes assignment of milestone for issue. -func ChangeMilestoneAssign(issue *Issue, oldMilestoneID int64) (err error) { - sess := x.NewSession() - defer sess.Close() - if err = sess.Begin(); err != nil { - return err - } - - if err = changeMilestoneAssign(sess, issue, oldMilestoneID); err != nil { - return err - } - return sess.Commit() -} - -// DeleteMilestoneOfRepoByID deletes a milestone from a repository. -func DeleteMilestoneOfRepoByID(repoID, id int64) error { - m, err := GetMilestoneByRepoID(repoID, id) - if err != nil { - if IsErrMilestoneNotExist(err) { - return nil - } - return err - } - - repo, err := GetRepositoryByID(m.RepoID) - if err != nil { - return err - } - - sess := x.NewSession() - defer sessionRelease(sess) - if err = sess.Begin(); err != nil { - return err - } - - if _, err = sess.Id(m.ID).Delete(new(Milestone)); err != nil { - 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 { - return err - } - - if _, err = sess.Exec("UPDATE `issue` SET milestone_id = 0 WHERE milestone_id = ?", m.ID); err != nil { - return err - } else if _, err = sess.Exec("UPDATE `issue_user` SET milestone_id = 0 WHERE milestone_id = ?", m.ID); err != nil { - return err - } - return sess.Commit() -} - -// Attachment represent a attachment of issue/comment/release. -type Attachment struct { - ID int64 `xorm:"pk autoincr"` - UUID string `xorm:"uuid UNIQUE"` - IssueID int64 `xorm:"INDEX"` - CommentID int64 - ReleaseID int64 `xorm:"INDEX"` - Name string - - Created time.Time `xorm:"-"` - CreatedUnix int64 -} - -func (a *Attachment) BeforeInsert() { - a.CreatedUnix = time.Now().Unix() -} - -func (a *Attachment) AfterSet(colName string, _ xorm.Cell) { - switch colName { - case "created_unix": - a.Created = time.Unix(a.CreatedUnix, 0).Local() - } -} - -// AttachmentLocalPath returns where attachment is stored in local file system based on given UUID. -func AttachmentLocalPath(uuid string) string { - return path.Join(setting.AttachmentPath, uuid[0:1], uuid[1:2], uuid) -} - -// LocalPath returns where attachment is stored in local file system. -func (attach *Attachment) LocalPath() string { - return AttachmentLocalPath(attach.UUID) -} - -// NewAttachment creates a new attachment object. -func NewAttachment(name string, buf []byte, file multipart.File) (_ *Attachment, err error) { - attach := &Attachment{ - UUID: gouuid.NewV4().String(), - Name: name, - } - - localPath := attach.LocalPath() - if err = os.MkdirAll(path.Dir(localPath), os.ModePerm); err != nil { - return nil, fmt.Errorf("MkdirAll: %v", err) - } - - fw, err := os.Create(localPath) - if err != nil { - return nil, fmt.Errorf("Create: %v", err) - } - defer fw.Close() - - if _, err = fw.Write(buf); err != nil { - return nil, fmt.Errorf("Write: %v", err) - } else if _, err = io.Copy(fw, file); err != nil { - return nil, fmt.Errorf("Copy: %v", err) - } - - if _, err := x.Insert(attach); err != nil { - return nil, err - } - - return attach, nil -} - -func getAttachmentByUUID(e Engine, uuid string) (*Attachment, error) { - attach := &Attachment{UUID: uuid} - has, err := x.Get(attach) - if err != nil { - return nil, err - } else if !has { - return nil, ErrAttachmentNotExist{0, uuid} - } - return attach, nil -} - -func getAttachmentsByUUIDs(e Engine, uuids []string) ([]*Attachment, error) { - if len(uuids) == 0 { - return []*Attachment{}, nil - } - - // Silently drop invalid uuids. - attachments := make([]*Attachment, 0, len(uuids)) - return attachments, e.In("uuid", uuids).Find(&attachments) -} - -// GetAttachmentByUUID returns attachment by given UUID. -func GetAttachmentByUUID(uuid string) (*Attachment, error) { - return getAttachmentByUUID(x, uuid) -} - -func getAttachmentsByIssueID(e Engine, issueID int64) ([]*Attachment, error) { - attachments := make([]*Attachment, 0, 10) - return attachments, e.Where("issue_id = ? AND comment_id = 0", issueID).Find(&attachments) -} - -// GetAttachmentsByIssueID returns all attachments of an issue. -func GetAttachmentsByIssueID(issueID int64) ([]*Attachment, error) { - return getAttachmentsByIssueID(x, issueID) -} - -func getAttachmentsByCommentID(e Engine, commentID int64) ([]*Attachment, error) { - attachments := make([]*Attachment, 0, 10) - return attachments, e.Where("comment_id=?", commentID).Find(&attachments) -} - -// GetAttachmentsByCommentID returns all attachments if comment by given ID. -func GetAttachmentsByCommentID(commentID int64) ([]*Attachment, error) { - return getAttachmentsByCommentID(x, commentID) -} - -// DeleteAttachment deletes the given attachment and optionally the associated file. -func DeleteAttachment(a *Attachment, remove bool) error { - _, err := DeleteAttachments([]*Attachment{a}, remove) - return err -} - -// DeleteAttachments deletes the given attachments and optionally the associated files. -func DeleteAttachments(attachments []*Attachment, remove bool) (int, error) { - for i, a := range attachments { - if remove { - if err := os.Remove(a.LocalPath()); err != nil { - return i, err - } - } - - if _, err := x.Delete(a); err != nil { - return i, err - } - } - - return len(attachments), nil -} - -// DeleteAttachmentsByIssue deletes all attachments associated with the given issue. -func DeleteAttachmentsByIssue(issueId int64, remove bool) (int, error) { - attachments, err := GetAttachmentsByIssueID(issueId) - - if err != nil { - return 0, err - } - - return DeleteAttachments(attachments, remove) -} - -// DeleteAttachmentsByComment deletes all attachments associated with the given comment. -func DeleteAttachmentsByComment(commentId int64, remove bool) (int, error) { - attachments, err := GetAttachmentsByCommentID(commentId) - - if err != nil { - return 0, err - } - - return DeleteAttachments(attachments, remove) -} diff --git a/models/milestone.go b/models/milestone.go new file mode 100644 index 00000000..8607887b --- /dev/null +++ b/models/milestone.go @@ -0,0 +1,349 @@ +// Copyright 2017 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 ( + "time" + + "github.com/go-xorm/xorm" + + api "github.com/gogits/go-gogs-client" + + "github.com/gogits/gogs/modules/setting" +) + +// Milestone represents a milestone of repository. +type Milestone struct { + ID int64 `xorm:"pk autoincr"` + RepoID int64 `xorm:"INDEX"` + Name string + Content string `xorm:"TEXT"` + RenderedContent string `xorm:"-"` + IsClosed bool + NumIssues int + NumClosedIssues int + NumOpenIssues int `xorm:"-"` + Completeness int // Percentage(1-100). + IsOverDue bool `xorm:"-"` + + DeadlineString string `xorm:"-"` + Deadline time.Time `xorm:"-"` + DeadlineUnix int64 + ClosedDate time.Time `xorm:"-"` + ClosedDateUnix int64 +} + +func (m *Milestone) BeforeInsert() { + m.DeadlineUnix = m.Deadline.Unix() +} + +func (m *Milestone) BeforeUpdate() { + if m.NumIssues > 0 { + m.Completeness = m.NumClosedIssues * 100 / m.NumIssues + } else { + m.Completeness = 0 + } + + m.DeadlineUnix = m.Deadline.Unix() + m.ClosedDateUnix = m.ClosedDate.Unix() +} + +func (m *Milestone) AfterSet(colName string, _ xorm.Cell) { + switch colName { + case "num_closed_issues": + m.NumOpenIssues = m.NumIssues - m.NumClosedIssues + + case "deadline_unix": + m.Deadline = time.Unix(m.DeadlineUnix, 0).Local() + if m.Deadline.Year() == 9999 { + return + } + + m.DeadlineString = m.Deadline.Format("2006-01-02") + if time.Now().Local().After(m.Deadline) { + m.IsOverDue = true + } + + case "closed_date_unix": + m.ClosedDate = time.Unix(m.ClosedDateUnix, 0).Local() + } +} + +// State returns string representation of milestone status. +func (m *Milestone) State() api.StateType { + if m.IsClosed { + return api.STATE_CLOSED + } + return api.STATE_OPEN +} + +func (m *Milestone) ChangeStatus(isClosed bool) error { + return ChangeMilestoneStatus(m, isClosed) +} + +func (m *Milestone) APIFormat() *api.Milestone { + apiMilestone := &api.Milestone{ + ID: m.ID, + State: m.State(), + Title: m.Name, + Description: m.Content, + OpenIssues: m.NumOpenIssues, + ClosedIssues: m.NumClosedIssues, + } + if m.IsClosed { + apiMilestone.Closed = &m.ClosedDate + } + if m.Deadline.Year() < 9999 { + apiMilestone.Deadline = &m.Deadline + } + return apiMilestone +} + +// NewMilestone creates new milestone of repository. +func NewMilestone(m *Milestone) (err error) { + sess := x.NewSession() + defer sessionRelease(sess) + if err = sess.Begin(); err != nil { + return err + } + + if _, err = sess.Insert(m); err != nil { + return err + } + + if _, err = sess.Exec("UPDATE `repository` SET num_milestones = num_milestones + 1 WHERE id = ?", m.RepoID); err != nil { + return err + } + return sess.Commit() +} + +func getMilestoneByRepoID(e Engine, repoID, id int64) (*Milestone, error) { + m := &Milestone{ + ID: id, + RepoID: repoID, + } + has, err := e.Get(m) + if err != nil { + return nil, err + } else if !has { + return nil, ErrMilestoneNotExist{id, repoID} + } + return m, nil +} + +// GetWebhookByRepoID returns the milestone in a repository. +func GetMilestoneByRepoID(repoID, id int64) (*Milestone, error) { + return getMilestoneByRepoID(x, repoID, id) +} + +// GetMilestonesByRepoID returns all milestones of a repository. +func GetMilestonesByRepoID(repoID int64) ([]*Milestone, error) { + miles := make([]*Milestone, 0, 10) + return miles, x.Where("repo_id = ?", repoID).Find(&miles) +} + +// GetMilestones returns a list of milestones of given repository and status. +func GetMilestones(repoID int64, page int, isClosed bool) ([]*Milestone, error) { + miles := make([]*Milestone, 0, setting.UI.IssuePagingNum) + sess := x.Where("repo_id = ? AND is_closed = ?", repoID, isClosed) + if page > 0 { + sess = sess.Limit(setting.UI.IssuePagingNum, (page-1)*setting.UI.IssuePagingNum) + } + return miles, sess.Find(&miles) +} + +func updateMilestone(e Engine, m *Milestone) error { + _, err := e.Id(m.ID).AllCols().Update(m) + return err +} + +// UpdateMilestone updates information of given milestone. +func UpdateMilestone(m *Milestone) error { + return updateMilestone(x, m) +} + +func countRepoMilestones(e Engine, repoID int64) int64 { + count, _ := e.Where("repo_id=?", repoID).Count(new(Milestone)) + return count +} + +// CountRepoMilestones returns number of milestones in given repository. +func CountRepoMilestones(repoID int64) int64 { + return countRepoMilestones(x, repoID) +} + +func countRepoClosedMilestones(e Engine, repoID int64) int64 { + closed, _ := e.Where("repo_id=? AND is_closed=?", repoID, true).Count(new(Milestone)) + return closed +} + +// CountRepoClosedMilestones returns number of closed milestones in given repository. +func CountRepoClosedMilestones(repoID int64) int64 { + return countRepoClosedMilestones(x, repoID) +} + +// MilestoneStats returns number of open and closed milestones of given repository. +func MilestoneStats(repoID int64) (open int64, closed int64) { + open, _ = x.Where("repo_id=? AND is_closed=?", repoID, false).Count(new(Milestone)) + return open, CountRepoClosedMilestones(repoID) +} + +// ChangeMilestoneStatus changes the milestone open/closed status. +// If milestone passes with changed values, those values will be +// updated to database as well. +func ChangeMilestoneStatus(m *Milestone, isClosed bool) (err error) { + repo, err := GetRepositoryByID(m.RepoID) + if err != nil { + return err + } + + sess := x.NewSession() + defer sessionRelease(sess) + if err = sess.Begin(); err != nil { + return err + } + + m.IsClosed = isClosed + if err = updateMilestone(sess, m); err != nil { + 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 { + return err + } + return sess.Commit() +} + +func changeMilestoneIssueStats(e *xorm.Session, issue *Issue) error { + if issue.MilestoneID == 0 { + return nil + } + + m, err := getMilestoneByRepoID(e, issue.RepoID, issue.MilestoneID) + if err != nil { + return err + } + + if issue.IsClosed { + m.NumOpenIssues-- + m.NumClosedIssues++ + } else { + m.NumOpenIssues++ + m.NumClosedIssues-- + } + + return updateMilestone(e, m) +} + +// ChangeMilestoneIssueStats updates the open/closed issues counter and progress +// for the milestone associated with the given issue. +func ChangeMilestoneIssueStats(issue *Issue) (err error) { + sess := x.NewSession() + defer sessionRelease(sess) + if err = sess.Begin(); err != nil { + return err + } + + if err = changeMilestoneIssueStats(sess, issue); err != nil { + return err + } + + return sess.Commit() +} + +func changeMilestoneAssign(e *xorm.Session, issue *Issue, oldMilestoneID int64) error { + if oldMilestoneID > 0 { + m, err := getMilestoneByRepoID(e, issue.RepoID, oldMilestoneID) + if err != nil { + return err + } + + m.NumIssues-- + if issue.IsClosed { + m.NumClosedIssues-- + } + + if err = updateMilestone(e, m); err != nil { + return err + } else if _, err = e.Exec("UPDATE `issue_user` SET milestone_id = 0 WHERE issue_id = ?", issue.ID); err != nil { + return err + } + } + + if issue.MilestoneID > 0 { + m, err := getMilestoneByRepoID(e, issue.RepoID, issue.MilestoneID) + if err != nil { + return err + } + + m.NumIssues++ + if issue.IsClosed { + m.NumClosedIssues++ + } + + if err = updateMilestone(e, m); err != nil { + return err + } else if _, err = e.Exec("UPDATE `issue_user` SET milestone_id = ? WHERE issue_id = ?", m.ID, issue.ID); err != nil { + return err + } + } + + return updateIssue(e, issue) +} + +// ChangeMilestoneAssign changes assignment of milestone for issue. +func ChangeMilestoneAssign(issue *Issue, oldMilestoneID int64) (err error) { + sess := x.NewSession() + defer sess.Close() + if err = sess.Begin(); err != nil { + return err + } + + if err = changeMilestoneAssign(sess, issue, oldMilestoneID); err != nil { + return err + } + return sess.Commit() +} + +// DeleteMilestoneOfRepoByID deletes a milestone from a repository. +func DeleteMilestoneOfRepoByID(repoID, id int64) error { + m, err := GetMilestoneByRepoID(repoID, id) + if err != nil { + if IsErrMilestoneNotExist(err) { + return nil + } + return err + } + + repo, err := GetRepositoryByID(m.RepoID) + if err != nil { + return err + } + + sess := x.NewSession() + defer sessionRelease(sess) + if err = sess.Begin(); err != nil { + return err + } + + if _, err = sess.Id(m.ID).Delete(new(Milestone)); err != nil { + 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 { + return err + } + + if _, err = sess.Exec("UPDATE `issue` SET milestone_id = 0 WHERE milestone_id = ?", m.ID); err != nil { + return err + } else if _, err = sess.Exec("UPDATE `issue_user` SET milestone_id = 0 WHERE milestone_id = ?", m.ID); err != nil { + return err + } + return sess.Commit() +} diff --git a/models/repo_mirror.go b/models/mirror.go index 722d769f..722d769f 100644 --- a/models/repo_mirror.go +++ b/models/mirror.go |