Compare commits

...

2 Commits

Author SHA1 Message Date
Giteabot
41655ee878
Show edit/close/delete button on organization wide repositories (#23388) (#23429)
Backport #23388 by @yp05327

A part of https://github.com/go-gitea/gitea/pull/22865

Co-authored-by: yp05327 <576951401@qq.com>
2023-03-12 15:17:25 +01:00
Giteabot
0d9b44c0e3
Preserve file size when creating attachments (#23406) (#23426)
Backport #23406 by @baez90

When creating attachments (issue, release, repo) the file size (being
part of the multipart file header) is passed through the chain of
creating an attachment to ensure the MinIO client can stream the file
directly instead of having to read it to memory completely at first.

Fixes #23393

Co-authored-by: Peter <peter.kurfer@googlemail.com>
Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
Co-authored-by: techknowlogick <techknowlogick@gitea.io>
2023-03-12 12:45:39 +01:00
12 changed files with 27 additions and 24 deletions

View File

@ -176,7 +176,7 @@ func CreateIssueAttachment(ctx *context.APIContext) {
filename = query filename = query
} }
attachment, err := attachment.UploadAttachment(file, setting.Attachment.AllowedTypes, &repo_model.Attachment{ attachment, err := attachment.UploadAttachment(file, setting.Attachment.AllowedTypes, header.Size, &repo_model.Attachment{
Name: filename, Name: filename,
UploaderID: ctx.Doer.ID, UploaderID: ctx.Doer.ID,
RepoID: ctx.Repo.Repository.ID, RepoID: ctx.Repo.Repository.ID,

View File

@ -180,7 +180,7 @@ func CreateIssueCommentAttachment(ctx *context.APIContext) {
filename = query filename = query
} }
attachment, err := attachment.UploadAttachment(file, setting.Attachment.AllowedTypes, &repo_model.Attachment{ attachment, err := attachment.UploadAttachment(file, setting.Attachment.AllowedTypes, header.Size, &repo_model.Attachment{
Name: filename, Name: filename,
UploaderID: ctx.Doer.ID, UploaderID: ctx.Doer.ID,
RepoID: ctx.Repo.Repository.ID, RepoID: ctx.Repo.Repository.ID,

View File

@ -194,7 +194,7 @@ func CreateReleaseAttachment(ctx *context.APIContext) {
} }
// Create a new attachment and save the file // Create a new attachment and save the file
attach, err := attachment.UploadAttachment(file, setting.Repository.Release.AllowedTypes, &repo_model.Attachment{ attach, err := attachment.UploadAttachment(file, setting.Repository.Release.AllowedTypes, header.Size, &repo_model.Attachment{
Name: filename, Name: filename,
UploaderID: ctx.Doer.ID, UploaderID: ctx.Doer.ID,
RepoID: release.RepoID, RepoID: release.RepoID,

View File

@ -44,7 +44,7 @@ func uploadAttachment(ctx *context.Context, repoID int64, allowedTypes string) {
} }
defer file.Close() defer file.Close()
attach, err := attachment.UploadAttachment(file, allowedTypes, &repo_model.Attachment{ attach, err := attachment.UploadAttachment(file, allowedTypes, header.Size, &repo_model.Attachment{
Name: header.Filename, Name: header.Filename,
UploaderID: ctx.Doer.ID, UploaderID: ctx.Doer.ID,
RepoID: repoID, RepoID: repoID,

View File

@ -113,7 +113,7 @@ func Projects(ctx *context.Context) {
pager.AddParam(ctx, "state", "State") pager.AddParam(ctx, "state", "State")
ctx.Data["Page"] = pager ctx.Data["Page"] = pager
ctx.Data["CanWriteProjects"] = true ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects)
ctx.Data["IsShowClosed"] = isShowClosed ctx.Data["IsShowClosed"] = isShowClosed
ctx.Data["IsProjectsPage"] = true ctx.Data["IsProjectsPage"] = true
ctx.Data["SortType"] = sortType ctx.Data["SortType"] = sortType

View File

@ -19,14 +19,14 @@ import (
) )
// NewAttachment creates a new attachment object, but do not verify. // NewAttachment creates a new attachment object, but do not verify.
func NewAttachment(attach *repo_model.Attachment, file io.Reader) (*repo_model.Attachment, error) { func NewAttachment(attach *repo_model.Attachment, file io.Reader, size int64) (*repo_model.Attachment, error) {
if attach.RepoID == 0 { if attach.RepoID == 0 {
return nil, fmt.Errorf("attachment %s should belong to a repository", attach.Name) return nil, fmt.Errorf("attachment %s should belong to a repository", attach.Name)
} }
err := db.WithTx(db.DefaultContext, func(ctx context.Context) error { err := db.WithTx(db.DefaultContext, func(ctx context.Context) error {
attach.UUID = uuid.New().String() attach.UUID = uuid.New().String()
size, err := storage.Attachments.Save(attach.RelativePath(), file, -1) size, err := storage.Attachments.Save(attach.RelativePath(), file, size)
if err != nil { if err != nil {
return fmt.Errorf("Create: %w", err) return fmt.Errorf("Create: %w", err)
} }
@ -39,7 +39,7 @@ func NewAttachment(attach *repo_model.Attachment, file io.Reader) (*repo_model.A
} }
// UploadAttachment upload new attachment into storage and update database // UploadAttachment upload new attachment into storage and update database
func UploadAttachment(file io.Reader, allowedTypes string, opts *repo_model.Attachment) (*repo_model.Attachment, error) { func UploadAttachment(file io.Reader, allowedTypes string, fileSize int64, opts *repo_model.Attachment) (*repo_model.Attachment, error) {
buf := make([]byte, 1024) buf := make([]byte, 1024)
n, _ := util.ReadAtMost(file, buf) n, _ := util.ReadAtMost(file, buf)
buf = buf[:n] buf = buf[:n]
@ -48,5 +48,5 @@ func UploadAttachment(file io.Reader, allowedTypes string, opts *repo_model.Atta
return nil, err return nil, err
} }
return NewAttachment(opts, io.MultiReader(bytes.NewReader(buf), file)) return NewAttachment(opts, io.MultiReader(bytes.NewReader(buf), file), fileSize)
} }

View File

@ -36,7 +36,7 @@ func TestUploadAttachment(t *testing.T) {
RepoID: 1, RepoID: 1,
UploaderID: user.ID, UploaderID: user.ID,
Name: filepath.Base(fPath), Name: filepath.Base(fPath),
}, f) }, f, -1)
assert.NoError(t, err) assert.NoError(t, err)
attachment, err := repo_model.GetAttachmentByUUID(db.DefaultContext, attach.UUID) attachment, err := repo_model.GetAttachmentByUUID(db.DefaultContext, attach.UUID)

View File

@ -87,7 +87,7 @@ func (h *ReplyHandler) Handle(ctx context.Context, content *MailContent, doer *u
attachmentIDs := make([]string, 0, len(content.Attachments)) attachmentIDs := make([]string, 0, len(content.Attachments))
if setting.Attachment.Enabled { if setting.Attachment.Enabled {
for _, attachment := range content.Attachments { for _, attachment := range content.Attachments {
a, err := attachment_service.UploadAttachment(bytes.NewReader(attachment.Content), setting.Attachment.AllowedTypes, &repo_model.Attachment{ a, err := attachment_service.UploadAttachment(bytes.NewReader(attachment.Content), setting.Attachment.AllowedTypes, int64(len(attachment.Content)), &repo_model.Attachment{
Name: attachment.Name, Name: attachment.Name,
UploaderID: doer.ID, UploaderID: doer.ID,
RepoID: issue.Repo.ID, RepoID: issue.Repo.ID,

View File

@ -106,11 +106,13 @@ func TestRelease_Create(t *testing.T) {
IsTag: false, IsTag: false,
}, nil, "")) }, nil, ""))
testPlayload := "testtest"
attach, err := attachment.NewAttachment(&repo_model.Attachment{ attach, err := attachment.NewAttachment(&repo_model.Attachment{
RepoID: repo.ID, RepoID: repo.ID,
UploaderID: user.ID, UploaderID: user.ID,
Name: "test.txt", Name: "test.txt",
}, strings.NewReader("testtest")) }, strings.NewReader(testPlayload), int64(len([]byte(testPlayload))))
assert.NoError(t, err) assert.NoError(t, err)
release := repo_model.Release{ release := repo_model.Release{
@ -239,11 +241,12 @@ func TestRelease_Update(t *testing.T) {
assert.Equal(t, tagName, release.TagName) assert.Equal(t, tagName, release.TagName)
// Add new attachments // Add new attachments
samplePayload := "testtest"
attach, err := attachment.NewAttachment(&repo_model.Attachment{ attach, err := attachment.NewAttachment(&repo_model.Attachment{
RepoID: repo.ID, RepoID: repo.ID,
UploaderID: user.ID, UploaderID: user.ID,
Name: "test.txt", Name: "test.txt",
}, strings.NewReader("testtest")) }, strings.NewReader(samplePayload), int64(len([]byte(samplePayload))))
assert.NoError(t, err) assert.NoError(t, err)
assert.NoError(t, UpdateRelease(user, gitRepo, release, []string{attach.UUID}, nil, nil)) assert.NoError(t, UpdateRelease(user, gitRepo, release, []string{attach.UUID}, nil, nil))

View File

@ -51,7 +51,7 @@
{{JsPrettyNumber .NumClosedIssues}}&nbsp;{{$.locale.Tr "repo.issues.closed_title"}} {{JsPrettyNumber .NumClosedIssues}}&nbsp;{{$.locale.Tr "repo.issues.closed_title"}}
</span> </span>
</div> </div>
{{if and (or $.CanWriteIssues $.CanWritePulls) (not $.Repository.IsArchived)}} {{if and $.CanWriteProjects (not $.Repository.IsArchived)}}
<div class="ui right operate"> <div class="ui right operate">
<a href="{{$.Link}}/{{.ID}}/edit" data-id={{.ID}} data-title={{.Title}}>{{svg "octicon-pencil"}} {{$.locale.Tr "repo.issues.label_edit"}}</a> <a href="{{$.Link}}/{{.ID}}/edit" data-id={{.ID}} data-title={{.Title}}>{{svg "octicon-pencil"}} {{$.locale.Tr "repo.issues.label_edit"}}</a>
{{if .IsClosed}} {{if .IsClosed}}
@ -59,7 +59,7 @@
{{else}} {{else}}
<a class="link-action" href data-url="{{$.Link}}/{{.ID}}/close">{{svg "octicon-skip"}} {{$.locale.Tr "repo.projects.close"}}</a> <a class="link-action" href data-url="{{$.Link}}/{{.ID}}/close">{{svg "octicon-skip"}} {{$.locale.Tr "repo.projects.close"}}</a>
{{end}} {{end}}
<a class="delete-button" href="#" data-url="{{$.RepoLink}}/projects/{{.ID}}/delete" data-id="{{.ID}}">{{svg "octicon-trash"}} {{$.locale.Tr "repo.issues.label_delete"}}</a> <a class="delete-button" href="#" data-url="{{$.Link}}/{{.ID}}/delete" data-id="{{.ID}}">{{svg "octicon-trash"}} {{$.locale.Tr "repo.issues.label_delete"}}</a>
</div> </div>
{{end}} {{end}}
{{if .Description}} {{if .Description}}
@ -75,7 +75,7 @@
</div> </div>
</div> </div>
{{if or .CanWriteIssues .CanWritePulls}} {{if $.CanWriteProjects}}
<div class="ui small basic delete modal"> <div class="ui small basic delete modal">
<div class="ui icon header"> <div class="ui icon header">
{{svg "octicon-trash"}} {{svg "octicon-trash"}}

View File

@ -43,7 +43,7 @@
<h2 class="project-title">{{$.Project.Title}}</h2> <h2 class="project-title">{{$.Project.Title}}</h2>
<div class="content project-description">{{$.Project.RenderedContent|Str2html}}</div> <div class="content project-description">{{$.Project.RenderedContent|Str2html}}</div>
</div> </div>
{{if or $.CanWriteIssues $.CanWritePulls}} {{if $.CanWriteProjects}}
<div class="column right aligned"> <div class="column right aligned">
<div class="ui compact right small menu"> <div class="ui compact right small menu">
<a class="item" href="{{$.Link}}/edit" data-id={{$.Project.ID}} data-title={{$.Project.Title}}> <a class="item" href="{{$.Link}}/edit" data-id={{$.Project.ID}} data-title={{$.Project.Title}}>
@ -256,7 +256,7 @@
</div> </div>
{{if or .CanWriteIssues .CanWritePulls}} {{if .CanWriteProjects}}
<div class="ui small basic delete modal"> <div class="ui small basic delete modal">
<div class="ui icon header"> <div class="ui icon header">
{{svg "octicon-trash"}} {{svg "octicon-trash"}}

View File

@ -53,15 +53,15 @@
{{JsPrettyNumber .NumClosedIssues}}&nbsp;{{$.locale.Tr "repo.issues.closed_title"}} {{JsPrettyNumber .NumClosedIssues}}&nbsp;{{$.locale.Tr "repo.issues.closed_title"}}
</span> </span>
</div> </div>
{{if and (or $.CanWriteIssues $.CanWritePulls) (not $.Repository.IsArchived)}} {{if and $.CanWriteProjects (not $.Repository.IsArchived)}}
<div class="ui right operate"> <div class="ui right operate">
<a href="{{$.Link}}/{{.ID}}/edit" data-id={{.ID}} data-title={{.Title}}>{{svg "octicon-pencil"}} {{$.locale.Tr "repo.issues.label_edit"}}</a> <a href="{{.Link}}/edit" data-id={{.ID}} data-title={{.Title}}>{{svg "octicon-pencil"}} {{$.locale.Tr "repo.issues.label_edit"}}</a>
{{if .IsClosed}} {{if .IsClosed}}
<a class="link-action" href data-url="{{$.Link}}/{{.ID}}/open">{{svg "octicon-check"}} {{$.locale.Tr "repo.projects.open"}}</a> <a class="link-action" href data-url="{{.Link}}/open">{{svg "octicon-check"}} {{$.locale.Tr "repo.projects.open"}}</a>
{{else}} {{else}}
<a class="link-action" href data-url="{{$.Link}}/{{.ID}}/close">{{svg "octicon-skip"}} {{$.locale.Tr "repo.projects.close"}}</a> <a class="link-action" href data-url="{{.Link}}/close">{{svg "octicon-skip"}} {{$.locale.Tr "repo.projects.close"}}</a>
{{end}} {{end}}
<a class="delete-button" href="#" data-url="{{$.RepoLink}}/projects/{{.ID}}/delete" data-id="{{.ID}}">{{svg "octicon-trash"}} {{$.locale.Tr "repo.issues.label_delete"}}</a> <a class="delete-button" href="#" data-url="{{.Link}}/delete" data-id="{{.ID}}">{{svg "octicon-trash"}} {{$.locale.Tr "repo.issues.label_delete"}}</a>
</div> </div>
{{end}} {{end}}
{{if .Description}} {{if .Description}}
@ -77,7 +77,7 @@
</div> </div>
</div> </div>
{{if or .CanWriteIssues .CanWritePulls}} {{if .CanWriteProjects}}
<div class="ui small basic delete modal"> <div class="ui small basic delete modal">
<div class="ui icon header"> <div class="ui icon header">
{{svg "octicon-trash"}} {{svg "octicon-trash"}}