2017-01-20 09:58:46 +03:00
// Copyright 2017 The Gitea 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
import (
"fmt"
"io"
"mime/multipart"
"os"
"path"
"code.gitea.io/gitea/modules/setting"
2017-12-11 07:37:04 +03:00
"code.gitea.io/gitea/modules/util"
2018-03-06 04:22:16 +03:00
api "code.gitea.io/sdk/gitea"
"github.com/go-xorm/xorm"
gouuid "github.com/satori/go.uuid"
2017-01-20 09:58:46 +03:00
)
// Attachment represent a attachment of issue/comment/release.
type Attachment struct {
2017-04-20 05:31:31 +03:00
ID int64 ` xorm:"pk autoincr" `
UUID string ` xorm:"uuid UNIQUE" `
IssueID int64 ` xorm:"INDEX" `
ReleaseID int64 ` xorm:"INDEX" `
CommentID int64
Name string
2017-12-11 07:37:04 +03:00
DownloadCount int64 ` xorm:"DEFAULT 0" `
2018-03-31 04:10:44 +03:00
Size int64 ` xorm:"DEFAULT 0" `
2017-12-11 07:37:04 +03:00
CreatedUnix util . TimeStamp ` xorm:"created" `
2017-01-20 09:58:46 +03:00
}
2017-04-20 05:31:31 +03:00
// IncreaseDownloadCount is update download count + 1
func ( a * Attachment ) IncreaseDownloadCount ( ) error {
// Update download count.
2017-12-11 10:16:23 +03:00
if _ , err := x . Exec ( "UPDATE `attachment` SET download_count=download_count+1 WHERE id=?" , a . ID ) ; err != nil {
2017-04-20 05:31:31 +03:00
return fmt . Errorf ( "increase attachment count: %v" , err )
}
return nil
}
2018-03-06 04:22:16 +03:00
// APIFormat converts models.Attachment to api.Attachment
func ( a * Attachment ) APIFormat ( ) * api . Attachment {
return & api . Attachment {
ID : a . ID ,
Name : a . Name ,
Created : a . CreatedUnix . AsTime ( ) ,
DownloadCount : a . DownloadCount ,
2018-03-31 04:10:44 +03:00
Size : a . Size ,
2018-03-06 04:22:16 +03:00
UUID : a . UUID ,
DownloadURL : a . DownloadURL ( ) ,
}
}
2017-01-20 09:58:46 +03:00
// AttachmentLocalPath returns where attachment is stored in local file
// system based on given UUID.
func AttachmentLocalPath ( uuid string ) string {
return path . Join ( setting . AttachmentPath , uuid [ 0 : 1 ] , uuid [ 1 : 2 ] , uuid )
}
// LocalPath returns where attachment is stored in local file system.
func ( a * Attachment ) LocalPath ( ) string {
return AttachmentLocalPath ( a . UUID )
}
2018-03-06 04:22:16 +03:00
// DownloadURL returns the download url of the attached file
func ( a * Attachment ) DownloadURL ( ) string {
return fmt . Sprintf ( "%sattachments/%s" , setting . AppURL , a . UUID )
}
2017-01-20 09:58:46 +03:00
// NewAttachment creates a new attachment object.
func NewAttachment ( name string , buf [ ] byte , file multipart . File ) ( _ * Attachment , err error ) {
attach := & Attachment {
UUID : gouuid . NewV4 ( ) . String ( ) ,
Name : name ,
}
localPath := attach . LocalPath ( )
if err = os . MkdirAll ( path . Dir ( localPath ) , os . ModePerm ) ; err != nil {
return nil , fmt . Errorf ( "MkdirAll: %v" , err )
}
fw , err := os . Create ( localPath )
if err != nil {
return nil , fmt . Errorf ( "Create: %v" , err )
}
defer fw . Close ( )
if _ , err = fw . Write ( buf ) ; err != nil {
return nil , fmt . Errorf ( "Write: %v" , err )
} else if _ , err = io . Copy ( fw , file ) ; err != nil {
return nil , fmt . Errorf ( "Copy: %v" , err )
}
2018-03-31 04:10:44 +03:00
// Update file size
var fi os . FileInfo
if fi , err = fw . Stat ( ) ; err != nil {
return nil , fmt . Errorf ( "file size: %v" , err )
}
attach . Size = fi . Size ( )
2017-01-20 09:58:46 +03:00
if _ , err := x . Insert ( attach ) ; err != nil {
return nil , err
}
return attach , nil
}
2018-03-06 04:22:16 +03:00
// GetAttachmentByID returns attachment by given id
func GetAttachmentByID ( id int64 ) ( * Attachment , error ) {
return getAttachmentByID ( x , id )
}
func getAttachmentByID ( e Engine , id int64 ) ( * Attachment , error ) {
attach := & Attachment { ID : id }
if has , err := e . Get ( attach ) ; err != nil {
return nil , err
} else if ! has {
return nil , ErrAttachmentNotExist { ID : id , UUID : "" }
}
return attach , nil
}
2017-01-20 09:58:46 +03:00
func getAttachmentByUUID ( e Engine , uuid string ) ( * Attachment , error ) {
attach := & Attachment { UUID : uuid }
2017-02-27 05:16:35 +03:00
has , err := e . Get ( attach )
2017-01-20 09:58:46 +03:00
if err != nil {
return nil , err
} else if ! has {
return nil , ErrAttachmentNotExist { 0 , uuid }
}
return attach , nil
}
func getAttachmentsByUUIDs ( e Engine , uuids [ ] string ) ( [ ] * Attachment , error ) {
if len ( uuids ) == 0 {
return [ ] * Attachment { } , nil
}
// Silently drop invalid uuids.
attachments := make ( [ ] * Attachment , 0 , len ( uuids ) )
return attachments , e . In ( "uuid" , uuids ) . Find ( & attachments )
}
// GetAttachmentByUUID returns attachment by given UUID.
func GetAttachmentByUUID ( uuid string ) ( * Attachment , error ) {
return getAttachmentByUUID ( x , uuid )
}
func getAttachmentsByIssueID ( e Engine , issueID int64 ) ( [ ] * Attachment , error ) {
attachments := make ( [ ] * Attachment , 0 , 10 )
return attachments , e . Where ( "issue_id = ? AND comment_id = 0" , issueID ) . Find ( & attachments )
}
// GetAttachmentsByIssueID returns all attachments of an issue.
func GetAttachmentsByIssueID ( issueID int64 ) ( [ ] * Attachment , error ) {
return getAttachmentsByIssueID ( x , issueID )
}
// GetAttachmentsByCommentID returns all attachments if comment by given ID.
func GetAttachmentsByCommentID ( commentID int64 ) ( [ ] * Attachment , error ) {
2017-10-01 19:52:35 +03:00
return getAttachmentsByCommentID ( x , commentID )
}
func getAttachmentsByCommentID ( e Engine , commentID int64 ) ( [ ] * Attachment , error ) {
2017-01-20 09:58:46 +03:00
attachments := make ( [ ] * Attachment , 0 , 10 )
return attachments , x . Where ( "comment_id=?" , commentID ) . Find ( & attachments )
}
// 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 ) {
2017-12-25 00:04:22 +03:00
if len ( attachments ) == 0 {
return 0 , nil
}
var ids = make ( [ ] int64 , 0 , len ( attachments ) )
for _ , a := range attachments {
ids = append ( ids , a . ID )
}
cnt , err := x . In ( "id" , ids ) . NoAutoCondition ( ) . Delete ( attachments [ 0 ] )
if err != nil {
return 0 , err
}
if remove {
for i , a := range attachments {
2017-01-20 09:58:46 +03:00
if err := os . Remove ( a . LocalPath ( ) ) ; err != nil {
return i , err
}
}
}
2017-12-25 00:04:22 +03:00
return int ( cnt ) , nil
2017-01-20 09:58:46 +03:00
}
// DeleteAttachmentsByIssue deletes all attachments associated with the given issue.
func DeleteAttachmentsByIssue ( issueID int64 , remove bool ) ( int , error ) {
attachments , err := GetAttachmentsByIssueID ( 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 := GetAttachmentsByCommentID ( commentID )
if err != nil {
return 0 , err
}
return DeleteAttachments ( attachments , remove )
}
2018-03-06 04:22:16 +03:00
// UpdateAttachment updates the given attachment in database
func UpdateAttachment ( atta * Attachment ) error {
return updateAttachment ( x , atta )
}
func updateAttachment ( e Engine , atta * Attachment ) error {
var sess * xorm . Session
if atta . ID != 0 && atta . UUID == "" {
sess = e . ID ( atta . ID )
} else {
// Use uuid only if id is not set and uuid is set
sess = e . Where ( "uuid = ?" , atta . UUID )
}
_ , err := sess . Cols ( "name" , "issue_id" , "release_id" , "comment_id" , "download_count" ) . Update ( atta )
return err
}