2014-03-20 16:04:56 -04:00
// 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
2014-03-22 13:50:50 -04:00
import (
2014-05-07 16:51:14 -04:00
"bytes"
2014-03-22 16:00:46 -04:00
"errors"
2014-07-24 10:15:05 +02:00
"html/template"
2014-07-23 21:15:47 +02:00
"os"
"strconv"
2014-03-22 13:50:50 -04:00
"strings"
"time"
2014-05-07 16:51:14 -04:00
2014-07-26 00:24:27 -04:00
"github.com/Unknwon/com"
2015-08-04 22:24:04 +08:00
"github.com/go-xorm/xorm"
2014-05-13 14:49:20 -04:00
2014-07-23 21:15:47 +02:00
"github.com/gogits/gogs/modules/log"
2015-07-24 16:42:47 +08:00
"github.com/gogits/gogs/modules/setting"
2014-03-22 13:50:50 -04:00
)
2014-03-22 16:00:46 -04:00
var (
2014-07-23 21:15:47 +02:00
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" )
2014-07-24 12:29:37 +02:00
ErrMissingIssueNumber = errors . New ( "No issue number specified" )
2014-03-22 16:00:46 -04:00
)
2014-03-22 13:50:50 -04:00
// Issue represents an issue or pull request of repository.
2014-03-20 16:04:56 -04:00
type Issue struct {
2015-07-24 21:02:49 +08:00
ID int64 ` xorm:"pk autoincr" `
2014-05-12 14:06:42 -04:00
RepoId int64 ` xorm:"INDEX" `
2014-03-29 10:24:42 -04:00
Index int64 // Index in one repository.
Name string
Repo * Repository ` xorm:"-" `
PosterId int64
2014-05-24 02:31:58 -04:00
Poster * User ` xorm:"-" `
LabelIds string ` xorm:"TEXT" `
Labels [ ] * Label ` xorm:"-" `
2014-03-29 10:24:42 -04:00
MilestoneId int64
AssigneeId int64
2014-05-08 12:24:11 -04:00
Assignee * User ` xorm:"-" `
IsRead bool ` xorm:"-" `
IsPull bool // Indicates whether is a pull request or not.
2014-03-29 10:24:42 -04:00
IsClosed bool
Content string ` xorm:"TEXT" `
RenderedContent string ` xorm:"-" `
2014-05-06 16:28:52 -04:00
Priority int
2014-03-29 10:24:42 -04:00
NumComments int
2014-05-06 16:28:52 -04:00
Deadline time . Time
2014-05-07 12:09:30 -04:00
Created time . Time ` xorm:"CREATED" `
Updated time . Time ` xorm:"UPDATED" `
}
func ( i * Issue ) GetPoster ( ) ( err error ) {
i . Poster , err = GetUserById ( i . PosterId )
2014-05-08 20:00:07 -04:00
if err == ErrUserNotExist {
i . Poster = & User { Name : "FakeUser" }
return nil
}
2014-05-07 12:09:30 -04:00
return err
}
2014-05-24 02:31:58 -04:00
func ( i * Issue ) GetLabels ( ) error {
if len ( i . LabelIds ) < 3 {
return nil
}
strIds := strings . Split ( strings . TrimSuffix ( i . LabelIds [ 1 : ] , "|" ) , "|$" )
i . Labels = make ( [ ] * Label , 0 , len ( strIds ) )
for _ , strId := range strIds {
2015-07-25 02:52:25 +08:00
id := com . StrTo ( strId ) . MustInt64 ( )
2014-05-24 02:31:58 -04:00
if id > 0 {
l , err := GetLabelById ( id )
if err != nil {
if err == ErrLabelNotExist {
continue
}
return err
}
i . Labels = append ( i . Labels , l )
}
}
return nil
}
2014-05-08 12:24:11 -04:00
func ( i * Issue ) GetAssignee ( ) ( err error ) {
if i . AssigneeId == 0 {
return nil
}
i . Assignee , err = GetUserById ( i . AssigneeId )
2014-05-24 02:31:58 -04:00
if err == ErrUserNotExist {
return nil
}
2014-05-08 12:24:11 -04:00
return err
}
2014-07-23 21:24:24 +02:00
func ( i * Issue ) Attachments ( ) [ ] * Attachment {
2015-07-24 21:02:49 +08:00
a , _ := GetAttachmentsForIssue ( i . ID )
2014-07-23 21:24:24 +02:00
return a
}
2014-07-23 21:15:47 +02:00
func ( i * Issue ) AfterDelete ( ) {
2015-07-24 21:02:49 +08:00
_ , err := DeleteAttachmentsByIssue ( i . ID , true )
2014-07-23 21:15:47 +02:00
if err != nil {
2015-07-24 21:02:49 +08:00
log . Info ( "Could not delete files for issue #%d: %s" , i . ID , err )
2014-07-23 21:15:47 +02:00
}
}
2014-03-22 13:50:50 -04:00
// CreateIssue creates new issue for repository.
2014-05-07 12:09:30 -04:00
func NewIssue ( issue * Issue ) ( err error ) {
2014-06-21 00:51:41 -04:00
sess := x . NewSession ( )
2015-07-24 16:42:47 +08:00
defer sessionRelease ( sess )
2014-05-07 12:09:30 -04:00
if err = sess . Begin ( ) ; err != nil {
return err
2014-03-22 16:00:46 -04:00
}
2014-05-07 12:09:30 -04:00
2014-03-27 12:48:29 -04:00
if _ , err = sess . Insert ( issue ) ; err != nil {
2014-05-07 12:09:30 -04:00
return err
2015-07-24 16:42:47 +08:00
} else if _ , err = sess . Exec ( "UPDATE `repository` SET num_issues = num_issues + 1 WHERE id = ?" , issue . RepoId ) ; err != nil {
2014-05-07 12:09:30 -04:00
return err
2014-03-27 12:48:29 -04:00
}
2014-07-22 13:50:34 +02:00
if err = sess . Commit ( ) ; err != nil {
return err
}
if issue . MilestoneId > 0 {
// FIXES(280): Update milestone counter.
return ChangeMilestoneAssign ( 0 , issue . MilestoneId , issue )
}
return
2014-03-20 16:04:56 -04:00
}
2014-07-23 13:48:06 +02:00
// 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 )
}
2014-05-07 12:09:30 -04:00
// GetIssueByIndex returns issue by given index in repository.
func GetIssueByIndex ( rid , index int64 ) ( * Issue , error ) {
issue := & Issue { RepoId : rid , Index : index }
2014-06-21 00:51:41 -04:00
has , err := x . Get ( issue )
2014-03-22 16:00:46 -04:00
if err != nil {
return nil , err
} else if ! has {
return nil , ErrIssueNotExist
}
return issue , nil
}
2014-05-07 16:51:14 -04:00
// GetIssueById returns an issue by ID.
func GetIssueById ( id int64 ) ( * Issue , error ) {
2015-07-24 21:02:49 +08:00
issue := & Issue { ID : id }
2014-06-21 00:51:41 -04:00
has , err := x . Get ( issue )
2014-05-07 16:51:14 -04:00
if err != nil {
return nil , err
} else if ! has {
return nil , ErrIssueNotExist
}
return issue , nil
}
2015-08-04 22:24:04 +08:00
// 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 ) {
2015-07-24 16:42:47 +08:00
sess := x . Limit ( setting . IssuePagingNum , ( page - 1 ) * setting . IssuePagingNum )
2014-03-22 16:00:46 -04:00
2015-07-25 02:52:25 +08:00
if repoID > 0 {
sess . Where ( "issue.repo_id=?" , repoID ) . And ( "issue.is_closed=?" , isClosed )
2014-03-22 16:00:46 -04:00
} else {
2015-07-25 02:52:25 +08:00
sess . Where ( "issue.is_closed=?" , isClosed )
2014-03-22 16:00:46 -04:00
}
2015-07-25 02:52:25 +08:00
if assigneeID > 0 {
sess . And ( "issue.assignee_id=?" , assigneeID )
} else if posterID > 0 {
sess . And ( "issue.poster_id=?" , posterID )
2014-03-22 13:50:50 -04:00
}
2015-07-25 02:52:25 +08:00
if milestoneID > 0 {
sess . And ( "issue.milestone_id=?" , milestoneID )
2014-03-22 13:50:50 -04:00
}
2014-05-24 02:31:58 -04:00
if len ( labelIds ) > 0 {
for _ , label := range strings . Split ( labelIds , "," ) {
2014-10-25 07:50:19 -04:00
if com . StrTo ( label ) . MustInt ( ) > 0 {
2015-07-25 02:52:25 +08:00
sess . And ( "label_ids like ?" , "%$" + label + "|%" )
2014-10-25 07:50:19 -04:00
}
2014-03-22 13:50:50 -04:00
}
}
switch sortType {
case "oldest" :
2014-03-23 06:27:01 -04:00
sess . Asc ( "created" )
2014-03-22 13:50:50 -04:00
case "recentupdate" :
2014-03-23 06:27:01 -04:00
sess . Desc ( "updated" )
2014-03-22 13:50:50 -04:00
case "leastupdate" :
2014-03-23 06:27:01 -04:00
sess . Asc ( "updated" )
2014-03-22 13:50:50 -04:00
case "mostcomment" :
2014-03-23 06:27:01 -04:00
sess . Desc ( "num_comments" )
2014-03-22 13:50:50 -04:00
case "leastcomment" :
2014-03-23 06:27:01 -04:00
sess . Asc ( "num_comments" )
2014-05-07 20:36:00 -04:00
case "priority" :
sess . Desc ( "priority" )
2014-03-22 13:50:50 -04:00
default :
2014-03-23 06:27:01 -04:00
sess . Desc ( "created" )
2014-03-22 13:50:50 -04:00
}
2015-07-25 02:52:25 +08:00
if isMention {
2015-07-25 13:18:19 +08:00
queryStr := "issue.id = issue_user.issue_id AND issue_user.is_mentioned=1"
2015-07-25 02:52:25 +08:00
if uid > 0 {
queryStr += " AND issue_user.uid = " + com . ToStr ( uid )
}
sess . Join ( "INNER" , "issue_user" , queryStr )
}
2015-08-04 22:24:04 +08:00
issues := make ( [ ] * Issue , 0 , setting . IssuePagingNum )
2015-07-24 16:42:47 +08:00
return issues , sess . Find ( & issues )
2014-03-22 13:50:50 -04:00
}
2014-05-24 03:05:41 -04:00
type IssueStatus int
const (
IS_OPEN = iota + 1
IS_CLOSE
)
2014-05-24 02:31:58 -04:00
// GetIssuesByLabel returns a list of issues by given label and repository.
2015-07-24 23:13:42 +08:00
func GetIssuesByLabel ( repoID , labelID int64 ) ( [ ] * Issue , error ) {
2014-05-24 02:31:58 -04:00
issues := make ( [ ] * Issue , 0 , 10 )
2015-07-24 23:13:42 +08:00
return issues , x . Where ( "repo_id=?" , repoID ) . And ( "label_ids like '%$" + com . ToStr ( labelID ) + "|%'" ) . Find ( & issues )
2014-05-24 02:31:58 -04:00
}
2014-05-07 16:51:14 -04:00
// GetIssueCountByPoster returns number of issues of repository by poster.
func GetIssueCountByPoster ( uid , rid int64 , isClosed bool ) int64 {
2014-06-21 00:51:41 -04:00
count , _ := x . Where ( "repo_id=?" , rid ) . And ( "poster_id=?" , uid ) . And ( "is_closed=?" , isClosed ) . Count ( new ( Issue ) )
2014-05-07 16:51:14 -04:00
return count
}
2014-05-15 12:08:53 -04:00
// .___ ____ ___
// | | ______ ________ __ ____ | | \______ ___________
// | |/ ___// ___/ | \_/ __ \| | / ___// __ \_ __ \
// | |\___ \ \___ \| | /\ ___/| | /\___ \\ ___/| | \/
// |___/____ >____ >____/ \___ >______//____ >\___ >__|
// \/ \/ \/ \/ \/
2014-05-07 16:51:14 -04:00
// IssueUser represents an issue-user relation.
type IssueUser struct {
Id int64
2014-06-25 00:44:48 -04:00
Uid int64 ` xorm:"INDEX" ` // User ID.
2014-05-07 16:51:14 -04:00
IssueId int64
2014-06-25 00:44:48 -04:00
RepoId int64 ` xorm:"INDEX" `
2014-05-14 13:04:57 -04:00
MilestoneId int64
2014-05-07 16:51:14 -04:00
IsRead bool
IsAssigned bool
IsMentioned bool
IsPoster bool
IsClosed bool
}
2015-07-24 16:42:47 +08:00
// FIXME: organization
2014-05-07 16:51:14 -04:00
// NewIssueUserPairs adds new issue-user pairs for new issue of repository.
2015-03-27 06:47:02 -04:00
func NewIssueUserPairs ( repo * Repository , issueID , orgID , posterID , assigneeID int64 ) error {
2015-02-11 21:58:37 -05:00
users , err := repo . GetCollaborators ( )
2014-05-07 16:51:14 -04:00
if err != nil {
return err
}
2015-02-11 21:58:37 -05:00
iu := & IssueUser {
IssueId : issueID ,
RepoId : repo . Id ,
}
2014-05-08 12:24:11 -04:00
isNeedAddPoster := true
2015-02-11 21:58:37 -05:00
for _ , u := range users {
2015-03-27 06:47:02 -04:00
iu . Id = 0
2014-05-08 12:24:11 -04:00
iu . Uid = u . Id
2015-02-11 21:58:37 -05:00
iu . IsPoster = iu . Uid == posterID
2014-05-08 12:24:11 -04:00
if isNeedAddPoster && iu . IsPoster {
isNeedAddPoster = false
2014-05-07 16:51:14 -04:00
}
2015-02-11 21:58:37 -05:00
iu . IsAssigned = iu . Uid == assigneeID
2014-06-21 00:51:41 -04:00
if _ , err = x . Insert ( iu ) ; err != nil {
2014-05-08 12:24:11 -04:00
return err
2014-05-07 16:51:14 -04:00
}
2014-05-08 12:24:11 -04:00
}
if isNeedAddPoster {
2015-03-27 06:47:02 -04:00
iu . Id = 0
2015-02-11 21:58:37 -05:00
iu . Uid = posterID
2014-05-08 12:24:11 -04:00
iu . IsPoster = true
2015-02-11 21:58:37 -05:00
iu . IsAssigned = iu . Uid == assigneeID
2014-06-21 00:51:41 -04:00
if _ , err = x . Insert ( iu ) ; err != nil {
2014-05-07 16:51:14 -04:00
return err
}
}
2014-05-08 12:24:11 -04:00
2015-07-24 16:42:47 +08:00
// 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
}
}
2014-05-07 16:51:14 -04:00
return nil
}
2014-05-07 12:09:30 -04:00
// PairsContains returns true when pairs list contains given issue.
2015-07-24 16:42:47 +08:00
func PairsContains ( ius [ ] * IssueUser , issueId , uid int64 ) int {
2014-05-07 12:09:30 -04:00
for i := range ius {
2015-07-24 16:42:47 +08:00
if ius [ i ] . IssueId == issueId &&
ius [ i ] . Uid == uid {
2014-05-07 16:51:14 -04:00
return i
2014-05-07 12:09:30 -04:00
}
}
2014-05-07 16:51:14 -04:00
return - 1
2014-05-07 12:09:30 -04:00
}
2014-05-07 16:51:14 -04:00
// GetIssueUserPairs returns issue-user pairs by given repository and user.
func GetIssueUserPairs ( rid , uid int64 , isClosed bool ) ( [ ] * IssueUser , error ) {
ius := make ( [ ] * IssueUser , 0 , 10 )
2014-06-21 00:51:41 -04:00
err := x . Where ( "is_closed=?" , isClosed ) . Find ( & ius , & IssueUser { RepoId : rid , Uid : uid } )
2014-05-07 12:09:30 -04:00
return ius , err
}
2014-05-07 16:51:14 -04:00
// GetIssueUserPairsByRepoIds returns issue-user pairs by given repository IDs.
func GetIssueUserPairsByRepoIds ( rids [ ] int64 , isClosed bool , page int ) ( [ ] * IssueUser , error ) {
2014-07-06 18:25:07 -04:00
if len ( rids ) == 0 {
return [ ] * IssueUser { } , nil
}
2014-05-07 16:51:14 -04:00
buf := bytes . NewBufferString ( "" )
for _ , rid := range rids {
buf . WriteString ( "repo_id=" )
2014-07-26 00:24:27 -04:00
buf . WriteString ( com . ToStr ( rid ) )
2014-05-07 16:51:14 -04:00
buf . WriteString ( " OR " )
}
cond := strings . TrimSuffix ( buf . String ( ) , " OR " )
ius := make ( [ ] * IssueUser , 0 , 10 )
2014-06-21 00:51:41 -04:00
sess := x . Limit ( 20 , ( page - 1 ) * 20 ) . Where ( "is_closed=?" , isClosed )
2014-05-07 16:51:14 -04:00
if len ( cond ) > 0 {
sess . And ( cond )
}
err := sess . Find ( & ius )
return ius , err
}
// GetIssueUserPairsByMode returns issue-user pairs by given repository and user.
func GetIssueUserPairsByMode ( uid , rid int64 , isClosed bool , page , filterMode int ) ( [ ] * IssueUser , error ) {
ius := make ( [ ] * IssueUser , 0 , 10 )
2014-06-21 00:51:41 -04:00
sess := x . Limit ( 20 , ( page - 1 ) * 20 ) . Where ( "uid=?" , uid ) . And ( "is_closed=?" , isClosed )
2014-05-07 16:51:14 -04:00
if rid > 0 {
sess . And ( "repo_id=?" , rid )
}
switch filterMode {
case FM_ASSIGN :
sess . And ( "is_assigned=?" , true )
case FM_CREATE :
sess . And ( "is_poster=?" , true )
default :
return ius , nil
}
err := sess . Find ( & ius )
return ius , err
2014-03-27 16:31:32 -04:00
}
2014-05-07 12:09:30 -04:00
// IssueStats represents issue statistic information.
type IssueStats struct {
OpenCount , ClosedCount int64
AllCount int64
AssignCount int64
CreateCount int64
MentionCount int64
}
// Filter modes.
const (
2015-07-25 02:52:25 +08:00
FM_ALL = iota
FM_ASSIGN
2014-05-07 12:09:30 -04:00
FM_CREATE
FM_MENTION
)
2014-05-07 16:51:14 -04:00
// GetIssueStats returns issue statistic information by given conditions.
2015-07-25 02:52:25 +08:00
func GetIssueStats ( repoID , uid , labelID int64 , isShowClosed bool , filterMode int ) * IssueStats {
2014-05-07 12:09:30 -04:00
stats := & IssueStats { }
issue := new ( Issue )
2015-07-25 02:52:25 +08:00
2015-07-25 13:07:00 +08:00
queryStr := "issue.repo_id=? AND issue.is_closed=?"
if labelID > 0 {
queryStr += " AND issue.label_ids like '%$" + com . ToStr ( labelID ) + "|%'"
}
2015-07-25 02:52:25 +08:00
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
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
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
case FM_MENTION :
queryStr += " AND uid=? AND is_mentioned=?"
2015-07-25 13:07:00 +08:00
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 )
2015-07-25 02:52:25 +08:00
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
}
2014-05-07 12:09:30 -04:00
return stats
}
2014-05-07 16:51:14 -04:00
// GetUserIssueStats returns issue statistic information for dashboard by given conditions.
2014-05-07 12:09:30 -04:00
func GetUserIssueStats ( uid int64 , filterMode int ) * IssueStats {
stats := & IssueStats { }
issue := new ( Issue )
2014-06-21 00:51:41 -04:00
stats . AssignCount , _ = x . Where ( "assignee_id=?" , uid ) . And ( "is_closed=?" , false ) . Count ( issue )
stats . CreateCount , _ = x . Where ( "poster_id=?" , uid ) . And ( "is_closed=?" , false ) . Count ( issue )
2014-05-07 12:09:30 -04:00
return stats
}
2014-03-23 19:09:11 -04:00
// UpdateIssue updates information of issue.
func UpdateIssue ( issue * Issue ) error {
2015-07-24 21:02:49 +08:00
_ , err := x . Id ( issue . ID ) . AllCols ( ) . Update ( issue )
2014-07-23 13:48:06 +02:00
if err != nil {
return err
}
2014-05-07 16:51:14 -04:00
return err
}
// UpdateIssueUserByStatus updates issue-user pairs by issue status.
func UpdateIssueUserPairsByStatus ( iid int64 , isClosed bool ) error {
rawSql := "UPDATE `issue_user` SET is_closed = ? WHERE issue_id = ?"
2014-06-21 00:51:41 -04:00
_ , err := x . Exec ( rawSql , isClosed , iid )
2014-03-23 19:09:11 -04:00
return err
}
2014-05-08 17:17:45 -04:00
// UpdateIssueUserPairByAssignee updates issue-user pair for assigning.
func UpdateIssueUserPairByAssignee ( aid , iid int64 ) error {
rawSql := "UPDATE `issue_user` SET is_assigned = ? WHERE issue_id = ?"
2014-06-21 00:51:41 -04:00
if _ , err := x . Exec ( rawSql , false , iid ) ; err != nil {
2014-05-08 17:17:45 -04:00
return err
}
2014-05-11 13:46:36 -04:00
// Assignee ID equals to 0 means clear assignee.
if aid == 0 {
return nil
}
2014-12-24 20:47:45 +03:00
rawSql = "UPDATE `issue_user` SET is_assigned = ? WHERE uid = ? AND issue_id = ?"
_ , err := x . Exec ( rawSql , true , aid , iid )
2014-05-08 17:17:45 -04:00
return err
}
2014-05-07 16:51:14 -04:00
// 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 = ?"
2014-06-21 00:51:41 -04:00
_ , err := x . Exec ( rawSql , true , uid , iid )
2014-05-07 16:51:14 -04:00
return err
}
// UpdateIssueUserPairsByMentions updates issue-user pairs by mentioning.
func UpdateIssueUserPairsByMentions ( uids [ ] int64 , iid int64 ) error {
for _ , uid := range uids {
iu := & IssueUser { Uid : uid , IssueId : iid }
2014-06-21 00:51:41 -04:00
has , err := x . Get ( iu )
2014-05-07 16:51:14 -04:00
if err != nil {
return err
}
iu . IsMentioned = true
if has {
2014-06-21 00:51:41 -04:00
_ , err = x . Id ( iu . Id ) . AllCols ( ) . Update ( iu )
2014-05-07 16:51:14 -04:00
} else {
2014-06-21 00:51:41 -04:00
_ , err = x . Insert ( iu )
2014-05-07 16:51:14 -04:00
}
if err != nil {
return err
}
}
return nil
}
2014-05-24 02:31:58 -04:00
// .____ ___. .__
// | | _____ \_ |__ ____ | |
// | | \__ \ | __ \_/ __ \| |
// | |___ / __ \| \_\ \ ___/| |__
// |_______ (____ /___ /\___ >____/
// \/ \/ \/ \/
// Label represents a label of repository for issues.
type Label struct {
2015-07-24 21:02:49 +08:00
ID int64 ` xorm:"pk autoincr" `
2014-05-24 02:31:58 -04:00
RepoId int64 ` xorm:"INDEX" `
Name string
Color string ` xorm:"VARCHAR(7)" `
NumIssues int
NumClosedIssues int
NumOpenIssues int ` xorm:"-" `
IsChecked bool ` xorm:"-" `
}
// CalOpenIssues calculates the open issues of label.
func ( m * Label ) CalOpenIssues ( ) {
m . NumOpenIssues = m . NumIssues - m . NumClosedIssues
}
// NewLabel creates new label of repository.
func NewLabel ( l * Label ) error {
2014-06-21 00:51:41 -04:00
_ , err := x . Insert ( l )
2014-05-24 02:31:58 -04:00
return err
}
// GetLabelById returns a label by given ID.
func GetLabelById ( id int64 ) ( * Label , error ) {
2014-05-24 15:34:02 -04:00
if id <= 0 {
return nil , ErrLabelNotExist
}
2015-07-24 21:02:49 +08:00
l := & Label { ID : id }
2014-06-21 00:51:41 -04:00
has , err := x . Get ( l )
2014-05-24 02:31:58 -04:00
if err != nil {
return nil , err
} else if ! has {
return nil , ErrLabelNotExist
}
return l , nil
}
// GetLabels returns a list of labels of given repository ID.
func GetLabels ( repoId int64 ) ( [ ] * Label , error ) {
labels := make ( [ ] * Label , 0 , 10 )
2014-06-21 00:51:41 -04:00
err := x . Where ( "repo_id=?" , repoId ) . Find ( & labels )
2014-05-24 02:31:58 -04:00
return labels , err
}
// UpdateLabel updates label information.
func UpdateLabel ( l * Label ) error {
2015-07-24 21:02:49 +08:00
_ , err := x . Id ( l . ID ) . AllCols ( ) . Update ( l )
2014-05-24 02:31:58 -04:00
return err
}
// DeleteLabel delete a label of given repository.
2015-07-24 23:13:42 +08:00
func DeleteLabel ( repoID , labelID int64 ) error {
l , err := GetLabelById ( labelID )
2014-05-24 02:31:58 -04:00
if err != nil {
if err == ErrLabelNotExist {
return nil
}
return err
}
2015-07-24 23:13:42 +08:00
issues , err := GetIssuesByLabel ( repoID , labelID )
2014-05-24 02:31:58 -04:00
if err != nil {
return err
}
2014-06-21 00:51:41 -04:00
sess := x . NewSession ( )
2015-07-24 23:13:42 +08:00
defer sessionRelease ( sess )
2014-05-24 02:31:58 -04:00
if err = sess . Begin ( ) ; err != nil {
return err
}
for _ , issue := range issues {
2015-07-24 23:13:42 +08:00
issue . LabelIds = strings . Replace ( issue . LabelIds , "$" + com . ToStr ( labelID ) + "|" , "" , - 1 )
2015-07-24 21:02:49 +08:00
if _ , err = sess . Id ( issue . ID ) . AllCols ( ) . Update ( issue ) ; err != nil {
2014-05-24 02:31:58 -04:00
return err
}
}
if _ , err = sess . Delete ( l ) ; err != nil {
return err
}
return sess . Commit ( )
}
2014-05-15 12:08:53 -04:00
// _____ .__.__ __
// / \ |__| | ____ _______/ |_ ____ ____ ____
// / \ / \| | | _/ __ \ / ___/\ __\/ _ \ / \_/ __ \
// / Y \ | |_\ ___/ \___ \ | | ( <_> ) | \ ___/
// \____|__ /__|____/\___ >____ > |__| \____/|___| /\___ >
// \/ \/ \/ \/ \/
2014-03-22 13:50:50 -04:00
// Milestone represents a milestone of repository.
type Milestone struct {
2015-08-03 17:42:09 +08:00
ID int64 ` xorm:"pk autoincr" `
2015-08-04 22:24:04 +08:00
RepoID int64 ` xorm:"INDEX" `
2014-05-12 14:06:42 -04:00
Index int64
2014-05-07 16:51:14 -04:00
Name string
2014-09-05 08:10:41 +07:00
Content string ` xorm:"TEXT" `
2014-05-12 14:06:42 -04:00
RenderedContent string ` xorm:"-" `
2014-05-07 16:51:14 -04:00
IsClosed bool
NumIssues int
NumClosedIssues int
2014-05-12 14:06:42 -04:00
NumOpenIssues int ` xorm:"-" `
2014-05-07 16:51:14 -04:00
Completeness int // Percentage(1-100).
Deadline time . Time
2014-05-13 13:28:21 -04:00
DeadlineString string ` xorm:"-" `
2015-08-05 15:24:26 +08:00
IsOverDue bool ` xorm:"-" `
2014-05-07 16:51:14 -04:00
ClosedDate time . Time
2014-03-22 13:50:50 -04:00
}
2015-08-04 22:24:04 +08:00
func ( m * Milestone ) BeforeSet ( colName string , val xorm . Cell ) {
if colName == "deadline" {
t := ( * val ) . ( time . Time )
if t . Year ( ) == 9999 {
return
}
m . DeadlineString = t . Format ( "2006-01-02" )
2015-08-05 15:24:26 +08:00
if time . Now ( ) . After ( t ) {
m . IsOverDue = true
}
2015-08-04 22:24:04 +08:00
}
}
2014-05-12 14:06:42 -04:00
// CalOpenIssues calculates the open issues of milestone.
func ( m * Milestone ) CalOpenIssues ( ) {
m . NumOpenIssues = m . NumIssues - m . NumClosedIssues
}
// NewMilestone creates new milestone of repository.
func NewMilestone ( m * Milestone ) ( err error ) {
2014-06-21 00:51:41 -04:00
sess := x . NewSession ( )
2014-05-12 14:06:42 -04:00
defer sess . Close ( )
if err = sess . Begin ( ) ; err != nil {
return err
}
if _ , err = sess . Insert ( m ) ; err != nil {
sess . Rollback ( )
return err
}
rawSql := "UPDATE `repository` SET num_milestones = num_milestones + 1 WHERE id = ?"
2015-08-04 22:24:04 +08:00
if _ , err = sess . Exec ( rawSql , m . RepoID ) ; err != nil {
2014-05-12 14:06:42 -04:00
sess . Rollback ( )
return err
}
return sess . Commit ( )
}
2015-08-04 22:24:04 +08:00
// MilestoneById returns the milestone by given ID.
func MilestoneById ( id int64 ) ( * Milestone , error ) {
2015-08-03 17:42:09 +08:00
m := & Milestone { ID : id }
2014-06-21 00:51:41 -04:00
has , err := x . Get ( m )
2014-05-14 10:55:36 -04:00
if err != nil {
return nil , err
} else if ! has {
2015-08-05 18:26:18 +08:00
return nil , ErrMilestoneNotExist { id , 0 }
2014-05-14 10:55:36 -04:00
}
return m , nil
}
2014-05-13 13:28:21 -04:00
// GetMilestoneByIndex returns the milestone of given repository and index.
func GetMilestoneByIndex ( repoId , idx int64 ) ( * Milestone , error ) {
2015-08-04 22:24:04 +08:00
m := & Milestone { RepoID : repoId , Index : idx }
2014-06-21 00:51:41 -04:00
has , err := x . Get ( m )
2014-05-13 13:28:21 -04:00
if err != nil {
return nil , err
} else if ! has {
2015-08-05 18:26:18 +08:00
return nil , ErrMilestoneNotExist { 0 , idx }
2014-05-13 13:28:21 -04:00
}
return m , nil
}
2015-08-05 11:18:24 +08:00
// GetMilestones returns a list of milestones of given repository and status.
func GetMilestones ( repoID int64 , page int , isClosed bool ) ( [ ] * Milestone , error ) {
2015-08-04 22:24:04 +08:00
miles := make ( [ ] * Milestone , 0 , setting . IssuePagingNum )
sess := x . Where ( "repo_id=? AND is_closed=?" , repoID , isClosed )
if page > 0 {
sess = sess . Limit ( setting . IssuePagingNum , ( page - 1 ) * setting . IssuePagingNum )
}
return miles , sess . Find ( & miles )
2014-05-12 14:06:42 -04:00
}
2014-05-13 13:28:21 -04:00
// UpdateMilestone updates information of given milestone.
func UpdateMilestone ( m * Milestone ) error {
2015-08-03 17:42:09 +08:00
_ , err := x . Id ( m . ID ) . AllCols ( ) . Update ( m )
2014-05-13 13:28:21 -04:00
return err
}
2015-08-04 22:24:04 +08:00
// CountClosedMilestones returns number of closed milestones in given repository.
func CountClosedMilestones ( repoID int64 ) int64 {
closed , _ := x . Where ( "repo_id=? AND is_closed=?" , repoID , true ) . Count ( new ( Milestone ) )
return closed
}
// 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 , CountClosedMilestones ( repoID )
}
2014-05-13 19:46:48 -04:00
// ChangeMilestoneStatus changes the milestone open/closed status.
func ChangeMilestoneStatus ( m * Milestone , isClosed bool ) ( err error ) {
2015-08-04 22:24:04 +08:00
repo , err := GetRepositoryById ( m . RepoID )
2014-05-13 19:46:48 -04:00
if err != nil {
return err
}
2014-06-21 00:51:41 -04:00
sess := x . NewSession ( )
2015-08-04 22:24:04 +08:00
defer sessionRelease ( sess )
2014-05-13 19:46:48 -04:00
if err = sess . Begin ( ) ; err != nil {
return err
}
m . IsClosed = isClosed
2015-08-04 22:24:04 +08:00
if err = UpdateMilestone ( m ) ; err != nil {
2014-05-13 19:46:48 -04:00
return err
}
2015-08-04 22:24:04 +08:00
repo . NumClosedMilestones = int ( CountClosedMilestones ( repo . Id ) )
if _ , err = sess . Id ( repo . Id ) . AllCols ( ) . Update ( repo ) ; err != nil {
2014-05-13 19:46:48 -04:00
return err
}
return sess . Commit ( )
}
2015-08-04 22:24:04 +08:00
// ChangeMilestoneIssueStats updates the open/closed issues counter and progress
// for the milestone associated witht the given issue.
2014-07-23 13:48:06 +02:00
func ChangeMilestoneIssueStats ( issue * Issue ) error {
if issue . MilestoneId == 0 {
return nil
}
2015-08-04 22:24:04 +08:00
m , err := MilestoneById ( issue . MilestoneId )
2014-07-23 13:48:06 +02:00
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 )
}
2014-05-14 10:55:36 -04:00
// ChangeMilestoneAssign changes assignment of milestone for issue.
2014-05-15 12:08:53 -04:00
func ChangeMilestoneAssign ( oldMid , mid int64 , issue * Issue ) ( err error ) {
2014-06-21 00:51:41 -04:00
sess := x . NewSession ( )
2014-05-14 10:55:36 -04:00
defer sess . Close ( )
if err = sess . Begin ( ) ; err != nil {
return err
}
if oldMid > 0 {
2015-08-04 22:24:04 +08:00
m , err := MilestoneById ( oldMid )
2014-05-14 10:55:36 -04:00
if err != nil {
return err
}
m . NumIssues --
2014-05-15 12:08:53 -04:00
if issue . IsClosed {
2014-05-14 10:55:36 -04:00
m . NumClosedIssues --
}
if m . NumIssues > 0 {
m . Completeness = m . NumClosedIssues * 100 / m . NumIssues
} else {
m . Completeness = 0
}
2014-07-23 13:48:06 +02:00
2015-08-03 17:42:09 +08:00
if _ , err = sess . Id ( m . ID ) . Cols ( "num_issues,num_completeness,num_closed_issues" ) . Update ( m ) ; err != nil {
2014-05-14 10:55:36 -04:00
sess . Rollback ( )
return err
}
2014-05-15 12:08:53 -04:00
rawSql := "UPDATE `issue_user` SET milestone_id = 0 WHERE issue_id = ?"
2015-07-24 21:02:49 +08:00
if _ , err = sess . Exec ( rawSql , issue . ID ) ; err != nil {
2014-05-15 12:08:53 -04:00
sess . Rollback ( )
return err
}
2014-05-14 10:55:36 -04:00
}
2014-05-14 11:14:51 -04:00
if mid > 0 {
2015-08-04 22:24:04 +08:00
m , err := MilestoneById ( mid )
2014-05-14 11:14:51 -04:00
if err != nil {
return err
}
2014-07-23 13:48:06 +02:00
2014-05-14 11:14:51 -04:00
m . NumIssues ++
2014-05-15 12:08:53 -04:00
if issue . IsClosed {
2014-05-14 11:14:51 -04:00
m . NumClosedIssues ++
}
2014-07-22 20:57:48 +02:00
if m . NumIssues == 0 {
return ErrWrongIssueCounter
}
2014-05-14 11:14:51 -04:00
m . Completeness = m . NumClosedIssues * 100 / m . NumIssues
2015-08-03 17:42:09 +08:00
if _ , err = sess . Id ( m . ID ) . Cols ( "num_issues,num_completeness,num_closed_issues" ) . Update ( m ) ; err != nil {
2014-05-14 11:14:51 -04:00
sess . Rollback ( )
return err
}
2014-05-15 12:08:53 -04:00
rawSql := "UPDATE `issue_user` SET milestone_id = ? WHERE issue_id = ?"
2015-08-03 17:42:09 +08:00
if _ , err = sess . Exec ( rawSql , m . ID , issue . ID ) ; err != nil {
2014-05-15 12:08:53 -04:00
sess . Rollback ( )
return err
}
2014-05-14 10:55:36 -04:00
}
2014-07-23 13:48:06 +02:00
2014-05-14 10:55:36 -04:00
return sess . Commit ( )
}
2014-05-13 19:46:48 -04:00
// DeleteMilestone deletes a milestone.
func DeleteMilestone ( m * Milestone ) ( err error ) {
2014-06-21 00:51:41 -04:00
sess := x . NewSession ( )
2014-05-13 19:46:48 -04:00
defer sess . Close ( )
if err = sess . Begin ( ) ; err != nil {
return err
}
if _ , err = sess . Delete ( m ) ; err != nil {
sess . Rollback ( )
return err
}
rawSql := "UPDATE `repository` SET num_milestones = num_milestones - 1 WHERE id = ?"
2015-08-04 22:24:04 +08:00
if _ , err = sess . Exec ( rawSql , m . RepoID ) ; err != nil {
2014-05-13 19:46:48 -04:00
sess . Rollback ( )
return err
}
rawSql = "UPDATE `issue` SET milestone_id = 0 WHERE milestone_id = ?"
2015-08-03 17:42:09 +08:00
if _ , err = sess . Exec ( rawSql , m . ID ) ; err != nil {
2014-05-13 19:46:48 -04:00
sess . Rollback ( )
return err
}
2014-05-15 12:08:53 -04:00
rawSql = "UPDATE `issue_user` SET milestone_id = 0 WHERE milestone_id = ?"
2015-08-03 17:42:09 +08:00
if _ , err = sess . Exec ( rawSql , m . ID ) ; err != nil {
2014-05-15 12:08:53 -04:00
sess . Rollback ( )
return err
}
2014-05-13 19:46:48 -04:00
return sess . Commit ( )
}
2014-05-15 12:08:53 -04:00
// _________ __
// \_ ___ \ ____ _____ _____ ____ _____/ |_
// / \ \/ / _ \ / \ / \_/ __ \ / \ __\
// \ \___( <_> ) Y Y \ Y Y \ ___/| | \ |
// \______ /\____/|__|_| /__|_| /\___ >___| /__|
// \/ \/ \/ \/ \/
2014-07-24 10:15:05 +02:00
// CommentType defines whether a comment is just a simple comment, an action (like close) or a reference.
type CommentType int
2014-03-29 17:50:51 -04:00
const (
2014-07-24 10:15:05 +02:00
// Plain comment, can be associated with a commit (CommitId > 0) and a line (Line > 0)
2015-02-06 20:47:21 -05:00
COMMENT_TYPE_COMMENT CommentType = iota
COMMENT_TYPE_REOPEN
COMMENT_TYPE_CLOSE
2014-07-24 10:15:05 +02:00
2015-02-06 20:47:21 -05:00
// References.
COMMENT_TYPE_ISSUE
2014-07-24 10:15:05 +02:00
// Reference from some commit (not part of a pull request)
2015-02-06 20:47:21 -05:00
COMMENT_TYPE_COMMIT
2014-07-24 10:15:05 +02:00
// Reference from some pull request
2015-02-06 20:47:21 -05:00
COMMENT_TYPE_PULL
2014-03-29 17:50:51 -04:00
)
2014-03-22 13:50:50 -04:00
// Comment represents a comment in commit and issue page.
2014-03-20 16:04:56 -04:00
type Comment struct {
2014-03-22 13:50:50 -04:00
Id int64
2014-07-24 10:15:05 +02:00
Type CommentType
2014-03-22 13:50:50 -04:00
PosterId int64
2014-03-26 12:31:01 -04:00
Poster * User ` xorm:"-" `
2014-03-22 13:50:50 -04:00
IssueId int64
CommitId int64
2014-03-26 12:31:01 -04:00
Line int64
2014-06-02 21:59:56 -04:00
Content string ` xorm:"TEXT" `
2014-05-07 12:09:30 -04:00
Created time . Time ` xorm:"CREATED" `
2014-03-20 16:04:56 -04:00
}
2014-03-26 12:31:01 -04:00
// CreateComment creates comment of issue or commit.
2014-07-24 12:29:37 +02:00
func CreateComment ( userId , repoId , issueId , commitId , line int64 , cmtType CommentType , content string , attachments [ ] int64 ) ( * Comment , error ) {
2014-06-21 00:51:41 -04:00
sess := x . NewSession ( )
2015-07-25 02:52:25 +08:00
defer sessionRelease ( sess )
2014-05-07 16:51:14 -04:00
if err := sess . Begin ( ) ; err != nil {
2014-07-23 21:15:47 +02:00
return nil , err
2014-05-07 16:51:14 -04:00
}
2014-03-26 16:41:16 -04:00
2014-07-23 21:15:47 +02:00
comment := & Comment { PosterId : userId , Type : cmtType , IssueId : issueId ,
CommitId : commitId , Line : line , Content : content }
if _ , err := sess . Insert ( comment ) ; err != nil {
return nil , err
2014-03-26 16:41:16 -04:00
}
2014-03-29 17:50:51 -04:00
// Check comment type.
switch cmtType {
2015-02-06 20:47:21 -05:00
case COMMENT_TYPE_COMMENT :
2014-03-29 17:50:51 -04:00
rawSql := "UPDATE `issue` SET num_comments = num_comments + 1 WHERE id = ?"
if _ , err := sess . Exec ( rawSql , issueId ) ; err != nil {
2014-07-23 21:15:47 +02:00
return nil , err
}
if len ( attachments ) > 0 {
rawSql = "UPDATE `attachment` SET comment_id = ? WHERE id IN (?)"
astrs := make ( [ ] string , 0 , len ( attachments ) )
for _ , a := range attachments {
astrs = append ( astrs , strconv . FormatInt ( a , 10 ) )
}
if _ , err := sess . Exec ( rawSql , comment . Id , strings . Join ( astrs , "," ) ) ; err != nil {
return nil , err
}
2014-03-29 17:50:51 -04:00
}
2015-02-06 20:47:21 -05:00
case COMMENT_TYPE_REOPEN :
2014-03-29 17:50:51 -04:00
rawSql := "UPDATE `repository` SET num_closed_issues = num_closed_issues - 1 WHERE id = ?"
if _ , err := sess . Exec ( rawSql , repoId ) ; err != nil {
2014-07-23 21:15:47 +02:00
return nil , err
2014-03-29 17:50:51 -04:00
}
2015-02-06 20:47:21 -05:00
case COMMENT_TYPE_CLOSE :
2014-03-29 17:50:51 -04:00
rawSql := "UPDATE `repository` SET num_closed_issues = num_closed_issues + 1 WHERE id = ?"
if _ , err := sess . Exec ( rawSql , repoId ) ; err != nil {
2014-07-23 21:15:47 +02:00
return nil , err
2014-03-29 17:50:51 -04:00
}
2014-03-26 16:41:16 -04:00
}
2014-07-23 21:15:47 +02:00
return comment , sess . Commit ( )
2014-03-26 12:31:01 -04:00
}
2014-07-24 09:04:09 +02:00
// GetCommentById returns the comment with the given id
func GetCommentById ( commentId int64 ) ( * Comment , error ) {
c := & Comment { Id : commentId }
_ , err := x . Get ( c )
return c , err
}
2014-07-24 10:15:05 +02:00
func ( c * Comment ) ContentHtml ( ) template . HTML {
return template . HTML ( c . Content )
}
2014-03-26 12:31:01 -04:00
// GetIssueComments returns list of comment by given issue id.
func GetIssueComments ( issueId int64 ) ( [ ] Comment , error ) {
comments := make ( [ ] Comment , 0 , 10 )
2014-06-21 00:51:41 -04:00
err := x . Asc ( "created" ) . Find ( & comments , & Comment { IssueId : issueId } )
2014-03-26 12:31:01 -04:00
return comments , err
}
2014-07-23 21:15:47 +02:00
// Attachments returns the attachments for this comment.
2014-07-23 21:24:24 +02:00
func ( c * Comment ) Attachments ( ) [ ] * Attachment {
a , _ := GetAttachmentsByComment ( c . Id )
return a
2014-07-23 21:15:47 +02:00
}
func ( c * Comment ) AfterDelete ( ) {
_ , err := DeleteAttachmentsByComment ( c . Id , true )
if err != nil {
log . Info ( "Could not delete files for comment %d on issue #%d: %s" , c . Id , c . IssueId , err )
}
}
type Attachment struct {
Id int64
IssueId int64
CommentId int64
Name string
2014-07-24 17:02:42 +02:00
Path string ` xorm:"TEXT" `
2014-07-23 21:15:47 +02:00
Created time . Time ` xorm:"CREATED" `
}
// CreateAttachment creates a new attachment inside the database and
func CreateAttachment ( issueId , commentId int64 , name , path string ) ( * Attachment , error ) {
sess := x . NewSession ( )
defer sess . Close ( )
if err := sess . Begin ( ) ; err != nil {
return nil , err
}
a := & Attachment { IssueId : issueId , CommentId : commentId , Name : name , Path : path }
if _ , err := sess . Insert ( a ) ; err != nil {
sess . Rollback ( )
return nil , err
}
return a , sess . Commit ( )
}
// Attachment returns the attachment by given ID.
func GetAttachmentById ( id int64 ) ( * Attachment , error ) {
m := & Attachment { Id : id }
has , err := x . Get ( m )
if err != nil {
return nil , err
}
if ! has {
return nil , ErrAttachmentNotExist
}
return m , nil
}
2014-07-23 21:24:24 +02:00
func GetAttachmentsForIssue ( issueId int64 ) ( [ ] * Attachment , error ) {
attachments := make ( [ ] * Attachment , 0 , 10 )
2014-07-24 09:04:09 +02:00
err := x . Where ( "issue_id = ?" , issueId ) . And ( "comment_id = 0" ) . Find ( & attachments )
2014-07-23 21:24:24 +02:00
return attachments , err
}
2014-07-23 21:15:47 +02:00
// GetAttachmentsByIssue returns a list of attachments for the given issue
func GetAttachmentsByIssue ( issueId int64 ) ( [ ] * Attachment , error ) {
attachments := make ( [ ] * Attachment , 0 , 10 )
2014-07-24 09:04:09 +02:00
err := x . Where ( "issue_id = ?" , issueId ) . And ( "comment_id > 0" ) . Find ( & attachments )
2014-07-23 21:15:47 +02:00
return attachments , err
}
// GetAttachmentsByComment returns a list of attachments for the given comment
func GetAttachmentsByComment ( commentId int64 ) ( [ ] * Attachment , error ) {
attachments := make ( [ ] * Attachment , 0 , 10 )
err := x . Where ( "comment_id = ?" , commentId ) . Find ( & attachments )
return attachments , err
}
// 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 . Path ) ; err != nil {
return i , err
}
}
if _ , err := x . Delete ( a . Id ) ; 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 := GetAttachmentsByIssue ( 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 := GetAttachmentsByComment ( commentId )
if err != nil {
return 0 , err
}
return DeleteAttachments ( attachments , remove )
}