diff --git a/integrations/release_test.go b/integrations/release_test.go index c817dcaecf..a14ad8434e 100644 --- a/integrations/release_test.go +++ b/integrations/release_test.go @@ -52,7 +52,7 @@ func checkLatestReleaseAndCount(t *testing.T, session *TestSession, repoURL, ver htmlDoc := NewHTMLParser(t, resp.Body) labelText := htmlDoc.doc.Find("#release-list > li .meta .label").First().Text() assert.EqualValues(t, label, labelText) - titleText := htmlDoc.doc.Find("#release-list > li .detail h3 a").First().Text() + titleText := htmlDoc.doc.Find("#release-list > li .detail h4 a").First().Text() assert.EqualValues(t, version, titleText) releaseList := htmlDoc.doc.Find("#release-list > li") @@ -83,7 +83,7 @@ func TestCreateRelease(t *testing.T) { session := loginUser(t, "user2") createNewRelease(t, session, "/user2/repo1", "v0.0.1", "v0.0.1", false, false) - checkLatestReleaseAndCount(t, session, "/user2/repo1", "v0.0.1", i18n.Tr("en", "repo.release.stable"), 3) + checkLatestReleaseAndCount(t, session, "/user2/repo1", "v0.0.1", i18n.Tr("en", "repo.release.stable"), 2) } func TestCreateReleasePreRelease(t *testing.T) { @@ -92,7 +92,7 @@ func TestCreateReleasePreRelease(t *testing.T) { session := loginUser(t, "user2") createNewRelease(t, session, "/user2/repo1", "v0.0.1", "v0.0.1", true, false) - checkLatestReleaseAndCount(t, session, "/user2/repo1", "v0.0.1", i18n.Tr("en", "repo.release.prerelease"), 3) + checkLatestReleaseAndCount(t, session, "/user2/repo1", "v0.0.1", i18n.Tr("en", "repo.release.prerelease"), 2) } func TestCreateReleaseDraft(t *testing.T) { @@ -101,7 +101,7 @@ func TestCreateReleaseDraft(t *testing.T) { session := loginUser(t, "user2") createNewRelease(t, session, "/user2/repo1", "v0.0.1", "v0.0.1", false, true) - checkLatestReleaseAndCount(t, session, "/user2/repo1", "v0.0.1", i18n.Tr("en", "repo.release.draft"), 3) + checkLatestReleaseAndCount(t, session, "/user2/repo1", "v0.0.1", i18n.Tr("en", "repo.release.draft"), 2) } func TestCreateReleasePaging(t *testing.T) { diff --git a/models/release.go b/models/release.go index b8ffb257a2..547327e652 100644 --- a/models/release.go +++ b/models/release.go @@ -53,7 +53,11 @@ func (r *Release) loadAttributes(e Engine) error { if r.Publisher == nil { r.Publisher, err = getUserByID(e, r.PublisherID) if err != nil { - return err + if IsErrUserNotExist(err) { + r.Publisher = NewGhostUser() + } else { + return err + } } } return getReleaseAttachments(e, r) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 60557aea76..a25f77b030 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -808,7 +808,9 @@ org_labels_desc_manage = manage milestones = Milestones commits = Commits commit = Commit +release = Release releases = Releases +released_this = released this file_raw = Raw file_history = History file_view_raw = View Raw @@ -1796,6 +1798,8 @@ diff.protected = Protected releases.desc = Track project versions and downloads. release.releases = Releases +release.detail = Release details +release.tags = Tags release.new_release = New Release release.draft = Draft release.prerelease = Pre-Release @@ -1818,11 +1822,15 @@ release.publish = Publish Release release.save_draft = Save Draft release.edit_release = Update Release release.delete_release = Delete Release +release.delete_tag = Delete Tag release.deletion = Delete Release -release.deletion_desc = Deleting a release removes its Git tag from the repository. Repository contents and history remain unchanged. Continue? +release.deletion_desc = Deleting a release only removes it from Gitea. Git tag, repository contents and history remain unchanged. Continue? release.deletion_success = The release has been deleted. +release.deletion_tag_desc = Will delete this tag from repository. Repository contents and history remain unchanged. Continue? +release.deletion_tag_success = The tag has been deleted. release.tag_name_already_exist = A release with this tag name already exists. release.tag_name_invalid = The tag name is not valid. +release.tag_already_exist = This tag name already exists. release.downloads = Downloads release.download_count = Downloads: %s diff --git a/routers/repo/release.go b/routers/repo/release.go index 73c42ec7c4..4d75c37c87 100644 --- a/routers/repo/release.go +++ b/routers/repo/release.go @@ -55,10 +55,26 @@ func calReleaseNumCommitsBehind(repoCtx *context.Repository, release *models.Rel // Releases render releases list page func Releases(ctx *context.Context) { - ctx.Data["Title"] = ctx.Tr("repo.release.releases") + releasesOrTags(ctx, false) +} + +// TagsList render tags list page +func TagsList(ctx *context.Context) { + releasesOrTags(ctx, true) +} + +func releasesOrTags(ctx *context.Context, isTagList bool) { ctx.Data["PageIsReleaseList"] = true ctx.Data["DefaultBranch"] = ctx.Repo.Repository.DefaultBranch + if isTagList { + ctx.Data["Title"] = ctx.Tr("repo.release.tags") + ctx.Data["PageIsTagList"] = true + } else { + ctx.Data["Title"] = ctx.Tr("repo.release.releases") + ctx.Data["PageIsTagList"] = false + } + writeAccess := ctx.Repo.CanWrite(models.UnitTypeReleases) ctx.Data["CanCreateRelease"] = writeAccess && !ctx.Repo.Repository.IsArchived @@ -68,7 +84,7 @@ func Releases(ctx *context.Context) { PageSize: convert.ToCorrectPageSize(ctx.QueryInt("limit")), }, IncludeDrafts: writeAccess, - IncludeTags: true, + IncludeTags: isTagList, } releases, err := models.GetReleasesByRepoID(ctx.Repo.Repository.ID, opts) @@ -83,8 +99,7 @@ func Releases(ctx *context.Context) { return } - err = models.GetReleaseAttachments(releases...) - if err != nil { + if err = models.GetReleaseAttachments(releases...); err != nil { ctx.ServerError("GetReleaseAttachments", err) return } @@ -118,6 +133,7 @@ func Releases(ctx *context.Context) { } ctx.Data["Releases"] = releases + ctx.Data["ReleasesNum"] = len(releases) pager := context.NewPagination(int(count), opts.PageSize, opts.Page, 5) pager.SetDefaultParams(ctx) @@ -194,6 +210,20 @@ 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 + if tagName := ctx.Query("tag"); len(tagName) > 0 { + rel, err := models.GetRelease(ctx.Repo.Repository.ID, tagName) + if err != nil && !models.IsErrReleaseNotExist(err) { + ctx.ServerError("GetRelease", err) + return + } + + if rel != nil { + ctx.Data["tag_name"] = rel.TagName + ctx.Data["tag_target"] = rel.Target + ctx.Data["title"] = rel.Title + ctx.Data["content"] = rel.Note + } + } ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled upload.AddUploadContext(ctx, "release") ctx.HTML(200, tplReleaseNew) @@ -354,10 +384,30 @@ func EditReleasePost(ctx *context.Context, form auth.EditReleaseForm) { // DeleteRelease delete a release func DeleteRelease(ctx *context.Context) { - if err := releaseservice.DeleteReleaseByID(ctx.QueryInt64("id"), ctx.User, true); err != nil { + deleteReleaseOrTag(ctx, false) +} + +// DeleteTag delete a tag +func DeleteTag(ctx *context.Context) { + deleteReleaseOrTag(ctx, true) +} + +func deleteReleaseOrTag(ctx *context.Context, isDelTag bool) { + if err := releaseservice.DeleteReleaseByID(ctx.QueryInt64("id"), ctx.User, isDelTag); err != nil { ctx.Flash.Error("DeleteReleaseByID: " + err.Error()) } else { - ctx.Flash.Success(ctx.Tr("repo.release.deletion_success")) + if isDelTag { + ctx.Flash.Success(ctx.Tr("repo.release.deletion_tag_success")) + } else { + ctx.Flash.Success(ctx.Tr("repo.release.deletion_success")) + } + } + + if isDelTag { + ctx.JSON(200, map[string]interface{}{ + "redirect": ctx.Repo.RepoLink + "/tags", + }) + return } ctx.JSON(200, map[string]interface{}{ diff --git a/routers/routes/routes.go b/routers/routes/routes.go index f123613b1f..eafdbe4e49 100644 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -896,12 +896,14 @@ func RegisterRoutes(m *macaron.Macaron) { // Releases m.Group("/:username/:reponame", func() { + m.Get("/tags", repo.TagsList, repo.MustBeNotEmpty, + reqRepoCodeReader, context.RepoRefByType(context.RepoRefTag)) m.Group("/releases", func() { m.Get("/", repo.Releases) m.Get("/tag/*", repo.SingleRelease) m.Get("/latest", repo.LatestRelease) m.Get("/attachments/:uuid", repo.GetAttachment) - }, repo.MustBeNotEmpty, context.RepoRefByType(context.RepoRefTag)) + }, repo.MustBeNotEmpty, reqRepoReleaseReader, context.RepoRefByType(context.RepoRefTag)) m.Group("/releases", func() { m.Get("/new", repo.NewRelease) m.Post("/new", bindIgnErr(auth.NewReleaseForm{}), repo.NewReleasePost) @@ -909,6 +911,8 @@ func RegisterRoutes(m *macaron.Macaron) { m.Post("/attachments", repo.UploadReleaseAttachment) m.Post("/attachments/remove", repo.DeleteAttachment) }, reqSignIn, repo.MustBeNotEmpty, context.RepoMustNotBeArchived(), reqRepoReleaseWriter, context.RepoRef()) + m.Post("/tags/delete", repo.DeleteTag, reqSignIn, + repo.MustBeNotEmpty, context.RepoMustNotBeArchived(), reqRepoCodeWriter, context.RepoRef()) m.Group("/releases", func() { m.Get("/edit/*", repo.EditRelease) m.Post("/edit/*", bindIgnErr(auth.EditReleaseForm{}), repo.EditReleasePost) diff --git a/services/release/release.go b/services/release/release.go index 0e04a71898..9e0654e860 100644 --- a/services/release/release.go +++ b/services/release/release.go @@ -157,10 +157,6 @@ func DeleteReleaseByID(id int64, doer *models.User, delTag bool) error { } } else { rel.IsTag = true - rel.IsDraft = false - rel.IsPrerelease = false - rel.Title = "" - rel.Note = "" if err = models.UpdateRelease(models.DefaultDBContext(), rel); err != nil { return fmt.Errorf("Update: %v", err) diff --git a/templates/repo/release/list.tmpl b/templates/repo/release/list.tmpl index 36bac57c42..cce331b677 100644 --- a/templates/repo/release/list.tmpl +++ b/templates/repo/release/list.tmpl @@ -3,20 +3,67 @@ {{template "repo/header" .}}
{{template "base/alert" .}} -

- {{.i18n.Tr "repo.release.releases"}} - {{if .CanCreateRelease}} - +

+ {{if (and .CanCreateRelease (not .PageIsTagList))}} + + {{.i18n.Tr "repo.release.new_release"}} + + {{end}} + {{if .PageIsTagList}} +
+ {{if gt .ReleasesNum 0}} +

+
+ {{svg "octicon-tag" 16 "mr-2"}}{{.i18n.Tr "repo.release.tags"}} +
+

+
+ + + + {{range $idx, $release := .Releases}} + + + + {{end}} + +
+

+ {{.TagName}} +

+
+ {{if $.Permission.CanRead $.UnitTypeCode}} + {{svg "octicon-git-commit" 16 "mr-2"}}{{ShortSha .Sha1}} + {{svg "octicon-file-zip" 16 "mr-2"}}ZIP + {{svg "octicon-file-zip" 16 "mr-2"}}TAR.GZ + {{if (and $.CanCreateRelease $release.IsTag)}} + {{svg "octicon-tag" 16 "mr-2"}}{{$.i18n.Tr "repo.release.new_release"}} + {{end}} + {{if (and ($.Permission.CanWrite $.UnitTypeCode) $release.IsTag)}} + + {{svg "octicon-trashcan" 16 "mr-2"}}{{$.i18n.Tr "repo.release.delete_tag"}} + + {{end}} + {{if (not $release.IsTag)}} + {{svg "octicon-tag" 16 "mr-2"}}{{$.i18n.Tr "repo.release.detail"}} + {{end}} + {{end}} +
+
+
+ {{end}} + {{else}} + {{end}} {{template "base/paginate" .}}
+ +{{if (and ($.Permission.CanWrite $.UnitTypeCode) .PageIsTagList)}} + +{{end}} + {{template "base/footer" .}} diff --git a/templates/repo/sub_menu.tmpl b/templates/repo/sub_menu.tmpl index 73d6409a90..2c3fb25a9c 100644 --- a/templates/repo/sub_menu.tmpl +++ b/templates/repo/sub_menu.tmpl @@ -5,11 +5,14 @@
{{svg "octicon-history"}} {{.CommitsCount}} {{.i18n.Tr (TrN .i18n.Lang .CommitsCount "repo.commit" "repo.commits") }}
- {{end}} - {{if and (.Permission.CanRead $.UnitTypeCode) (not .IsEmptyRepo) }}
{{svg "octicon-git-branch"}} {{.BranchesCount}} {{.i18n.Tr (TrN .i18n.Lang .BranchesCount "repo.branch" "repo.branches") }}
+ {{if $.Permission.CanRead $.UnitTypeCode}} +
+ {{svg "octicon-tag"}} {{.NumReleases}} {{.i18n.Tr (TrN .i18n.Lang .NumReleases "repo.release" "repo.releases") }} +
+ {{end}}
{{svg "octicon-database"}} {{SizeFmt .Repository.Size}}
diff --git a/web_src/less/_repository.less b/web_src/less/_repository.less index 9e8df7b37f..45abae9178 100644 --- a/web_src/less/_repository.less +++ b/web_src/less/_repository.less @@ -1901,6 +1901,12 @@ margin-top: 20px; padding-top: 15px; + .release-list-title { + font-size: 2rem; + font-weight: normal; + margin-top: -6px; + } + > li { list-style: none; @@ -1926,7 +1932,7 @@ } .detail { - border-left: 1px solid #dddddd; + border-left: 2px solid #dddddd; .author { img { @@ -1965,7 +1971,7 @@ .dot { width: 9px; height: 9px; - background-color: #cccccc; + background-color: #ddd; z-index: 999; position: absolute; display: block; @@ -1977,6 +1983,13 @@ } } } + + #tags-table { + .release-tag-name { + font-size: 1.5rem; + font-weight: normal; + } + } } &.new.release { diff --git a/web_src/less/helpers.less b/web_src/less/helpers.less index c40619410e..fff866c66c 100644 --- a/web_src/less/helpers.less +++ b/web_src/less/helpers.less @@ -1,6 +1,8 @@ .df { display: flex; } .ac { align-items: center; } .jc { justify-content: center; } +.js { justify-content: flex-start; } +.je { justify-content: flex-end; } .sb { justify-content: space-between; } .m-0 { margin: 0 !important; } diff --git a/web_src/less/themes/theme-arc-green.less b/web_src/less/themes/theme-arc-green.less index 04367e2f66..2a556dd28e 100644 --- a/web_src/less/themes/theme-arc-green.less +++ b/web_src/less/themes/theme-arc-green.less @@ -1510,7 +1510,7 @@ input { .ui.radio.checkbox label::after, .ui.radio.checkbox input:checked ~ label::after, .ui.radio.checkbox input:focus ~ label::after, -.ui.radio.checkbox input:focus:checked ~ label::after, { +.ui.radio.checkbox input:focus:checked ~ label::after { background: #dbdbdb; } @@ -2075,7 +2075,7 @@ footer .container .links > * { } .repository.release #release-list > li .detail .dot { - background-color: #888; + background-color: #505667; border-color: #383c4a; }