Browse Source

release: able to add attchments to release (#1614)

Added new config section '[release.attachment]’.
pull/3422/merge
Unknwon 8 years ago
parent
commit
1df54ea0cd
No known key found for this signature in database
GPG Key ID: 25B575AE3213B2B3
  1. 10
      cmd/web.go
  2. 18
      conf/app.ini
  3. 2
      gogs.go
  4. 16
      models/attachment.go
  5. 2
      models/issue.go
  6. 57
      models/release.go
  7. 6
      modules/bindata/bindata.go
  8. 6
      modules/form/repo.go
  9. 13
      modules/setting/setting.go
  10. 4
      routers/repo/download.go
  11. 41
      routers/repo/issue.go
  12. 2
      routers/repo/pull.go
  13. 41
      routers/repo/release.go
  14. 2
      templates/.VERSION
  15. 11
      templates/repo/release/list.tmpl
  16. 47
      templates/repo/release/new.tmpl

10
cmd/web.go

@ -339,15 +339,15 @@ func runWeb(ctx *cli.Context) error {
defer fr.Close() defer fr.Close()
ctx.Header().Set("Cache-Control", "public,max-age=86400") ctx.Header().Set("Cache-Control", "public,max-age=86400")
fmt.Println("attach.Name:", attach.Name)
ctx.Header().Set("Content-Disposition", fmt.Sprintf(`inline; filename="%s"`, attach.Name)) ctx.Header().Set("Content-Disposition", fmt.Sprintf(`inline; filename="%s"`, attach.Name))
// Fix #312. Attachments with , in their name are not handled correctly by Google Chrome. if err = repo.ServeData(ctx, attach.Name, fr); err != nil {
// We must put the name in " manually.
if err = repo.ServeData(ctx, "\""+attach.Name+"\"", fr); err != nil {
ctx.Handle(500, "ServeData", err) ctx.Handle(500, "ServeData", err)
return return
} }
}) })
m.Post("/issues/attachments", repo.UploadIssueAttachment) m.Post("/issues/attachments", repo.UploadIssueAttachment)
m.Post("/releases/attachments", repo.UploadReleaseAttachment)
}, ignSignIn) }, ignSignIn)
m.Group("/:username", func() { m.Group("/:username", func() {
@ -490,7 +490,7 @@ func runWeb(ctx *cli.Context) error {
// So they can apply their own enable/disable logic on routers. // So they can apply their own enable/disable logic on routers.
m.Group("/issues", func() { m.Group("/issues", func() {
m.Combo("/new", repo.MustEnableIssues).Get(context.RepoRef(), repo.NewIssue). m.Combo("/new", repo.MustEnableIssues).Get(context.RepoRef(), repo.NewIssue).
Post(bindIgnErr(form.CreateIssue{}), repo.NewIssuePost) Post(bindIgnErr(form.NewIssue{}), repo.NewIssuePost)
m.Group("/:index", func() { m.Group("/:index", func() {
m.Post("/label", repo.UpdateIssueLabel) m.Post("/label", repo.UpdateIssueLabel)
@ -538,7 +538,7 @@ func runWeb(ctx *cli.Context) error {
// e.g. /org1/test-repo/compare/master...org1:develop // e.g. /org1/test-repo/compare/master...org1:develop
// which should be /org1/test-repo/compare/master...develop // which should be /org1/test-repo/compare/master...develop
m.Combo("/compare/*", repo.MustAllowPulls).Get(repo.CompareAndPullRequest). m.Combo("/compare/*", repo.MustAllowPulls).Get(repo.CompareAndPullRequest).
Post(bindIgnErr(form.CreateIssue{}), repo.CompareAndPullRequestPost) Post(bindIgnErr(form.NewIssue{}), repo.CompareAndPullRequestPost)
m.Group("", func() { m.Group("", func() {
m.Combo("/_edit/*").Get(repo.EditFile). m.Combo("/_edit/*").Get(repo.EditFile).

18
conf/app.ini

@ -114,6 +114,19 @@ FILE_MAX_SIZE = 3
; Maximum number of files per upload ; Maximum number of files per upload
MAX_FILES = 5 MAX_FILES = 5
; Attachment settings for releases
[release.attachment]
; Whether attachments are enabled. Defaults to `true`
ENABLED = true
; Path for attachments. Defaults to `data/attachments`
PATH = data/attachments
; One or more allowed types, e.g. image/jpeg|image/png
ALLOWED_TYPES = */*
; Max size of each file. Defaults to 32MB
MAX_SIZE = 32
; Max number of files per upload. Defaults to 10
MAX_FILES = 10
[markdown] [markdown]
; Enable hard line break extension ; Enable hard line break extension
ENABLE_HARD_LINE_BREAK = false ENABLE_HARD_LINE_BREAK = false
@ -273,6 +286,7 @@ DISABLE_GRAVATAR = false
; This value will be forced to be false in offline mode or Gravatar is disbaled. ; This value will be forced to be false in offline mode or Gravatar is disbaled.
ENABLE_FEDERATED_AVATAR = true ENABLE_FEDERATED_AVATAR = true
; Attachment settings for issues
[attachment] [attachment]
; Whether attachments are enabled. Defaults to `true` ; Whether attachments are enabled. Defaults to `true`
ENABLE = true ENABLE = true
@ -280,9 +294,9 @@ ENABLE = true
PATH = data/attachments PATH = data/attachments
; One or more allowed types, e.g. image/jpeg|image/png ; One or more allowed types, e.g. image/jpeg|image/png
ALLOWED_TYPES = image/jpeg|image/png ALLOWED_TYPES = image/jpeg|image/png
; Max size of each file. Defaults to 32MB ; Max size of each file. Defaults to 4MB
MAX_SIZE = 4 MAX_SIZE = 4
; Max number of files per upload. Defaults to 10 ; Max number of files per upload. Defaults to 5
MAX_FILES = 5 MAX_FILES = 5
[time] [time]

2
gogs.go

@ -16,7 +16,7 @@ import (
"github.com/gogits/gogs/modules/setting" "github.com/gogits/gogs/modules/setting"
) )
const APP_VER = "0.10.17.0313" const APP_VER = "0.10.18.0313"
func init() { func init() {
setting.AppVer = APP_VER setting.AppVer = APP_VER

16
models/attachment.go

@ -110,7 +110,7 @@ func GetAttachmentByUUID(uuid string) (*Attachment, error) {
} }
func getAttachmentsByIssueID(e Engine, issueID int64) ([]*Attachment, error) { func getAttachmentsByIssueID(e Engine, issueID int64) ([]*Attachment, error) {
attachments := make([]*Attachment, 0, 10) attachments := make([]*Attachment, 0, 5)
return attachments, e.Where("issue_id = ? AND comment_id = 0", issueID).Find(&attachments) return attachments, e.Where("issue_id = ? AND comment_id = 0", issueID).Find(&attachments)
} }
@ -120,15 +120,25 @@ func GetAttachmentsByIssueID(issueID int64) ([]*Attachment, error) {
} }
func getAttachmentsByCommentID(e Engine, commentID int64) ([]*Attachment, error) { func getAttachmentsByCommentID(e Engine, commentID int64) ([]*Attachment, error) {
attachments := make([]*Attachment, 0, 10) attachments := make([]*Attachment, 0, 5)
return attachments, e.Where("comment_id=?", commentID).Find(&attachments) return attachments, e.Where("comment_id=?", commentID).Find(&attachments)
} }
// GetAttachmentsByCommentID returns all attachments if comment by given ID. // GetAttachmentsByCommentID returns all attachments of a comment.
func GetAttachmentsByCommentID(commentID int64) ([]*Attachment, error) { func GetAttachmentsByCommentID(commentID int64) ([]*Attachment, error) {
return getAttachmentsByCommentID(x, commentID) return getAttachmentsByCommentID(x, commentID)
} }
func getAttachmentsByReleaseID(e Engine, releaseID int64) ([]*Attachment, error) {
attachments := make([]*Attachment, 0, 10)
return attachments, e.Where("release_id = ?", releaseID).Find(&attachments)
}
// GetAttachmentsByReleaseID returns all attachments of a release.
func GetAttachmentsByReleaseID(releaseID int64) ([]*Attachment, error) {
return getAttachmentsByReleaseID(x, releaseID)
}
// DeleteAttachment deletes the given attachment and optionally the associated file. // DeleteAttachment deletes the given attachment and optionally the associated file.
func DeleteAttachment(a *Attachment, remove bool) error { func DeleteAttachment(a *Attachment, remove bool) error {
_, err := DeleteAttachments([]*Attachment{a}, remove) _, err := DeleteAttachments([]*Attachment{a}, remove)

2
models/issue.go

@ -741,7 +741,7 @@ func newIssue(e *xorm.Session, opts NewIssueOptions) (err error) {
return opts.Issue.loadAttributes(e) return opts.Issue.loadAttributes(e)
} }
// NewIssue creates new issue with labels for repository. // NewIssue creates new issue with labels and attachments for repository.
func NewIssue(repo *Repository, issue *Issue, labelIDs []int64, uuids []string) (err error) { func NewIssue(repo *Repository, issue *Issue, labelIDs []int64, uuids []string) (err error) {
sess := x.NewSession() sess := x.NewSession()
defer sessionRelease(sess) defer sessionRelease(sess)

57
models/release.go

@ -39,6 +39,8 @@ type Release struct {
Created time.Time `xorm:"-"` Created time.Time `xorm:"-"`
CreatedUnix int64 CreatedUnix int64
Attachments []*Attachment `xorm:"-"`
} }
func (r *Release) BeforeInsert() { func (r *Release) BeforeInsert() {
@ -74,6 +76,13 @@ func (r *Release) loadAttributes(e Engine) (err error) {
} }
} }
if r.Attachments == nil {
r.Attachments, err = getAttachmentsByReleaseID(e, r.ID)
if err != nil {
return fmt.Errorf("getAttachmentsByReleaseID [%d]: %v", r.ID, err)
}
}
return nil return nil
} }
@ -150,8 +159,8 @@ func (r *Release) preparePublishWebhooks() {
} }
} }
// CreateRelease creates a new release of repository. // NewRelease creates a new release with attachments for repository.
func CreateRelease(gitRepo *git.Repository, r *Release) error { func NewRelease(gitRepo *git.Repository, r *Release, uuids []string) error {
isExist, err := IsReleaseExist(r.RepoID, r.TagName) isExist, err := IsReleaseExist(r.RepoID, r.TagName)
if err != nil { if err != nil {
return err return err
@ -163,10 +172,27 @@ func CreateRelease(gitRepo *git.Repository, r *Release) error {
return err return err
} }
r.LowerTagName = strings.ToLower(r.TagName) r.LowerTagName = strings.ToLower(r.TagName)
if _, err = x.Insert(r); err != nil {
sess := x.NewSession()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
return err
}
if _, err = sess.Insert(r); err != nil {
return fmt.Errorf("Insert: %v", err) return fmt.Errorf("Insert: %v", err)
} }
if len(uuids) > 0 {
if _, err = sess.In("uuid", uuids).Cols("release_id").Update(&Attachment{ReleaseID: r.ID}); err != nil {
return fmt.Errorf("link attachments: %v", err)
}
}
if err = sess.Commit(); err != nil {
return fmt.Errorf("Commit: %v", err)
}
// Only send webhook when actually published, skip drafts // Only send webhook when actually published, skip drafts
if r.IsDraft { if r.IsDraft {
return nil return nil
@ -254,15 +280,36 @@ func SortReleases(rels []*Release) {
} }
// UpdateRelease updates information of a release. // UpdateRelease updates information of a release.
func UpdateRelease(doer *User, gitRepo *git.Repository, r *Release, isPublish bool) (err error) { func UpdateRelease(doer *User, gitRepo *git.Repository, r *Release, isPublish bool, uuids []string) (err error) {
if err = createTag(gitRepo, r); err != nil { if err = createTag(gitRepo, r); err != nil {
return fmt.Errorf("createTag: %v", err) return fmt.Errorf("createTag: %v", err)
} }
r.PublisherID = doer.ID r.PublisherID = doer.ID
if _, err = x.Id(r.ID).AllCols().Update(r); err != nil {
sess := x.NewSession()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
return err return err
} }
if _, err = sess.Id(r.ID).AllCols().Update(r); err != nil {
return fmt.Errorf("Update: %v", err)
}
// Unlink all current attachments and link back later if still valid
if _, err = sess.Exec("UPDATE attachment SET release_id = 0 WHERE release_id = ?", r.ID); err != nil {
return fmt.Errorf("unlink current attachments: %v", err)
}
if len(uuids) > 0 {
if _, err = sess.In("uuid", uuids).Cols("release_id").Update(&Attachment{ReleaseID: r.ID}); err != nil {
return fmt.Errorf("link attachments: %v", err)
}
}
if err = sess.Commit(); err != nil {
return fmt.Errorf("Commit: %v", err)
}
if !isPublish { if !isPublish {
return nil return nil

6
modules/bindata/bindata.go

File diff suppressed because one or more lines are too long

6
modules/form/repo.go

@ -200,7 +200,7 @@ func (f *NewDiscordHook) Validate(ctx *macaron.Context, errs binding.Errors) bin
// |___/____ >____ >____/ \___ > // |___/____ >____ >____/ \___ >
// \/ \/ \/ // \/ \/ \/
type CreateIssue struct { type NewIssue struct {
Title string `binding:"Required;MaxSize(255)"` Title string `binding:"Required;MaxSize(255)"`
LabelIDs string `form:"label_ids"` LabelIDs string `form:"label_ids"`
MilestoneID int64 MilestoneID int64
@ -209,7 +209,7 @@ type CreateIssue struct {
Files []string Files []string
} }
func (f *CreateIssue) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { func (f *NewIssue) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale) return validate(errs, ctx.Data, f, ctx.Locale)
} }
@ -279,6 +279,7 @@ type NewRelease struct {
Content string Content string
Draft string Draft string
Prerelease bool Prerelease bool
Files []string
} }
func (f *NewRelease) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { func (f *NewRelease) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
@ -290,6 +291,7 @@ type EditRelease struct {
Content string Content string
Draft string Draft string
Prerelease bool Prerelease bool
Files []string
} }
func (f *EditRelease) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { func (f *EditRelease) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {

13
modules/setting/setting.go

@ -146,6 +146,17 @@ var (
PagingNum int PagingNum int
} }
// Release settigns
Release struct {
Attachment struct {
Enabled bool
TempPath string
AllowedTypes []string `delim:"|"`
MaxSize int64
MaxFiles int
} `ini:"-"`
}
// Markdown sttings // Markdown sttings
Markdown struct { Markdown struct {
EnableHardLineBreak bool EnableHardLineBreak bool
@ -590,6 +601,8 @@ func NewContext() {
log.Fatal(2, "Fail to map HTTP settings: %v", err) log.Fatal(2, "Fail to map HTTP settings: %v", err)
} else if err = Cfg.Section("webhook").MapTo(&Webhook); err != nil { } else if err = Cfg.Section("webhook").MapTo(&Webhook); err != nil {
log.Fatal(2, "Fail to map Webhook settings: %v", err) log.Fatal(2, "Fail to map Webhook settings: %v", err)
} else if err = Cfg.Section("release.attachment").MapTo(&Release.Attachment); err != nil {
log.Fatal(2, "Fail to map Release.Attachment settings: %v", err)
} else if err = Cfg.Section("markdown").MapTo(&Markdown); err != nil { } else if err = Cfg.Section("markdown").MapTo(&Markdown); err != nil {
log.Fatal(2, "Fail to map Markdown settings: %v", err) log.Fatal(2, "Fail to map Markdown settings: %v", err)
} else if err = Cfg.Section("smartypants").MapTo(&Smartypants); err != nil { } else if err = Cfg.Section("smartypants").MapTo(&Smartypants); err != nil {

4
routers/repo/download.go

@ -23,7 +23,7 @@ func ServeData(ctx *context.Context, name string, reader io.Reader) error {
if !base.IsTextFile(buf) { if !base.IsTextFile(buf) {
if !base.IsImageFile(buf) { if !base.IsImageFile(buf) {
ctx.Resp.Header().Set("Content-Disposition", "attachment; filename=\""+path.Base(ctx.Repo.TreePath)+"\"") ctx.Resp.Header().Set("Content-Disposition", "attachment; filename=\""+name+"\"")
ctx.Resp.Header().Set("Content-Transfer-Encoding", "binary") ctx.Resp.Header().Set("Content-Transfer-Encoding", "binary")
} }
} else if !ctx.QueryBool("render") { } else if !ctx.QueryBool("render") {
@ -40,7 +40,7 @@ func ServeBlob(ctx *context.Context, blob *git.Blob) error {
return err return err
} }
return ServeData(ctx, ctx.Repo.TreePath, dataRc) return ServeData(ctx, path.Base(ctx.Repo.TreePath), dataRc)
} }
func SingleDownload(ctx *context.Context) { func SingleDownload(ctx *context.Context) {

41
routers/repo/issue.go

@ -348,7 +348,7 @@ func NewIssue(ctx *context.Context) {
ctx.HTML(200, ISSUE_NEW) ctx.HTML(200, ISSUE_NEW)
} }
func ValidateRepoMetas(ctx *context.Context, f form.CreateIssue) ([]int64, int64, int64) { func ValidateRepoMetas(ctx *context.Context, f form.NewIssue) ([]int64, int64, int64) {
var ( var (
repo = ctx.Repo.Repository repo = ctx.Repo.Repository
err error err error
@ -402,34 +402,30 @@ func ValidateRepoMetas(ctx *context.Context, f form.CreateIssue) ([]int64, int64
return labelIDs, milestoneID, assigneeID return labelIDs, milestoneID, assigneeID
} }
func NewIssuePost(ctx *context.Context, f form.CreateIssue) { func NewIssuePost(ctx *context.Context, f form.NewIssue) {
ctx.Data["Title"] = ctx.Tr("repo.issues.new") ctx.Data["Title"] = ctx.Tr("repo.issues.new")
ctx.Data["PageIsIssueList"] = true ctx.Data["PageIsIssueList"] = true
ctx.Data["RequireHighlightJS"] = true ctx.Data["RequireHighlightJS"] = true
ctx.Data["RequireSimpleMDE"] = true ctx.Data["RequireSimpleMDE"] = true
renderAttachmentSettings(ctx) renderAttachmentSettings(ctx)
var (
repo = ctx.Repo.Repository
attachments []string
)
labelIDs, milestoneID, assigneeID := ValidateRepoMetas(ctx, f) labelIDs, milestoneID, assigneeID := ValidateRepoMetas(ctx, f)
if ctx.Written() { if ctx.Written() {
return return
} }
if setting.AttachmentEnabled {
attachments = f.Files
}
if ctx.HasError() { if ctx.HasError() {
ctx.HTML(200, ISSUE_NEW) ctx.HTML(200, ISSUE_NEW)
return return
} }
var attachments []string
if setting.AttachmentEnabled {
attachments = f.Files
}
issue := &models.Issue{ issue := &models.Issue{
RepoID: repo.ID, RepoID: ctx.Repo.Repository.ID,
Title: f.Title, Title: f.Title,
PosterID: ctx.User.ID, PosterID: ctx.User.ID,
Poster: ctx.User, Poster: ctx.User,
@ -437,21 +433,16 @@ func NewIssuePost(ctx *context.Context, f form.CreateIssue) {
AssigneeID: assigneeID, AssigneeID: assigneeID,
Content: f.Content, Content: f.Content,
} }
if err := models.NewIssue(repo, issue, labelIDs, attachments); err != nil { if err := models.NewIssue(ctx.Repo.Repository, issue, labelIDs, attachments); err != nil {
ctx.Handle(500, "NewIssue", err) ctx.Handle(500, "NewIssue", err)
return return
} }
log.Trace("Issue created: %d/%d", repo.ID, issue.ID) log.Trace("Issue created: %d/%d", ctx.Repo.Repository.ID, issue.ID)
ctx.Redirect(ctx.Repo.RepoLink + "/issues/" + com.ToStr(issue.Index)) ctx.Redirect(ctx.Repo.RepoLink + "/issues/" + com.ToStr(issue.Index))
} }
func UploadIssueAttachment(ctx *context.Context) { func uploadAttachment(ctx *context.Context, allowedTypes []string) {
if !setting.AttachmentEnabled {
ctx.Error(404, "attachment is not enabled")
return
}
file, header, err := ctx.Req.FormFile("file") file, header, err := ctx.Req.FormFile("file")
if err != nil { if err != nil {
ctx.Error(500, fmt.Sprintf("FormFile: %v", err)) ctx.Error(500, fmt.Sprintf("FormFile: %v", err))
@ -466,7 +457,6 @@ func UploadIssueAttachment(ctx *context.Context) {
} }
fileType := http.DetectContentType(buf) fileType := http.DetectContentType(buf)
allowedTypes := strings.Split(setting.AttachmentAllowedTypes, ",")
allowed := false allowed := false
for _, t := range allowedTypes { for _, t := range allowedTypes {
t := strings.Trim(t, " ") t := strings.Trim(t, " ")
@ -493,6 +483,15 @@ func UploadIssueAttachment(ctx *context.Context) {
}) })
} }
func UploadIssueAttachment(ctx *context.Context) {
if !setting.AttachmentEnabled {
ctx.NotFound()
return
}
uploadAttachment(ctx, strings.Split(setting.AttachmentAllowedTypes, ","))
}
func ViewIssue(ctx *context.Context) { func ViewIssue(ctx *context.Context) {
ctx.Data["RequireHighlightJS"] = true ctx.Data["RequireHighlightJS"] = true
ctx.Data["RequireDropzone"] = true ctx.Data["RequireDropzone"] = true

2
routers/repo/pull.go

@ -634,7 +634,7 @@ func CompareAndPullRequest(ctx *context.Context) {
ctx.HTML(200, COMPARE_PULL) ctx.HTML(200, COMPARE_PULL)
} }
func CompareAndPullRequestPost(ctx *context.Context, f form.CreateIssue) { func CompareAndPullRequestPost(ctx *context.Context, f form.NewIssue) {
ctx.Data["Title"] = ctx.Tr("repo.pulls.compare_changes") ctx.Data["Title"] = ctx.Tr("repo.pulls.compare_changes")
ctx.Data["PageIsComparePull"] = true ctx.Data["PageIsComparePull"] = true
ctx.Data["IsDiffCompare"] = true ctx.Data["IsDiffCompare"] = true

41
routers/repo/release.go

@ -6,6 +6,7 @@ package repo
import ( import (
"fmt" "fmt"
"strings"
log "gopkg.in/clog.v1" log "gopkg.in/clog.v1"
@ -14,6 +15,7 @@ import (
"github.com/gogits/gogs/modules/context" "github.com/gogits/gogs/modules/context"
"github.com/gogits/gogs/modules/form" "github.com/gogits/gogs/modules/form"
"github.com/gogits/gogs/modules/markdown" "github.com/gogits/gogs/modules/markdown"
"github.com/gogits/gogs/modules/setting"
) )
const ( const (
@ -148,16 +150,26 @@ func Releases(ctx *context.Context) {
ctx.HTML(200, RELEASES) ctx.HTML(200, RELEASES)
} }
func renderReleaseAttachmentSettings(ctx *context.Context) {
ctx.Data["RequireDropzone"] = true
ctx.Data["IsAttachmentEnabled"] = setting.Release.Attachment.Enabled
ctx.Data["AttachmentAllowedTypes"] = strings.Join(setting.Release.Attachment.AllowedTypes, ",")
ctx.Data["AttachmentMaxSize"] = setting.Release.Attachment.MaxSize
ctx.Data["AttachmentMaxFiles"] = setting.Release.Attachment.MaxFiles
}
func NewRelease(ctx *context.Context) { func NewRelease(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("repo.release.new_release") ctx.Data["Title"] = ctx.Tr("repo.release.new_release")
ctx.Data["PageIsReleaseList"] = true ctx.Data["PageIsReleaseList"] = true
ctx.Data["tag_target"] = ctx.Repo.Repository.DefaultBranch ctx.Data["tag_target"] = ctx.Repo.Repository.DefaultBranch
renderReleaseAttachmentSettings(ctx)
ctx.HTML(200, RELEASE_NEW) ctx.HTML(200, RELEASE_NEW)
} }
func NewReleasePost(ctx *context.Context, f form.NewRelease) { func NewReleasePost(ctx *context.Context, f form.NewRelease) {
ctx.Data["Title"] = ctx.Tr("repo.release.new_release") ctx.Data["Title"] = ctx.Tr("repo.release.new_release")
ctx.Data["PageIsReleaseList"] = true ctx.Data["PageIsReleaseList"] = true
renderReleaseAttachmentSettings(ctx)
if ctx.HasError() { if ctx.HasError() {
ctx.HTML(200, RELEASE_NEW) ctx.HTML(200, RELEASE_NEW)
@ -169,6 +181,7 @@ func NewReleasePost(ctx *context.Context, f form.NewRelease) {
return return
} }
// Use current time if tag not yet exist, otherwise get time from Git
var tagCreatedUnix int64 var tagCreatedUnix int64
tag, err := ctx.Repo.GitRepo.GetTag(f.TagName) tag, err := ctx.Repo.GitRepo.GetTag(f.TagName)
if err == nil { if err == nil {
@ -190,6 +203,11 @@ func NewReleasePost(ctx *context.Context, f form.NewRelease) {
return return
} }
var attachments []string
if setting.Release.Attachment.Enabled {
attachments = f.Files
}
rel := &models.Release{ rel := &models.Release{
RepoID: ctx.Repo.Repository.ID, RepoID: ctx.Repo.Repository.ID,
PublisherID: ctx.User.ID, PublisherID: ctx.User.ID,
@ -203,7 +221,7 @@ func NewReleasePost(ctx *context.Context, f form.NewRelease) {
IsPrerelease: f.Prerelease, IsPrerelease: f.Prerelease,
CreatedUnix: tagCreatedUnix, CreatedUnix: tagCreatedUnix,
} }
if err = models.CreateRelease(ctx.Repo.GitRepo, rel); err != nil { if err = models.NewRelease(ctx.Repo.GitRepo, rel, attachments); err != nil {
ctx.Data["Err_TagName"] = true ctx.Data["Err_TagName"] = true
switch { switch {
case models.IsErrReleaseAlreadyExist(err): case models.IsErrReleaseAlreadyExist(err):
@ -211,7 +229,7 @@ func NewReleasePost(ctx *context.Context, f form.NewRelease) {
case models.IsErrInvalidTagName(err): case models.IsErrInvalidTagName(err):
ctx.RenderWithErr(ctx.Tr("repo.release.tag_name_invalid"), RELEASE_NEW, &f) ctx.RenderWithErr(ctx.Tr("repo.release.tag_name_invalid"), RELEASE_NEW, &f)
default: default:
ctx.Handle(500, "CreateRelease", err) ctx.Handle(500, "NewRelease", err)
} }
return return
} }
@ -224,6 +242,7 @@ func EditRelease(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("repo.release.edit_release") ctx.Data["Title"] = ctx.Tr("repo.release.edit_release")
ctx.Data["PageIsReleaseList"] = true ctx.Data["PageIsReleaseList"] = true
ctx.Data["PageIsEditRelease"] = true ctx.Data["PageIsEditRelease"] = true
renderReleaseAttachmentSettings(ctx)
tagName := ctx.Params("*") tagName := ctx.Params("*")
rel, err := models.GetRelease(ctx.Repo.Repository.ID, tagName) rel, err := models.GetRelease(ctx.Repo.Repository.ID, tagName)
@ -240,6 +259,7 @@ func EditRelease(ctx *context.Context) {
ctx.Data["tag_target"] = rel.Target ctx.Data["tag_target"] = rel.Target
ctx.Data["title"] = rel.Title ctx.Data["title"] = rel.Title
ctx.Data["content"] = rel.Note ctx.Data["content"] = rel.Note
ctx.Data["attachments"] = rel.Attachments
ctx.Data["prerelease"] = rel.IsPrerelease ctx.Data["prerelease"] = rel.IsPrerelease
ctx.Data["IsDraft"] = rel.IsDraft ctx.Data["IsDraft"] = rel.IsDraft
@ -250,6 +270,7 @@ func EditReleasePost(ctx *context.Context, f form.EditRelease) {
ctx.Data["Title"] = ctx.Tr("repo.release.edit_release") ctx.Data["Title"] = ctx.Tr("repo.release.edit_release")
ctx.Data["PageIsReleaseList"] = true ctx.Data["PageIsReleaseList"] = true
ctx.Data["PageIsEditRelease"] = true ctx.Data["PageIsEditRelease"] = true
renderReleaseAttachmentSettings(ctx)
tagName := ctx.Params("*") tagName := ctx.Params("*")
rel, err := models.GetRelease(ctx.Repo.Repository.ID, tagName) rel, err := models.GetRelease(ctx.Repo.Repository.ID, tagName)
@ -265,6 +286,7 @@ func EditReleasePost(ctx *context.Context, f form.EditRelease) {
ctx.Data["tag_target"] = rel.Target ctx.Data["tag_target"] = rel.Target
ctx.Data["title"] = rel.Title ctx.Data["title"] = rel.Title
ctx.Data["content"] = rel.Note ctx.Data["content"] = rel.Note
ctx.Data["attachments"] = rel.Attachments
ctx.Data["prerelease"] = rel.IsPrerelease ctx.Data["prerelease"] = rel.IsPrerelease
ctx.Data["IsDraft"] = rel.IsDraft ctx.Data["IsDraft"] = rel.IsDraft
@ -273,18 +295,31 @@ func EditReleasePost(ctx *context.Context, f form.EditRelease) {
return return
} }
var attachments []string
if setting.Release.Attachment.Enabled {
attachments = f.Files
}
isPublish := rel.IsDraft && len(f.Draft) == 0 isPublish := rel.IsDraft && len(f.Draft) == 0
rel.Title = f.Title rel.Title = f.Title
rel.Note = f.Content rel.Note = f.Content
rel.IsDraft = len(f.Draft) > 0 rel.IsDraft = len(f.Draft) > 0
rel.IsPrerelease = f.Prerelease rel.IsPrerelease = f.Prerelease
if err = models.UpdateRelease(ctx.User, ctx.Repo.GitRepo, rel, isPublish); err != nil { if err = models.UpdateRelease(ctx.User, ctx.Repo.GitRepo, rel, isPublish, attachments); err != nil {
ctx.Handle(500, "UpdateRelease", err) ctx.Handle(500, "UpdateRelease", err)
return return
} }
ctx.Redirect(ctx.Repo.RepoLink + "/releases") ctx.Redirect(ctx.Repo.RepoLink + "/releases")
} }
func UploadReleaseAttachment(ctx *context.Context) {
if !setting.Release.Attachment.Enabled {
ctx.NotFound()
return
}
uploadAttachment(ctx, setting.Release.Attachment.AllowedTypes)
}
func DeleteRelease(ctx *context.Context) { func DeleteRelease(ctx *context.Context) {
if err := models.DeleteReleaseOfRepoByID(ctx.Repo.Repository.ID, ctx.QueryInt64("id")); err != nil { if err := models.DeleteReleaseOfRepoByID(ctx.Repo.Repository.ID, ctx.QueryInt64("id")); err != nil {
ctx.Flash.Error("DeleteReleaseByID: " + err.Error()) ctx.Flash.Error("DeleteReleaseByID: " + err.Error())

2
templates/.VERSION

@ -1 +1 @@
0.10.17.0313 0.10.18.0313

11
templates/repo/release/list.tmpl

@ -51,12 +51,19 @@
<div class="download"> <div class="download">
<h2>{{$.i18n.Tr "repo.release.downloads"}}</h2> <h2>{{$.i18n.Tr "repo.release.downloads"}}</h2>
<ul class="list"> <ul class="list">
{{range .Attachments}}
<li> <li>
<a href="{{$.RepoLink}}/archive/{{.TagName}}.zip" rel="nofollow"><i class="octicon octicon-file-zip"></i> {{$.i18n.Tr "repo.release.source_code"}} (ZIP)</a> <i class="octicon octicon-package"></i> <a href="{{AppSubUrl}}/attachments/{{.UUID}}" rel="nofollow">{{.Name}}</a>
</li>
{{end}}
{{if not .IsDraft}}
<li>
<i class="octicon octicon-file-zip"></i> <a href="{{$.RepoLink}}/archive/{{.TagName}}.zip" rel="nofollow">{{$.i18n.Tr "repo.release.source_code"}} (ZIP)</a>
</li> </li>
<li> <li>
<a href="{{$.RepoLink}}/archive/{{.TagName}}.tar.gz"><i class="octicon octicon-file-zip"></i> {{$.i18n.Tr "repo.release.source_code"}} (TAR.GZ)</a> <i class="octicon octicon-file-zip"></i> <a href="{{$.RepoLink}}/archive/{{.TagName}}.tar.gz">{{$.i18n.Tr "repo.release.source_code"}} (TAR.GZ)</a>
</li> </li>
{{end}}
</ul> </ul>
</div> </div>
{{else}} {{else}}

47
templates/repo/release/new.tmpl

@ -48,10 +48,28 @@
<label>{{.i18n.Tr "repo.release.content"}}</label> <label>{{.i18n.Tr "repo.release.content"}}</label>
<textarea name="content">{{.content}}</textarea> <textarea name="content">{{.content}}</textarea>
</div> </div>
{{if .attachments}}
<table class="ui table">
<thead></thead>
<tbody>
{{range .attachments}}
<tr>
<td>
<a target="_blank" href="{{AppSubUrl}}/attachments/{{.UUID}}" rel="nofollow">{{.Name}}</a>
<a class="ui text red right delete-attachment-button" href="#"><i class="octicon octicon-x" data-uuid="{{.UUID}}"></i></a>
<input name="files" type="hidden" value="{{.UUID}}">
</td>
</tr>
{{end}}
</tbody>
</table>
{{end}}
{{if .IsAttachmentEnabled}}
<div class="files"></div>
<div class="ui basic button dropzone" id="dropzone" data-upload-url="{{AppSubUrl}}/releases/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>
<div class="ui container"> <div class="ui container">
<div class="ui divider"></div>
<div class="ui text right">
<div class="prerelease field"> <div class="prerelease field">
<div class="ui checkbox"> <div class="ui checkbox">
<input type="checkbox" name="prerelease" {{if .prerelease}}checked{{end}}> <input type="checkbox" name="prerelease" {{if .prerelease}}checked{{end}}>
@ -59,14 +77,8 @@
</div> </div>
</div> </div>
<span class="help">{{.i18n.Tr "repo.release.prerelease_helper"}}</span> <span class="help">{{.i18n.Tr "repo.release.prerelease_helper"}}</span>
<div class="ui divider"></div>
<div class="field"> <div class="field">
<a class="ui blue basic button" href="{{.RepoLink}}/releases">
{{.i18n.Tr "repo.release.cancel"}}
</a>
{{/* Release didn't save as draft at first time is not possible to be draft again because the Git tag had been created. */}}
{{if or (not .PageIsEditRelease) .IsDraft}}
<input class="ui grey button" type="submit" name="draft" value="{{.i18n.Tr "repo.release.save_draft"}}"/>
{{end}}
{{if .PageIsEditRelease}} {{if .PageIsEditRelease}}
<button class="ui green button"> <button class="ui green button">
{{if .IsDraft}} {{if .IsDraft}}
@ -83,7 +95,13 @@
{{.i18n.Tr "repo.release.publish"}} {{.i18n.Tr "repo.release.publish"}}
</button> </button>
{{end}} {{end}}
</div> {{/* Release didn't save as draft at first time is not possible to be draft again because the Git tag had been created. */}}
{{if or (not .PageIsEditRelease) .IsDraft}}
<input class="ui grey button" type="submit" name="draft" value="{{.i18n.Tr "repo.release.save_draft"}}"/>
{{end}}
<a class="ui basic button" href="{{.RepoLink}}/releases">
{{.i18n.Tr "repo.release.cancel"}}
</a>
</div> </div>
</div> </div>
</form> </form>
@ -102,4 +120,13 @@
{{template "base/delete_modal_actions" .}} {{template "base/delete_modal_actions" .}}
</div> </div>
{{end}} {{end}}
<script>
// Delete attachment field
$('.delete-attachment-button').click(function (e) {
$(this).parentsUntil('tbody').remove();
e.preventDefault();
})
</script>
{{template "base/footer" .}} {{template "base/footer" .}}

Loading…
Cancel
Save