From 1610b9f547982af08e771e979718ae9e84b573b6 Mon Sep 17 00:00:00 2001 From: Philip Couling Date: Fri, 20 Jan 2017 06:58:46 +0000 Subject: [PATCH] Spun attachments into seperate go file (#701) Moved attachments into seperate go file --- models/attachment.go | 176 +++++++++++++++++++++++++++++++++++++ models/issue.go | 160 --------------------------------- routers/repo/attachment.go | 73 +++++++++++++++ routers/repo/issue.go | 57 ------------ 4 files changed, 249 insertions(+), 217 deletions(-) create mode 100644 models/attachment.go create mode 100644 routers/repo/attachment.go diff --git a/models/attachment.go b/models/attachment.go new file mode 100644 index 000000000..149d9ea5b --- /dev/null +++ b/models/attachment.go @@ -0,0 +1,176 @@ +// 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" + "time" + + "github.com/go-xorm/xorm" + gouuid "github.com/satori/go.uuid" + + "code.gitea.io/gitea/modules/setting" +) + +// Attachment represent a attachment of issue/comment/release. +type Attachment struct { + ID int64 `xorm:"pk autoincr"` + UUID string `xorm:"uuid UNIQUE"` + IssueID int64 `xorm:"INDEX"` + CommentID int64 + ReleaseID int64 `xorm:"INDEX"` + Name string + + Created time.Time `xorm:"-"` + CreatedUnix int64 +} + + +// BeforeInsert is invoked from XORM before inserting an object of this type. +func (a *Attachment) BeforeInsert() { + a.CreatedUnix = time.Now().Unix() +} + +// AfterSet is invoked from XORM after setting the value of a field of +// this object. +func (a *Attachment) AfterSet(colName string, _ xorm.Cell) { + switch colName { + case "created_unix": + a.Created = time.Unix(a.CreatedUnix, 0).Local() + } +} + +// 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) +} + +// 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) + } + + if _, err := x.Insert(attach); err != nil { + return nil, err + } + + return attach, nil +} + +func getAttachmentByUUID(e Engine, uuid string) (*Attachment, error) { + attach := &Attachment{UUID: uuid} + has, err := x.Get(attach) + 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) { + 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) { + for i, a := range attachments { + if remove { + if err := os.Remove(a.LocalPath()); err != nil { + return i, err + } + } + + if _, err := x.Delete(a); 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 := 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) +} diff --git a/models/issue.go b/models/issue.go index ff7df26c9..ac50d2dfb 100644 --- a/models/issue.go +++ b/models/issue.go @@ -7,17 +7,12 @@ package models import ( "errors" "fmt" - "io" - "mime/multipart" - "os" - "path" "strings" "time" api "code.gitea.io/sdk/gitea" "github.com/Unknwon/com" "github.com/go-xorm/xorm" - gouuid "github.com/satori/go.uuid" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/log" @@ -1740,158 +1735,3 @@ func DeleteMilestoneByRepoID(repoID, id int64) error { return sess.Commit() } -// Attachment represent a attachment of issue/comment/release. -type Attachment struct { - ID int64 `xorm:"pk autoincr"` - UUID string `xorm:"uuid UNIQUE"` - IssueID int64 `xorm:"INDEX"` - CommentID int64 - ReleaseID int64 `xorm:"INDEX"` - Name string - - Created time.Time `xorm:"-"` - CreatedUnix int64 -} - -// BeforeInsert is invoked from XORM before inserting an object of this type. -func (a *Attachment) BeforeInsert() { - a.CreatedUnix = time.Now().Unix() -} - -// AfterSet is invoked from XORM after setting the value of a field of -// this object. -func (a *Attachment) AfterSet(colName string, _ xorm.Cell) { - switch colName { - case "created_unix": - a.Created = time.Unix(a.CreatedUnix, 0).Local() - } -} - -// 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) -} - -// 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) - } - - if _, err := x.Insert(attach); err != nil { - return nil, err - } - - return attach, nil -} - -func getAttachmentByUUID(e Engine, uuid string) (*Attachment, error) { - attach := &Attachment{UUID: uuid} - has, err := x.Get(attach) - 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) { - 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) { - for i, a := range attachments { - if remove { - if err := os.Remove(a.LocalPath()); err != nil { - return i, err - } - } - - if _, err := x.Delete(a); 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 := 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) -} diff --git a/routers/repo/attachment.go b/routers/repo/attachment.go new file mode 100644 index 000000000..2ba389f07 --- /dev/null +++ b/routers/repo/attachment.go @@ -0,0 +1,73 @@ +// 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 repo + +import ( + "fmt" + "net/http" + "strings" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" +) + +func renderAttachmentSettings(ctx *context.Context) { + ctx.Data["RequireDropzone"] = true + ctx.Data["IsAttachmentEnabled"] = setting.AttachmentEnabled + ctx.Data["AttachmentAllowedTypes"] = setting.AttachmentAllowedTypes + ctx.Data["AttachmentMaxSize"] = setting.AttachmentMaxSize + ctx.Data["AttachmentMaxFiles"] = setting.AttachmentMaxFiles +} + +// UploadAttachment response for uploading issue's attachment +func UploadAttachment(ctx *context.Context) { + if !setting.AttachmentEnabled { + ctx.Error(404, "attachment is not enabled") + return + } + + file, header, err := ctx.Req.FormFile("file") + if err != nil { + ctx.Error(500, fmt.Sprintf("FormFile: %v", err)) + return + } + defer file.Close() + + buf := make([]byte, 1024) + n, _ := file.Read(buf) + if n > 0 { + buf = buf[:n] + } + fileType := http.DetectContentType(buf) + + allowedTypes := strings.Split(setting.AttachmentAllowedTypes, ",") + allowed := false + for _, t := range allowedTypes { + t := strings.Trim(t, " ") + if t == "*/*" || t == fileType { + allowed = true + break + } + } + + if !allowed { + ctx.Error(400, ErrFileTypeForbidden.Error()) + return + } + + attach, err := models.NewAttachment(header.Filename, buf, file) + if err != nil { + ctx.Error(500, fmt.Sprintf("NewAttachment: %v", err)) + return + } + + log.Trace("New attachment uploaded: %s", attach.UUID) + ctx.JSON(200, map[string]string{ + "uuid": attach.UUID, + }) +} + diff --git a/routers/repo/issue.go b/routers/repo/issue.go index f77b44f63..50995e70f 100644 --- a/routers/repo/issue.go +++ b/routers/repo/issue.go @@ -9,7 +9,6 @@ import ( "fmt" "io" "io/ioutil" - "net/http" "net/url" "strings" "time" @@ -268,14 +267,6 @@ func Issues(ctx *context.Context) { ctx.HTML(200, tplIssues) } -func renderAttachmentSettings(ctx *context.Context) { - ctx.Data["RequireDropzone"] = true - ctx.Data["IsAttachmentEnabled"] = setting.AttachmentEnabled - ctx.Data["AttachmentAllowedTypes"] = setting.AttachmentAllowedTypes - ctx.Data["AttachmentMaxSize"] = setting.AttachmentMaxSize - ctx.Data["AttachmentMaxFiles"] = setting.AttachmentMaxFiles -} - // RetrieveRepoMilestonesAndAssignees find all the milestones and assignees of a repository func RetrieveRepoMilestonesAndAssignees(ctx *context.Context, repo *models.Repository) { var err error @@ -477,54 +468,6 @@ func NewIssuePost(ctx *context.Context, form auth.CreateIssueForm) { ctx.Redirect(ctx.Repo.RepoLink + "/issues/" + com.ToStr(issue.Index)) } -// UploadAttachment response for uploading issue's attachment -func UploadAttachment(ctx *context.Context) { - if !setting.AttachmentEnabled { - ctx.Error(404, "attachment is not enabled") - return - } - - file, header, err := ctx.Req.FormFile("file") - if err != nil { - ctx.Error(500, fmt.Sprintf("FormFile: %v", err)) - return - } - defer file.Close() - - buf := make([]byte, 1024) - n, _ := file.Read(buf) - if n > 0 { - buf = buf[:n] - } - fileType := http.DetectContentType(buf) - - allowedTypes := strings.Split(setting.AttachmentAllowedTypes, ",") - allowed := false - for _, t := range allowedTypes { - t := strings.Trim(t, " ") - if t == "*/*" || t == fileType { - allowed = true - break - } - } - - if !allowed { - ctx.Error(400, ErrFileTypeForbidden.Error()) - return - } - - attach, err := models.NewAttachment(header.Filename, buf, file) - if err != nil { - ctx.Error(500, fmt.Sprintf("NewAttachment: %v", err)) - return - } - - log.Trace("New attachment uploaded: %s", attach.UUID) - ctx.JSON(200, map[string]string{ - "uuid": attach.UUID, - }) -} - // ViewIssue render issue view page func ViewIssue(ctx *context.Context) { ctx.Data["RequireHighlightJS"] = true