summaryrefslogtreecommitdiffstats
path: root/models
diff options
context:
space:
mode:
Diffstat (limited to 'models')
-rw-r--r--models/action.go4
-rw-r--r--models/issue.go213
-rw-r--r--models/models.go2
3 files changed, 202 insertions, 17 deletions
diff --git a/models/action.go b/models/action.go
index 362b238f26..f6684baa0a 100644
--- a/models/action.go
+++ b/models/action.go
@@ -127,7 +127,7 @@ func updateIssuesCommit(userId, repoId int64, repoUserName, repoName string, com
url := fmt.Sprintf("/%s/%s/commit/%s", repoUserName, repoName, c.Sha1)
message := fmt.Sprintf(`<a href="%s">%s</a>`, url, c.Message)
- if err = CreateComment(userId, issue.RepoId, issue.Id, 0, 0, COMMIT, message); err != nil {
+ if _, err = CreateComment(userId, issue.RepoId, issue.Id, 0, 0, COMMIT, message, nil); err != nil {
return err
}
@@ -159,7 +159,7 @@ func updateIssuesCommit(userId, repoId int64, repoUserName, repoName string, com
}
// If commit happened in the referenced repository, it means the issue can be closed.
- if err = CreateComment(userId, repoId, issue.Id, 0, 0, CLOSE, ""); err != nil {
+ if _, err = CreateComment(userId, repoId, issue.Id, 0, 0, CLOSE, "", nil); err != nil {
return err
}
}
diff --git a/models/issue.go b/models/issue.go
index fb84ffa841..f16c384b6f 100644
--- a/models/issue.go
+++ b/models/issue.go
@@ -8,6 +8,7 @@ import (
"bytes"
"errors"
"html/template"
+ "os"
"strconv"
"strings"
"time"
@@ -15,14 +16,17 @@ import (
"github.com/go-xorm/xorm"
"github.com/gogits/gogs/modules/base"
+ "github.com/gogits/gogs/modules/log"
)
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")
- ErrMissingIssueNumber = errors.New("No issue number specified")
+ 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")
+ ErrAttachmentNotExist = errors.New("Attachment does not exist")
+ ErrAttachmentNotLinked = errors.New("Attachment does not belong to this issue")
+ ErrMissingIssueNumber = errors.New("No issue number specified")
)
// Issue represents an issue or pull request of repository.
@@ -94,6 +98,19 @@ func (i *Issue) GetAssignee() (err error) {
return err
}
+func (i *Issue) Attachments() []*Attachment {
+ a, _ := GetAttachmentsForIssue(i.Id)
+ return a
+}
+
+func (i *Issue) AfterDelete() {
+ _, err := DeleteAttachmentsByIssue(i.Id, true)
+
+ if err != nil {
+ log.Info("Could not delete files for issue #%d: %s", i.Id, err)
+ }
+}
+
// CreateIssue creates new issue for repository.
func NewIssue(issue *Issue) (err error) {
sess := x.NewSession()
@@ -871,17 +888,19 @@ type Comment struct {
}
// CreateComment creates comment of issue or commit.
-func CreateComment(userId, repoId, issueId, commitId, line int64, cmtType CommentType, content string) error {
+func CreateComment(userId, repoId, issueId, commitId, line int64, cmtType CommentType, content string, attachments []int64) (*Comment, error) {
sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
- return err
+ return nil, err
}
- if _, err := sess.Insert(&Comment{PosterId: userId, Type: cmtType, IssueId: issueId,
- CommitId: commitId, Line: line, Content: content}); err != nil {
+ comment := &Comment{PosterId: userId, Type: cmtType, IssueId: issueId,
+ CommitId: commitId, Line: line, Content: content}
+
+ if _, err := sess.Insert(comment); err != nil {
sess.Rollback()
- return err
+ return nil, err
}
// Check comment type.
@@ -890,22 +909,46 @@ func CreateComment(userId, repoId, issueId, commitId, line int64, cmtType Commen
rawSql := "UPDATE `issue` SET num_comments = num_comments + 1 WHERE id = ?"
if _, err := sess.Exec(rawSql, issueId); err != nil {
sess.Rollback()
- return err
+ 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 {
+ sess.Rollback()
+ return nil, err
+ }
}
case REOPEN:
rawSql := "UPDATE `repository` SET num_closed_issues = num_closed_issues - 1 WHERE id = ?"
if _, err := sess.Exec(rawSql, repoId); err != nil {
sess.Rollback()
- return err
+ return nil, err
}
case CLOSE:
rawSql := "UPDATE `repository` SET num_closed_issues = num_closed_issues + 1 WHERE id = ?"
if _, err := sess.Exec(rawSql, repoId); err != nil {
sess.Rollback()
- return err
+ return nil, err
}
}
- return sess.Commit()
+
+ return comment, sess.Commit()
+}
+
+// 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
}
func (c *Comment) ContentHtml() template.HTML {
@@ -918,3 +961,145 @@ func GetIssueComments(issueId int64) ([]Comment, error) {
err := x.Asc("created").Find(&comments, &Comment{IssueId: issueId})
return comments, err
}
+
+// Attachments returns the attachments for this comment.
+func (c *Comment) Attachments() []*Attachment {
+ a, _ := GetAttachmentsByComment(c.Id)
+ return a
+}
+
+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
+ Path string
+ 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
+}
+
+func GetAttachmentsForIssue(issueId int64) ([]*Attachment, error) {
+ attachments := make([]*Attachment, 0, 10)
+ err := x.Where("issue_id = ?", issueId).And("comment_id = 0").Find(&attachments)
+ return attachments, err
+}
+
+// GetAttachmentsByIssue returns a list of attachments for the given issue
+func GetAttachmentsByIssue(issueId int64) ([]*Attachment, error) {
+ attachments := make([]*Attachment, 0, 10)
+ err := x.Where("issue_id = ?", issueId).And("comment_id > 0").Find(&attachments)
+ 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)
+}
+
+// AssignAttachment assigns the given attachment to the specified comment
+func AssignAttachment(issueId, commentId, attachmentId int64) error {
+ a, err := GetAttachmentById(attachmentId)
+
+ if err != nil {
+ return err
+ }
+
+ if a.IssueId != issueId {
+ return ErrAttachmentNotLinked
+ }
+
+ a.CommentId = commentId
+
+ _, err = x.Id(a.Id).Update(a)
+ return err
+}
diff --git a/models/models.go b/models/models.go
index ded8b05984..31509ed349 100644
--- a/models/models.go
+++ b/models/models.go
@@ -36,7 +36,7 @@ func init() {
new(Action), new(Access), new(Issue), new(Comment), new(Oauth2), new(Follow),
new(Mirror), new(Release), new(LoginSource), new(Webhook), new(IssueUser),
new(Milestone), new(Label), new(HookTask), new(Team), new(OrgUser), new(TeamUser),
- new(UpdateTask))
+ new(UpdateTask), new(Attachment))
}
func LoadModelsConfig() {