You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

188 lines
5.1 KiB

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