Skip to content

Commit 64375d8

Browse files
coulinglunny
authored andcommitted
Attach to release (#673)
* Moved attachaments POST url from /issues/attachments to /attachments * Implemented attachment upload on release page * Implemented downloading attachments on the release page * Added zip and gzip files to default allowed attachments * Implemented uploading attachments on edit release * Renamed UploadIssueAttachment to UploadAttachment
1 parent dce03c1 commit 64375d8

File tree

11 files changed

+144
-14
lines changed

11 files changed

+144
-14
lines changed

cmd/web.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ func runWeb(ctx *cli.Context) error {
309309
return
310310
}
311311
})
312-
m.Post("/issues/attachments", repo.UploadIssueAttachment)
312+
m.Post("/attachments", repo.UploadAttachment)
313313
}, ignSignIn)
314314

315315
m.Group("/:username", func() {
@@ -463,13 +463,11 @@ func runWeb(ctx *cli.Context) error {
463463
m.Get("/:id/:action", repo.ChangeMilestonStatus)
464464
m.Post("/delete", repo.DeleteMilestone)
465465
}, reqRepoWriter, context.RepoRef())
466-
467466
m.Group("/releases", func() {
468467
m.Get("/new", repo.NewRelease)
469468
m.Post("/new", bindIgnErr(auth.NewReleaseForm{}), repo.NewReleasePost)
470469
m.Post("/delete", repo.DeleteRelease)
471470
}, reqRepoWriter, context.RepoRef())
472-
473471
m.Group("/releases", func() {
474472
m.Get("/edit/*", repo.EditRelease)
475473
m.Post("/edit/*", bindIgnErr(auth.EditReleaseForm{}), repo.EditReleasePost)

conf/app.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ ENABLE = true
289289
; Path for attachments. Defaults to `data/attachments`
290290
PATH = data/attachments
291291
; One or more allowed types, e.g. image/jpeg|image/png
292-
ALLOWED_TYPES = image/jpeg|image/png
292+
ALLOWED_TYPES = image/jpeg|image/png|application/zip|application/gzip
293293
; Max size of each file. Defaults to 32MB
294294
MAX_SIZE = 4
295295
; Max number of files per upload. Defaults to 10

models/release.go

Lines changed: 100 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ type Release struct {
3838
IsDraft bool `xorm:"NOT NULL DEFAULT false"`
3939
IsPrerelease bool
4040

41+
Attachments []*Attachment `xorm:"-"`
42+
4143
Created time.Time `xorm:"-"`
4244
CreatedUnix int64 `xorm:"INDEX"`
4345
}
@@ -155,8 +157,33 @@ func createTag(gitRepo *git.Repository, rel *Release) error {
155157
return nil
156158
}
157159

160+
func addReleaseAttachments(releaseID int64, attachmentUUIDs []string) (err error) {
161+
// Check attachments
162+
var attachments = make([]*Attachment,0)
163+
for _, uuid := range attachmentUUIDs {
164+
attach, err := getAttachmentByUUID(x, uuid)
165+
if err != nil {
166+
if IsErrAttachmentNotExist(err) {
167+
continue
168+
}
169+
return fmt.Errorf("getAttachmentByUUID [%s]: %v", uuid, err)
170+
}
171+
attachments = append(attachments, attach)
172+
}
173+
174+
for i := range attachments {
175+
attachments[i].ReleaseID = releaseID
176+
// No assign value could be 0, so ignore AllCols().
177+
if _, err = x.Id(attachments[i].ID).Update(attachments[i]); err != nil {
178+
return fmt.Errorf("update attachment [%d]: %v", attachments[i].ID, err)
179+
}
180+
}
181+
182+
return
183+
}
184+
158185
// CreateRelease creates a new release of repository.
159-
func CreateRelease(gitRepo *git.Repository, rel *Release) error {
186+
func CreateRelease(gitRepo *git.Repository, rel *Release, attachmentUUIDs []string) error {
160187
isExist, err := IsReleaseExist(rel.RepoID, rel.TagName)
161188
if err != nil {
162189
return err
@@ -168,7 +195,14 @@ func CreateRelease(gitRepo *git.Repository, rel *Release) error {
168195
return err
169196
}
170197
rel.LowerTagName = strings.ToLower(rel.TagName)
198+
171199
_, err = x.InsertOne(rel)
200+
if err != nil {
201+
return err
202+
}
203+
204+
err = addReleaseAttachments(rel.ID, attachmentUUIDs)
205+
172206
return err
173207
}
174208

@@ -222,6 +256,64 @@ func GetReleasesByRepoIDAndNames(repoID int64, tagNames []string) (rels []*Relea
222256
return rels, err
223257
}
224258

259+
type releaseMetaSearch struct {
260+
ID [] int64
261+
Rel [] *Release
262+
}
263+
func (s releaseMetaSearch) Len() int {
264+
return len(s.ID)
265+
}
266+
func (s releaseMetaSearch) Swap(i, j int) {
267+
s.ID[i], s.ID[j] = s.ID[j], s.ID[i]
268+
s.Rel[i], s.Rel[j] = s.Rel[j], s.Rel[i]
269+
}
270+
func (s releaseMetaSearch) Less(i, j int) bool {
271+
return s.ID[i] < s.ID[j]
272+
}
273+
274+
// GetReleaseAttachments retrieves the attachments for releases
275+
func GetReleaseAttachments(rels ... *Release) (err error){
276+
if len(rels) == 0 {
277+
return
278+
}
279+
280+
// To keep this efficient as possible sort all releases by id,
281+
// select attachments by release id,
282+
// then merge join them
283+
284+
// Sort
285+
var sortedRels = releaseMetaSearch{ID: make([]int64, len(rels)), Rel: make([]*Release, len(rels))}
286+
var attachments [] *Attachment
287+
for index, element := range rels {
288+
element.Attachments = []*Attachment{}
289+
sortedRels.ID[index] = element.ID
290+
sortedRels.Rel[index] = element
291+
}
292+
sort.Sort(sortedRels)
293+
294+
// Select attachments
295+
err = x.
296+
Asc("release_id").
297+
In("release_id", sortedRels.ID).
298+
Find(&attachments, Attachment{})
299+
300+
if err != nil {
301+
return err
302+
}
303+
304+
// merge join
305+
var currentIndex = 0
306+
for _, attachment := range attachments {
307+
for sortedRels.ID[currentIndex] < attachment.ReleaseID {
308+
currentIndex++
309+
}
310+
sortedRels.Rel[currentIndex].Attachments = append(sortedRels.Rel[currentIndex].Attachments, attachment)
311+
}
312+
313+
return
314+
315+
}
316+
225317
type releaseSorter struct {
226318
rels []*Release
227319
}
@@ -249,11 +341,17 @@ func SortReleases(rels []*Release) {
249341
}
250342

251343
// UpdateRelease updates information of a release.
252-
func UpdateRelease(gitRepo *git.Repository, rel *Release) (err error) {
344+
func UpdateRelease(gitRepo *git.Repository, rel *Release, attachmentUUIDs []string) (err error) {
253345
if err = createTag(gitRepo, rel); err != nil {
254346
return err
255347
}
256348
_, err = x.Id(rel.ID).AllCols().Update(rel)
349+
if err != nil {
350+
return err
351+
}
352+
353+
err = addReleaseAttachments(rel.ID, attachmentUUIDs)
354+
257355
return err
258356
}
259357

modules/auth/repo_form.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ type NewReleaseForm struct {
267267
Content string
268268
Draft string
269269
Prerelease bool
270+
Files []string
270271
}
271272

272273
// Validate valideates the fields
@@ -280,6 +281,7 @@ type EditReleaseForm struct {
280281
Content string `form:"content"`
281282
Draft string `form:"draft"`
282283
Prerelease bool `form:"prerelease"`
284+
Files []string
283285
}
284286

285287
// Validate valideates the fields

modules/setting/setting.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -718,7 +718,7 @@ please consider changing to GITEA_CUSTOM`)
718718
if !filepath.IsAbs(AttachmentPath) {
719719
AttachmentPath = path.Join(workDir, AttachmentPath)
720720
}
721-
AttachmentAllowedTypes = strings.Replace(sec.Key("ALLOWED_TYPES").MustString("image/jpeg,image/png"), "|", ",", -1)
721+
AttachmentAllowedTypes = strings.Replace(sec.Key("ALLOWED_TYPES").MustString("image/jpeg,image/png,application/zip,application/gzip"), "|", ",", -1)
722722
AttachmentMaxSize = sec.Key("MAX_SIZE").MustInt64(4)
723723
AttachmentMaxFiles = sec.Key("MAX_FILES").MustInt(5)
724724
AttachmentEnabled = sec.Key("ENABLE").MustBool(true)

routers/api/v1/repo/release.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ func CreateRelease(ctx *context.APIContext, form api.CreateReleaseOption) {
9999
IsPrerelease: form.IsPrerelease,
100100
CreatedUnix: commit.Author.When.Unix(),
101101
}
102-
if err := models.CreateRelease(ctx.Repo.GitRepo, rel); err != nil {
102+
if err := models.CreateRelease(ctx.Repo.GitRepo, rel, nil); err != nil {
103103
if models.IsErrReleaseAlreadyExist(err) {
104104
ctx.Status(409)
105105
} else {
@@ -145,7 +145,7 @@ func EditRelease(ctx *context.APIContext, form api.EditReleaseOption) {
145145
if form.IsPrerelease != nil {
146146
rel.IsPrerelease = *form.IsPrerelease
147147
}
148-
if err := models.UpdateRelease(ctx.Repo.GitRepo, rel); err != nil {
148+
if err := models.UpdateRelease(ctx.Repo.GitRepo, rel, nil); err != nil {
149149
ctx.Error(500, "UpdateRelease", err)
150150
return
151151
}

routers/repo/issue.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -477,8 +477,8 @@ func NewIssuePost(ctx *context.Context, form auth.CreateIssueForm) {
477477
ctx.Redirect(ctx.Repo.RepoLink + "/issues/" + com.ToStr(issue.Index))
478478
}
479479

480-
// UploadIssueAttachment response for uploading issue's attachment
481-
func UploadIssueAttachment(ctx *context.Context) {
480+
// UploadAttachment response for uploading issue's attachment
481+
func UploadAttachment(ctx *context.Context) {
482482
if !setting.AttachmentEnabled {
483483
ctx.Error(404, "attachment is not enabled")
484484
return

routers/repo/release.go

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"code.gitea.io/gitea/modules/context"
1616
"code.gitea.io/gitea/modules/log"
1717
"code.gitea.io/gitea/modules/markdown"
18+
"code.gitea.io/gitea/modules/setting"
1819
"github.com/Unknwon/paginater"
1920
)
2021

@@ -99,6 +100,12 @@ func Releases(ctx *context.Context) {
99100
return
100101
}
101102

103+
err = models.GetReleaseAttachments(releases...)
104+
if err != nil {
105+
ctx.Handle(500, "GetReleaseAttachments", err)
106+
return
107+
}
108+
102109
// Temproray cache commits count of used branches to speed up.
103110
countCache := make(map[string]int64)
104111
var cacheUsers = make(map[int64]*models.User)
@@ -162,6 +169,7 @@ func NewRelease(ctx *context.Context) {
162169
ctx.Data["Title"] = ctx.Tr("repo.release.new_release")
163170
ctx.Data["PageIsReleaseList"] = true
164171
ctx.Data["tag_target"] = ctx.Repo.Repository.DefaultBranch
172+
renderAttachmentSettings(ctx);
165173
ctx.HTML(200, tplReleaseNew)
166174
}
167175

@@ -215,7 +223,12 @@ func NewReleasePost(ctx *context.Context, form auth.NewReleaseForm) {
215223
CreatedUnix: tagCreatedUnix,
216224
}
217225

218-
if err = models.CreateRelease(ctx.Repo.GitRepo, rel); err != nil {
226+
var attachmentUUIDs []string
227+
if setting.AttachmentEnabled {
228+
attachmentUUIDs = form.Files
229+
}
230+
231+
if err = models.CreateRelease(ctx.Repo.GitRepo, rel, attachmentUUIDs); err != nil {
219232
ctx.Data["Err_TagName"] = true
220233
switch {
221234
case models.IsErrReleaseAlreadyExist(err):
@@ -237,6 +250,7 @@ func EditRelease(ctx *context.Context) {
237250
ctx.Data["Title"] = ctx.Tr("repo.release.edit_release")
238251
ctx.Data["PageIsReleaseList"] = true
239252
ctx.Data["PageIsEditRelease"] = true
253+
renderAttachmentSettings(ctx);
240254

241255
tagName := ctx.Params("*")
242256
rel, err := models.GetRelease(ctx.Repo.Repository.ID, tagName)
@@ -286,11 +300,16 @@ func EditReleasePost(ctx *context.Context, form auth.EditReleaseForm) {
286300
return
287301
}
288302

303+
var attachmentUUIDs []string
304+
if setting.AttachmentEnabled {
305+
attachmentUUIDs = form.Files
306+
}
307+
289308
rel.Title = form.Title
290309
rel.Note = form.Content
291310
rel.IsDraft = len(form.Draft) > 0
292311
rel.IsPrerelease = form.Prerelease
293-
if err = models.UpdateRelease(ctx.Repo.GitRepo, rel); err != nil {
312+
if err = models.UpdateRelease(ctx.Repo.GitRepo, rel, attachmentUUIDs); err != nil {
294313
ctx.Handle(500, "UpdateRelease", err)
295314
return
296315
}

templates/repo/issue/comment_tab.tmpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@
1313
</div>
1414
{{if .IsAttachmentEnabled}}
1515
<div class="files"></div>
16-
<div class="ui basic button dropzone" id="dropzone" data-upload-url="{{AppSubUrl}}/issues/attachments" data-accepts="{{.AttachmentAllowedTypes}}" data-max-file="{{.AttachmentMaxFiles}}" data-max-size="{{.AttachmentMaxSize}}" data-default-message="{{.i18n.Tr "dropzone.default_message"}}" data-invalid-input-type="{{.i18n.Tr "dropzone.invalid_input_type"}}" data-file-too-big="{{.i18n.Tr "dropzone.file_too_big"}}" data-remove-file="{{.i18n.Tr "dropzone.remove_file"}}"></div>
16+
<div class="ui basic button dropzone" id="dropzone" data-upload-url="{{AppSubUrl}}/attachments" data-accepts="{{.AttachmentAllowedTypes}}" data-max-file="{{.AttachmentMaxFiles}}" data-max-size="{{.AttachmentMaxSize}}" data-default-message="{{.i18n.Tr "dropzone.default_message"}}" data-invalid-input-type="{{.i18n.Tr "dropzone.invalid_input_type"}}" data-file-too-big="{{.i18n.Tr "dropzone.file_too_big"}}" data-remove-file="{{.i18n.Tr "dropzone.remove_file"}}"></div>
1717
{{end}}

templates/repo/release/list.tmpl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,15 @@
5959
<li>
6060
<a href="{{$.RepoLink}}/archive/{{.TagName}}.tar.gz"><i class="octicon octicon-file-zip"></i> {{$.i18n.Tr "repo.release.source_code"}} (TAR.GZ)</a>
6161
</li>
62+
{{if .Attachments}}
63+
{{range .Attachments}}
64+
<li>
65+
<a target="_blank" rel="noopener" href="{{AppSubUrl}}/attachments/{{.UUID}}">
66+
<span class="ui image octicon octicon-desktop-download" title='{{.Name}}'></span> {{.Name}}
67+
</a>
68+
</li>
69+
{{end}}
70+
{{end}}
6271
</ul>
6372
</div>
6473
{{else}}

templates/repo/release/new.tmpl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@
4848
<label>{{.i18n.Tr "repo.release.content"}}</label>
4949
<textarea name="content">{{.content}}</textarea>
5050
</div>
51+
{{if .IsAttachmentEnabled}}
52+
<div class="files"></div>
53+
<div class="ui basic button dropzone" id="dropzone" data-upload-url="{{AppSubUrl}}/attachments" data-accepts="{{.AttachmentAllowedTypes}}" data-max-file="{{.AttachmentMaxFiles}}" data-max-size="{{.AttachmentMaxSize}}" data-default-message="{{.i18n.Tr "dropzone.default_message"}}" data-invalid-input-type="{{.i18n.Tr "dropzone.invalid_input_type"}}" data-file-too-big="{{.i18n.Tr "dropzone.file_too_big"}}" data-remove-file="{{.i18n.Tr "dropzone.remove_file"}}"></div>
54+
{{end}}
5155
</div>
5256
<div class="ui container">
5357
<div class="ui divider"></div>

0 commit comments

Comments
 (0)