attachment.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. package models
  2. import (
  3. "fmt"
  4. "gitote/gitote/pkg/setting"
  5. "io"
  6. "mime/multipart"
  7. "os"
  8. "path"
  9. "time"
  10. "github.com/go-xorm/xorm"
  11. gouuid "github.com/satori/go.uuid"
  12. )
  13. // Attachment represent a attachment of issue/comment/release.
  14. type Attachment struct {
  15. ID int64
  16. UUID string `xorm:"uuid UNIQUE"`
  17. IssueID int64 `xorm:"INDEX"`
  18. CommentID int64
  19. ReleaseID int64 `xorm:"INDEX"`
  20. Name string
  21. Created time.Time `xorm:"-" json:"-"`
  22. CreatedUnix int64
  23. }
  24. func (a *Attachment) BeforeInsert() {
  25. a.CreatedUnix = time.Now().Unix()
  26. }
  27. func (a *Attachment) AfterSet(colName string, _ xorm.Cell) {
  28. switch colName {
  29. case "created_unix":
  30. a.Created = time.Unix(a.CreatedUnix, 0).Local()
  31. }
  32. }
  33. // AttachmentLocalPath returns where attachment is stored in local file system based on given UUID.
  34. func AttachmentLocalPath(uuid string) string {
  35. return path.Join(setting.AttachmentPath, uuid[0:1], uuid[1:2], uuid)
  36. }
  37. // LocalPath returns where attachment is stored in local file system.
  38. func (attach *Attachment) LocalPath() string {
  39. return AttachmentLocalPath(attach.UUID)
  40. }
  41. // NewAttachment creates a new attachment object.
  42. func NewAttachment(name string, buf []byte, file multipart.File) (_ *Attachment, err error) {
  43. attach := &Attachment{
  44. UUID: gouuid.NewV4().String(),
  45. Name: name,
  46. }
  47. localPath := attach.LocalPath()
  48. if err = os.MkdirAll(path.Dir(localPath), os.ModePerm); err != nil {
  49. return nil, fmt.Errorf("MkdirAll: %v", err)
  50. }
  51. fw, err := os.Create(localPath)
  52. if err != nil {
  53. return nil, fmt.Errorf("Create: %v", err)
  54. }
  55. defer fw.Close()
  56. if _, err = fw.Write(buf); err != nil {
  57. return nil, fmt.Errorf("Write: %v", err)
  58. } else if _, err = io.Copy(fw, file); err != nil {
  59. return nil, fmt.Errorf("Copy: %v", err)
  60. }
  61. if _, err := x.Insert(attach); err != nil {
  62. return nil, err
  63. }
  64. return attach, nil
  65. }
  66. func getAttachmentByUUID(e Engine, uuid string) (*Attachment, error) {
  67. attach := &Attachment{UUID: uuid}
  68. has, err := x.Get(attach)
  69. if err != nil {
  70. return nil, err
  71. } else if !has {
  72. return nil, ErrAttachmentNotExist{0, uuid}
  73. }
  74. return attach, nil
  75. }
  76. func getAttachmentsByUUIDs(e Engine, uuids []string) ([]*Attachment, error) {
  77. if len(uuids) == 0 {
  78. return []*Attachment{}, nil
  79. }
  80. // Silently drop invalid uuids.
  81. attachments := make([]*Attachment, 0, len(uuids))
  82. return attachments, e.In("uuid", uuids).Find(&attachments)
  83. }
  84. // GetAttachmentByUUID returns attachment by given UUID.
  85. func GetAttachmentByUUID(uuid string) (*Attachment, error) {
  86. return getAttachmentByUUID(x, uuid)
  87. }
  88. func getAttachmentsByIssueID(e Engine, issueID int64) ([]*Attachment, error) {
  89. attachments := make([]*Attachment, 0, 5)
  90. return attachments, e.Where("issue_id = ? AND comment_id = 0", issueID).Find(&attachments)
  91. }
  92. // GetAttachmentsByIssueID returns all attachments of an issue.
  93. func GetAttachmentsByIssueID(issueID int64) ([]*Attachment, error) {
  94. return getAttachmentsByIssueID(x, issueID)
  95. }
  96. func getAttachmentsByCommentID(e Engine, commentID int64) ([]*Attachment, error) {
  97. attachments := make([]*Attachment, 0, 5)
  98. return attachments, e.Where("comment_id=?", commentID).Find(&attachments)
  99. }
  100. // GetAttachmentsByCommentID returns all attachments of a comment.
  101. func GetAttachmentsByCommentID(commentID int64) ([]*Attachment, error) {
  102. return getAttachmentsByCommentID(x, commentID)
  103. }
  104. func getAttachmentsByReleaseID(e Engine, releaseID int64) ([]*Attachment, error) {
  105. attachments := make([]*Attachment, 0, 10)
  106. return attachments, e.Where("release_id = ?", releaseID).Find(&attachments)
  107. }
  108. // GetAttachmentsByReleaseID returns all attachments of a release.
  109. func GetAttachmentsByReleaseID(releaseID int64) ([]*Attachment, error) {
  110. return getAttachmentsByReleaseID(x, releaseID)
  111. }
  112. // DeleteAttachment deletes the given attachment and optionally the associated file.
  113. func DeleteAttachment(a *Attachment, remove bool) error {
  114. _, err := DeleteAttachments([]*Attachment{a}, remove)
  115. return err
  116. }
  117. // DeleteAttachments deletes the given attachments and optionally the associated files.
  118. func DeleteAttachments(attachments []*Attachment, remove bool) (int, error) {
  119. for i, a := range attachments {
  120. if remove {
  121. if err := os.Remove(a.LocalPath()); err != nil {
  122. return i, err
  123. }
  124. }
  125. if _, err := x.Delete(a); err != nil {
  126. return i, err
  127. }
  128. }
  129. return len(attachments), nil
  130. }
  131. // DeleteAttachmentsByIssue deletes all attachments associated with the given issue.
  132. func DeleteAttachmentsByIssue(issueId int64, remove bool) (int, error) {
  133. attachments, err := GetAttachmentsByIssueID(issueId)
  134. if err != nil {
  135. return 0, err
  136. }
  137. return DeleteAttachments(attachments, remove)
  138. }
  139. // DeleteAttachmentsByComment deletes all attachments associated with the given comment.
  140. func DeleteAttachmentsByComment(commentId int64, remove bool) (int, error) {
  141. attachments, err := GetAttachmentsByCommentID(commentId)
  142. if err != nil {
  143. return 0, err
  144. }
  145. return DeleteAttachments(attachments, remove)
  146. }