|  | @@ -7,6 +7,7 @@ package models
 | 
	
		
			
				|  |  |  import (
 | 
	
		
			
				|  |  |  	"bytes"
 | 
	
		
			
				|  |  |  	"errors"
 | 
	
		
			
				|  |  | +	"strconv"
 | 
	
		
			
				|  |  |  	"strings"
 | 
	
		
			
				|  |  |  	"time"
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -16,10 +17,11 @@ import (
 | 
	
		
			
				|  |  |  )
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  var (
 | 
	
		
			
				|  |  | -	ErrIssueNotExist     = errors.New("Issue does not exist")
 | 
	
		
			
				|  |  | -	ErrLabelNotExist     = errors.New("Label does not exist")
 | 
	
		
			
				|  |  | -	ErrMilestoneNotExist = errors.New("Milestone does not exist")
 | 
	
		
			
				|  |  | -	ErrWrongIssueCounter = errors.New("Invalid number of issues for this milestone")
 | 
	
		
			
				|  |  | +	ErrIssueNotExist      = errors.New("Issue does not exist")
 | 
	
		
			
				|  |  | +	ErrLabelNotExist      = errors.New("Label does not exist")
 | 
	
		
			
				|  |  | +	ErrMilestoneNotExist  = errors.New("Milestone does not exist")
 | 
	
		
			
				|  |  | +	ErrWrongIssueCounter  = errors.New("Invalid number of issues for this milestone")
 | 
	
		
			
				|  |  | +	ErrMissingIssueNumber = errors.New("No issue number specified")
 | 
	
		
			
				|  |  |  )
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  // Issue represents an issue or pull request of repository.
 | 
	
	
		
			
				|  | @@ -122,6 +124,29 @@ func NewIssue(issue *Issue) (err error) {
 | 
	
		
			
				|  |  |  	return
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +// GetIssueByRef returns an Issue specified by a GFM reference.
 | 
	
		
			
				|  |  | +// See https://help.github.com/articles/writing-on-github#references for more information on the syntax.
 | 
	
		
			
				|  |  | +func GetIssueByRef(ref string) (issue *Issue, err error) {
 | 
	
		
			
				|  |  | +	var issueNumber int64
 | 
	
		
			
				|  |  | +	var repo *Repository
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	n := strings.IndexByte(ref, byte('#'))
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if n == -1 {
 | 
	
		
			
				|  |  | +		return nil, ErrMissingIssueNumber
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if issueNumber, err = strconv.ParseInt(ref[n+1:], 10, 64); err != nil {
 | 
	
		
			
				|  |  | +		return
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if repo, err = GetRepositoryByRef(ref[:n]); err != nil {
 | 
	
		
			
				|  |  | +		return
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	return GetIssueByIndex(repo.Id, issueNumber)
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  // GetIssueByIndex returns issue by given index in repository.
 | 
	
		
			
				|  |  |  func GetIssueByIndex(rid, index int64) (*Issue, error) {
 | 
	
		
			
				|  |  |  	issue := &Issue{RepoId: rid, Index: index}
 | 
	
	
		
			
				|  | @@ -400,6 +425,11 @@ func GetUserIssueStats(uid int64, filterMode int) *IssueStats {
 | 
	
		
			
				|  |  |  // 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
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -670,6 +700,32 @@ func ChangeMilestoneStatus(m *Milestone, isClosed bool) (err error) {
 | 
	
		
			
				|  |  |  	return sess.Commit()
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +// ChangeMilestoneIssueStats updates the open/closed issues counter and progress for the
 | 
	
		
			
				|  |  | +// milestone associated witht the given issue.
 | 
	
		
			
				|  |  | +func ChangeMilestoneIssueStats(issue *Issue) error {
 | 
	
		
			
				|  |  | +	if issue.MilestoneId == 0 {
 | 
	
		
			
				|  |  | +		return nil
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	m, err := GetMilestoneById(issue.MilestoneId)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if err != nil {
 | 
	
		
			
				|  |  | +		return err
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if issue.IsClosed {
 | 
	
		
			
				|  |  | +		m.NumOpenIssues--
 | 
	
		
			
				|  |  | +		m.NumClosedIssues++
 | 
	
		
			
				|  |  | +	} else {
 | 
	
		
			
				|  |  | +		m.NumOpenIssues++
 | 
	
		
			
				|  |  | +		m.NumClosedIssues--
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	m.Completeness = m.NumClosedIssues * 100 / m.NumIssues
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	return UpdateMilestone(m)
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  // ChangeMilestoneAssign changes assignment of milestone for issue.
 | 
	
		
			
				|  |  |  func ChangeMilestoneAssign(oldMid, mid int64, issue *Issue) (err error) {
 | 
	
		
			
				|  |  |  	sess := x.NewSession()
 | 
	
	
		
			
				|  | @@ -693,6 +749,7 @@ func ChangeMilestoneAssign(oldMid, mid int64, issue *Issue) (err error) {
 | 
	
		
			
				|  |  |  		} else {
 | 
	
		
			
				|  |  |  			m.Completeness = 0
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  		if _, err = sess.Id(m.Id).Update(m); err != nil {
 | 
	
		
			
				|  |  |  			sess.Rollback()
 | 
	
		
			
				|  |  |  			return err
 | 
	
	
		
			
				|  | @@ -710,6 +767,7 @@ func ChangeMilestoneAssign(oldMid, mid int64, issue *Issue) (err error) {
 | 
	
		
			
				|  |  |  		if err != nil {
 | 
	
		
			
				|  |  |  			return err
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  		m.NumIssues++
 | 
	
		
			
				|  |  |  		if issue.IsClosed {
 | 
	
		
			
				|  |  |  			m.NumClosedIssues++
 | 
	
	
		
			
				|  | @@ -731,6 +789,7 @@ func ChangeMilestoneAssign(oldMid, mid int64, issue *Issue) (err error) {
 | 
	
		
			
				|  |  |  			return err
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  	return sess.Commit()
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 |