summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPhilip Couling <couling@gmail.com>2017-01-15 14:57:00 +0000
committerLunny Xiao <xiaolunwen@gmail.com>2017-01-15 22:57:00 +0800
commit64375d875b4d46a6081026290da8efd82c84b25f (patch)
treede35ef6470c9f8762f2dcd1912fc56dc4a499bad
parentdce03c19cb6c0f067a75f04a1bc1e7cb3528c585 (diff)
downloadgitea-64375d875b4d46a6081026290da8efd82c84b25f.tar.gz
gitea-64375d875b4d46a6081026290da8efd82c84b25f.zip
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
-rw-r--r--cmd/web.go4
-rw-r--r--conf/app.ini2
-rw-r--r--models/release.go102
-rw-r--r--modules/auth/repo_form.go2
-rw-r--r--modules/setting/setting.go2
-rw-r--r--routers/api/v1/repo/release.go4
-rw-r--r--routers/repo/issue.go4
-rw-r--r--routers/repo/release.go23
-rw-r--r--templates/repo/issue/comment_tab.tmpl2
-rw-r--r--templates/repo/release/list.tmpl9
-rw-r--r--templates/repo/release/new.tmpl4
11 files changed, 144 insertions, 14 deletions
diff --git a/cmd/web.go b/cmd/web.go
index 2ab680ecf3..ff4fcc376d 100644
--- a/cmd/web.go
+++ b/cmd/web.go
@@ -309,7 +309,7 @@ func runWeb(ctx *cli.Context) error {
return
}
})
- m.Post("/issues/attachments", repo.UploadIssueAttachment)
+ m.Post("/attachments", repo.UploadAttachment)
}, ignSignIn)
m.Group("/:username", func() {
@@ -463,13 +463,11 @@ func runWeb(ctx *cli.Context) error {
m.Get("/:id/:action", repo.ChangeMilestonStatus)
m.Post("/delete", repo.DeleteMilestone)
}, reqRepoWriter, context.RepoRef())
-
m.Group("/releases", func() {
m.Get("/new", repo.NewRelease)
m.Post("/new", bindIgnErr(auth.NewReleaseForm{}), repo.NewReleasePost)
m.Post("/delete", repo.DeleteRelease)
}, reqRepoWriter, context.RepoRef())
-
m.Group("/releases", func() {
m.Get("/edit/*", repo.EditRelease)
m.Post("/edit/*", bindIgnErr(auth.EditReleaseForm{}), repo.EditReleasePost)
diff --git a/conf/app.ini b/conf/app.ini
index f0a2b8ceed..ec4a3e3034 100644
--- a/conf/app.ini
+++ b/conf/app.ini
@@ -289,7 +289,7 @@ ENABLE = true
; Path for attachments. Defaults to `data/attachments`
PATH = data/attachments
; One or more allowed types, e.g. image/jpeg|image/png
-ALLOWED_TYPES = image/jpeg|image/png
+ALLOWED_TYPES = image/jpeg|image/png|application/zip|application/gzip
; Max size of each file. Defaults to 32MB
MAX_SIZE = 4
; Max number of files per upload. Defaults to 10
diff --git a/models/release.go b/models/release.go
index 67ef81e21c..113a0d68e4 100644
--- a/models/release.go
+++ b/models/release.go
@@ -38,6 +38,8 @@ type Release struct {
IsDraft bool `xorm:"NOT NULL DEFAULT false"`
IsPrerelease bool
+ Attachments []*Attachment `xorm:"-"`
+
Created time.Time `xorm:"-"`
CreatedUnix int64 `xorm:"INDEX"`
}
@@ -155,8 +157,33 @@ func createTag(gitRepo *git.Repository, rel *Release) error {
return nil
}
+func addReleaseAttachments(releaseID int64, attachmentUUIDs []string) (err error) {
+ // Check attachments
+ var attachments = make([]*Attachment,0)
+ for _, uuid := range attachmentUUIDs {
+ attach, err := getAttachmentByUUID(x, uuid)
+ if err != nil {
+ if IsErrAttachmentNotExist(err) {
+ continue
+ }
+ return fmt.Errorf("getAttachmentByUUID [%s]: %v", uuid, err)
+ }
+ attachments = append(attachments, attach)
+ }
+
+ for i := range attachments {
+ attachments[i].ReleaseID = releaseID
+ // No assign value could be 0, so ignore AllCols().
+ if _, err = x.Id(attachments[i].ID).Update(attachments[i]); err != nil {
+ return fmt.Errorf("update attachment [%d]: %v", attachments[i].ID, err)
+ }
+ }
+
+ return
+}
+
// CreateRelease creates a new release of repository.
-func CreateRelease(gitRepo *git.Repository, rel *Release) error {
+func CreateRelease(gitRepo *git.Repository, rel *Release, attachmentUUIDs []string) error {
isExist, err := IsReleaseExist(rel.RepoID, rel.TagName)
if err != nil {
return err
@@ -168,7 +195,14 @@ func CreateRelease(gitRepo *git.Repository, rel *Release) error {
return err
}
rel.LowerTagName = strings.ToLower(rel.TagName)
+
_, err = x.InsertOne(rel)
+ if err != nil {
+ return err
+ }
+
+ err = addReleaseAttachments(rel.ID, attachmentUUIDs)
+
return err
}
@@ -222,6 +256,64 @@ func GetReleasesByRepoIDAndNames(repoID int64, tagNames []string) (rels []*Relea
return rels, err
}
+type releaseMetaSearch struct {
+ ID [] int64
+ Rel [] *Release
+}
+func (s releaseMetaSearch) Len() int {
+ return len(s.ID)
+}
+func (s releaseMetaSearch) Swap(i, j int) {
+ s.ID[i], s.ID[j] = s.ID[j], s.ID[i]
+ s.Rel[i], s.Rel[j] = s.Rel[j], s.Rel[i]
+}
+func (s releaseMetaSearch) Less(i, j int) bool {
+ return s.ID[i] < s.ID[j]
+}
+
+// GetReleaseAttachments retrieves the attachments for releases
+func GetReleaseAttachments(rels ... *Release) (err error){
+ if len(rels) == 0 {
+ return
+ }
+
+ // To keep this efficient as possible sort all releases by id,
+ // select attachments by release id,
+ // then merge join them
+
+ // Sort
+ var sortedRels = releaseMetaSearch{ID: make([]int64, len(rels)), Rel: make([]*Release, len(rels))}
+ var attachments [] *Attachment
+ for index, element := range rels {
+ element.Attachments = []*Attachment{}
+ sortedRels.ID[index] = element.ID
+ sortedRels.Rel[index] = element
+ }
+ sort.Sort(sortedRels)
+
+ // Select attachments
+ err = x.
+ Asc("release_id").
+ In("release_id", sortedRels.ID).
+ Find(&attachments, Attachment{})
+
+ if err != nil {
+ return err
+ }
+
+ // merge join
+ var currentIndex = 0
+ for _, attachment := range attachments {
+ for sortedRels.ID[currentIndex] < attachment.ReleaseID {
+ currentIndex++
+ }
+ sortedRels.Rel[currentIndex].Attachments = append(sortedRels.Rel[currentIndex].Attachments, attachment)
+ }
+
+ return
+
+}
+
type releaseSorter struct {
rels []*Release
}
@@ -249,11 +341,17 @@ func SortReleases(rels []*Release) {
}
// UpdateRelease updates information of a release.
-func UpdateRelease(gitRepo *git.Repository, rel *Release) (err error) {
+func UpdateRelease(gitRepo *git.Repository, rel *Release, attachmentUUIDs []string) (err error) {
if err = createTag(gitRepo, rel); err != nil {
return err
}
_, err = x.Id(rel.ID).AllCols().Update(rel)
+ if err != nil {
+ return err
+ }
+
+ err = addReleaseAttachments(rel.ID, attachmentUUIDs)
+
return err
}
diff --git a/modules/auth/repo_form.go b/modules/auth/repo_form.go
index 8a200c0206..dda1f9d252 100644
--- a/modules/auth/repo_form.go
+++ b/modules/auth/repo_form.go
@@ -267,6 +267,7 @@ type NewReleaseForm struct {
Content string
Draft string
Prerelease bool
+ Files []string
}
// Validate valideates the fields
@@ -280,6 +281,7 @@ type EditReleaseForm struct {
Content string `form:"content"`
Draft string `form:"draft"`
Prerelease bool `form:"prerelease"`
+ Files []string
}
// Validate valideates the fields
diff --git a/modules/setting/setting.go b/modules/setting/setting.go
index 523582f703..ab916c1b53 100644
--- a/modules/setting/setting.go
+++ b/modules/setting/setting.go
@@ -718,7 +718,7 @@ please consider changing to GITEA_CUSTOM`)
if !filepath.IsAbs(AttachmentPath) {
AttachmentPath = path.Join(workDir, AttachmentPath)
}
- AttachmentAllowedTypes = strings.Replace(sec.Key("ALLOWED_TYPES").MustString("image/jpeg,image/png"), "|", ",", -1)
+ AttachmentAllowedTypes = strings.Replace(sec.Key("ALLOWED_TYPES").MustString("image/jpeg,image/png,application/zip,application/gzip"), "|", ",", -1)
AttachmentMaxSize = sec.Key("MAX_SIZE").MustInt64(4)
AttachmentMaxFiles = sec.Key("MAX_FILES").MustInt(5)
AttachmentEnabled = sec.Key("ENABLE").MustBool(true)
diff --git a/routers/api/v1/repo/release.go b/routers/api/v1/repo/release.go
index d14f2f3cae..7dacb8018a 100644
--- a/routers/api/v1/repo/release.go
+++ b/routers/api/v1/repo/release.go
@@ -99,7 +99,7 @@ func CreateRelease(ctx *context.APIContext, form api.CreateReleaseOption) {
IsPrerelease: form.IsPrerelease,
CreatedUnix: commit.Author.When.Unix(),
}
- if err := models.CreateRelease(ctx.Repo.GitRepo, rel); err != nil {
+ if err := models.CreateRelease(ctx.Repo.GitRepo, rel, nil); err != nil {
if models.IsErrReleaseAlreadyExist(err) {
ctx.Status(409)
} else {
@@ -145,7 +145,7 @@ func EditRelease(ctx *context.APIContext, form api.EditReleaseOption) {
if form.IsPrerelease != nil {
rel.IsPrerelease = *form.IsPrerelease
}
- if err := models.UpdateRelease(ctx.Repo.GitRepo, rel); err != nil {
+ if err := models.UpdateRelease(ctx.Repo.GitRepo, rel, nil); err != nil {
ctx.Error(500, "UpdateRelease", err)
return
}
diff --git a/routers/repo/issue.go b/routers/repo/issue.go
index a43a3d992b..f77b44f633 100644
--- a/routers/repo/issue.go
+++ b/routers/repo/issue.go
@@ -477,8 +477,8 @@ func NewIssuePost(ctx *context.Context, form auth.CreateIssueForm) {
ctx.Redirect(ctx.Repo.RepoLink + "/issues/" + com.ToStr(issue.Index))
}
-// UploadIssueAttachment response for uploading issue's attachment
-func UploadIssueAttachment(ctx *context.Context) {
+// UploadAttachment response for uploading issue's attachment
+func UploadAttachment(ctx *context.Context) {
if !setting.AttachmentEnabled {
ctx.Error(404, "attachment is not enabled")
return
diff --git a/routers/repo/release.go b/routers/repo/release.go
index 01ed3c37a1..3e0fc94e4e 100644
--- a/routers/repo/release.go
+++ b/routers/repo/release.go
@@ -15,6 +15,7 @@ import (
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markdown"
+ "code.gitea.io/gitea/modules/setting"
"github.com/Unknwon/paginater"
)
@@ -99,6 +100,12 @@ func Releases(ctx *context.Context) {
return
}
+ err = models.GetReleaseAttachments(releases...)
+ if err != nil {
+ ctx.Handle(500, "GetReleaseAttachments", err)
+ return
+ }
+
// Temproray cache commits count of used branches to speed up.
countCache := make(map[string]int64)
var cacheUsers = make(map[int64]*models.User)
@@ -162,6 +169,7 @@ func NewRelease(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("repo.release.new_release")
ctx.Data["PageIsReleaseList"] = true
ctx.Data["tag_target"] = ctx.Repo.Repository.DefaultBranch
+ renderAttachmentSettings(ctx);
ctx.HTML(200, tplReleaseNew)
}
@@ -215,7 +223,12 @@ func NewReleasePost(ctx *context.Context, form auth.NewReleaseForm) {
CreatedUnix: tagCreatedUnix,
}
- if err = models.CreateRelease(ctx.Repo.GitRepo, rel); err != nil {
+ var attachmentUUIDs []string
+ if setting.AttachmentEnabled {
+ attachmentUUIDs = form.Files
+ }
+
+ if err = models.CreateRelease(ctx.Repo.GitRepo, rel, attachmentUUIDs); err != nil {
ctx.Data["Err_TagName"] = true
switch {
case models.IsErrReleaseAlreadyExist(err):
@@ -237,6 +250,7 @@ func EditRelease(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("repo.release.edit_release")
ctx.Data["PageIsReleaseList"] = true
ctx.Data["PageIsEditRelease"] = true
+ renderAttachmentSettings(ctx);
tagName := ctx.Params("*")
rel, err := models.GetRelease(ctx.Repo.Repository.ID, tagName)
@@ -286,11 +300,16 @@ func EditReleasePost(ctx *context.Context, form auth.EditReleaseForm) {
return
}
+ var attachmentUUIDs []string
+ if setting.AttachmentEnabled {
+ attachmentUUIDs = form.Files
+ }
+
rel.Title = form.Title
rel.Note = form.Content
rel.IsDraft = len(form.Draft) > 0
rel.IsPrerelease = form.Prerelease
- if err = models.UpdateRelease(ctx.Repo.GitRepo, rel); err != nil {
+ if err = models.UpdateRelease(ctx.Repo.GitRepo, rel, attachmentUUIDs); err != nil {
ctx.Handle(500, "UpdateRelease", err)
return
}
diff --git a/templates/repo/issue/comment_tab.tmpl b/templates/repo/issue/comment_tab.tmpl
index b009060cd1..3af3d8a041 100644
--- a/templates/repo/issue/comment_tab.tmpl
+++ b/templates/repo/issue/comment_tab.tmpl
@@ -13,5 +13,5 @@
</div>
{{if .IsAttachmentEnabled}}
<div class="files"></div>
- <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>
+ <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>
{{end}}
diff --git a/templates/repo/release/list.tmpl b/templates/repo/release/list.tmpl
index 4879e04811..c18bc4884d 100644
--- a/templates/repo/release/list.tmpl
+++ b/templates/repo/release/list.tmpl
@@ -59,6 +59,15 @@
<li>
<a href="{{$.RepoLink}}/archive/{{.TagName}}.tar.gz"><i class="octicon octicon-file-zip"></i> {{$.i18n.Tr "repo.release.source_code"}} (TAR.GZ)</a>
</li>
+ {{if .Attachments}}
+ {{range .Attachments}}
+ <li>
+ <a target="_blank" rel="noopener" href="{{AppSubUrl}}/attachments/{{.UUID}}">
+ <span class="ui image octicon octicon-desktop-download" title='{{.Name}}'></span> {{.Name}}
+ </a>
+ </li>
+ {{end}}
+ {{end}}
</ul>
</div>
{{else}}
diff --git a/templates/repo/release/new.tmpl b/templates/repo/release/new.tmpl
index 3a9e6fadd8..5f82cf2f52 100644
--- a/templates/repo/release/new.tmpl
+++ b/templates/repo/release/new.tmpl
@@ -48,6 +48,10 @@
<label>{{.i18n.Tr "repo.release.content"}}</label>
<textarea name="content">{{.content}}</textarea>
</div>
+ {{if .IsAttachmentEnabled}}
+ <div class="files"></div>
+ <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>
+ {{end}}
</div>
<div class="ui container">
<div class="ui divider"></div>