fix: correct repo/attatchment absolute url and release layout (#39178)

This commit is contained in:
wxiaoguang
2026-08-31 23:31:17 +00:00
committed by GitHub
parent fc9800b383
commit 4875bb3b2b
21 changed files with 72 additions and 80 deletions
+5 -3
View File
@@ -12,6 +12,7 @@ import (
"path" "path"
"gitea.dev/models/db" "gitea.dev/models/db"
"gitea.dev/modules/httplib"
"gitea.dev/modules/log" "gitea.dev/modules/log"
"gitea.dev/modules/setting" "gitea.dev/modules/setting"
"gitea.dev/modules/storage" "gitea.dev/modules/storage"
@@ -62,12 +63,13 @@ func (a *Attachment) RelativePath() string {
} }
// DownloadURL returns the download url of the attached file // DownloadURL returns the download url of the attached file
func (a *Attachment) DownloadURL() string { func (a *Attachment) DownloadURL(optCtx ...context.Context) string {
// mail template doesn't have context, so we need to use a default one
ctx := util.OptionalArg(optCtx, context.TODO())
if a.CustomDownloadURL != "" { if a.CustomDownloadURL != "" {
return a.CustomDownloadURL return a.CustomDownloadURL
} }
return httplib.MakeAbsoluteURL(ctx, setting.AppSubURL+"/attachments/"+url.PathEscape(a.UUID))
return setting.AppURL + "attachments/" + url.PathEscape(a.UUID)
} }
// ErrAttachmentNotExist represents a "AttachmentNotExist" kind of error. // ErrAttachmentNotExist represents a "AttachmentNotExist" kind of error.
+1 -1
View File
@@ -74,7 +74,7 @@ func TestAttachment_DownloadURL(t *testing.T) {
UUID: "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", UUID: "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11",
ID: 1, ID: 1,
} }
assert.Equal(t, "https://try.gitea.io/attachments/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", attach.DownloadURL()) assert.Equal(t, "https://try.gitea.io/attachments/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", attach.DownloadURL(t.Context()))
} }
func TestUpdateAttachment(t *testing.T) { func TestUpdateAttachment(t *testing.T) {
+1 -1
View File
@@ -433,7 +433,7 @@ func GetReleaseAttachments(ctx context.Context, rels ...*Release) (err error) {
// If the names unique, use the URL with the Name instead of the UUID // If the names unique, use the URL with the Name instead of the UUID
if !hasDuplicateName(release.Attachments) { if !hasDuplicateName(release.Attachments) {
for _, attachment := range release.Attachments { for _, attachment := range release.Attachments {
attachment.CustomDownloadURL = release.Repo.HTMLURL() + "/releases/download/" + url.PathEscape(release.TagName) + "/" + url.PathEscape(attachment.Name) attachment.CustomDownloadURL = release.Repo.HTMLURL(ctx) + "/releases/download/" + url.PathEscape(release.TagName) + "/" + url.PathEscape(attachment.Name)
} }
} }
} }
+1 -1
View File
@@ -124,7 +124,7 @@ func createPackageMetadataResponse(ctx *context.Context, registryURL string, pds
log.Error("GetDoerRepoPermission[%d]: %v", pd.Repository.ID, err) log.Error("GetDoerRepoPermission[%d]: %v", pd.Repository.ID, err)
} else if permission.HasAnyUnitAccessOrPublicAccess() { } else if permission.HasAnyUnitAccessOrPublicAccess() {
pkg.Source = Source{ pkg.Source = Source{
URL: pd.Repository.HTMLURL(), URL: pd.Repository.HTMLURL(ctx),
Type: "git", Type: "git",
Reference: pd.Version.Version, Reference: pd.Version.Version,
} }
+3 -3
View File
@@ -67,7 +67,7 @@ func GetIssueAttachment(ctx *context.APIContext) {
return return
} }
ctx.JSON(http.StatusOK, convert.ToAPIAttachment(ctx.Repo.Repository, attach)) ctx.JSON(http.StatusOK, convert.ToAPIAttachment(ctx, ctx.Repo.Repository, attach))
} }
// ListIssueAttachments lists all attachments of the issue // ListIssueAttachments lists all attachments of the issue
@@ -210,7 +210,7 @@ func CreateIssueAttachment(ctx *context.APIContext) {
return return
} }
ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx.Repo.Repository, attachment)) ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx, ctx.Repo.Repository, attachment))
} }
// EditIssueAttachment updates the given attachment // EditIssueAttachment updates the given attachment
@@ -279,7 +279,7 @@ func EditIssueAttachment(ctx *context.APIContext) {
return return
} }
ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx.Repo.Repository, attachment)) ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx, ctx.Repo.Repository, attachment))
} }
// DeleteIssueAttachment delete a given attachment // DeleteIssueAttachment delete a given attachment
@@ -72,7 +72,7 @@ func GetIssueCommentAttachment(ctx *context.APIContext) {
return return
} }
ctx.JSON(http.StatusOK, convert.ToAPIAttachment(ctx.Repo.Repository, attachment)) ctx.JSON(http.StatusOK, convert.ToAPIAttachment(ctx, ctx.Repo.Repository, attachment))
} }
// ListIssueCommentAttachments lists all attachments of the comment // ListIssueCommentAttachments lists all attachments of the comment
@@ -114,7 +114,7 @@ func ListIssueCommentAttachments(ctx *context.APIContext) {
return return
} }
ctx.JSON(http.StatusOK, convert.ToAPIAttachments(ctx.Repo.Repository, comment.Attachments)) ctx.JSON(http.StatusOK, convert.ToAPIAttachments(ctx, ctx.Repo.Repository, comment.Attachments))
} }
// CreateIssueCommentAttachment creates an attachment and saves the given file // CreateIssueCommentAttachment creates an attachment and saves the given file
@@ -225,7 +225,7 @@ func CreateIssueCommentAttachment(ctx *context.APIContext) {
return return
} }
ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx.Repo.Repository, attachment)) ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx, ctx.Repo.Repository, attachment))
} }
// EditIssueCommentAttachment updates the given attachment // EditIssueCommentAttachment updates the given attachment
@@ -291,7 +291,7 @@ func EditIssueCommentAttachment(ctx *context.APIContext) {
ctx.APIErrorInternal(err) ctx.APIErrorInternal(err)
return return
} }
ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx.Repo.Repository, attach)) ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx, ctx.Repo.Repository, attach))
} }
// DeleteIssueCommentAttachment delete a given attachment // DeleteIssueCommentAttachment delete a given attachment
+3 -3
View File
@@ -98,7 +98,7 @@ func GetReleaseAttachment(ctx *context.APIContext) {
return return
} }
// FIXME Should prove the existence of the given repo, but results in unnecessary database requests // FIXME Should prove the existence of the given repo, but results in unnecessary database requests
ctx.JSON(http.StatusOK, convert.ToAPIAttachment(ctx.Repo.Repository, attach)) ctx.JSON(http.StatusOK, convert.ToAPIAttachment(ctx, ctx.Repo.Repository, attach))
} }
// ListReleaseAttachments lists all attachments of the release // ListReleaseAttachments lists all attachments of the release
@@ -263,7 +263,7 @@ func CreateReleaseAttachment(ctx *context.APIContext) {
return return
} }
ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx.Repo.Repository, attach)) ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx, ctx.Repo.Repository, attach))
} }
// EditReleaseAttachment updates the given attachment // EditReleaseAttachment updates the given attachment
@@ -346,7 +346,7 @@ func EditReleaseAttachment(ctx *context.APIContext) {
ctx.APIErrorInternal(err) ctx.APIErrorInternal(err)
return return
} }
ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx.Repo.Repository, attach)) ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx, ctx.Repo.Repository, attach))
} }
// DeleteReleaseAttachment delete a given attachment // DeleteReleaseAttachment delete a given attachment
+1 -1
View File
@@ -581,7 +581,7 @@ func GetIssueAttachments(ctx *context.Context) {
} }
attachments := make([]*api.Attachment, len(issue.Attachments)) attachments := make([]*api.Attachment, len(issue.Attachments))
for i := 0; i < len(issue.Attachments); i++ { for i := 0; i < len(issue.Attachments); i++ {
attachments[i] = convert.ToAttachment(ctx.Repo.Repository, issue.Attachments[i]) attachments[i] = convert.ToAttachment(ctx, ctx.Repo.Repository, issue.Attachments[i])
} }
ctx.JSON(http.StatusOK, attachments) ctx.JSON(http.StatusOK, attachments)
} }
+1 -1
View File
@@ -444,7 +444,7 @@ func GetCommentAttachments(ctx *context.Context) {
return return
} }
for i := 0; i < len(comment.Attachments); i++ { for i := 0; i < len(comment.Attachments); i++ {
attachments = append(attachments, convert.ToAttachment(ctx.Repo.Repository, comment.Attachments[i])) attachments = append(attachments, convert.ToAttachment(ctx, ctx.Repo.Repository, comment.Attachments[i]))
} }
ctx.JSON(http.StatusOK, attachments) ctx.JSON(http.StatusOK, attachments)
} }
+1 -1
View File
@@ -679,7 +679,7 @@ func TestWebhook(ctx *context.Context) {
apiCommit := &api.PayloadCommit{ apiCommit := &api.PayloadCommit{
ID: commit.ID.String(), ID: commit.ID.String(),
Message: commit.MessageUTF8(), Message: commit.MessageUTF8(),
URL: ctx.Repo.Repository.HTMLURL() + "/commit/" + url.PathEscape(commit.ID.String()), URL: ctx.Repo.Repository.HTMLURL(ctx) + "/commit/" + url.PathEscape(commit.ID.String()),
Author: &api.PayloadUser{ Author: &api.PayloadUser{
Name: commit.Author.Name, Name: commit.Author.Name,
Email: commit.Author.Email, Email: commit.Author.Email,
+16 -14
View File
@@ -4,30 +4,32 @@
package convert package convert
import ( import (
"context"
repo_model "gitea.dev/models/repo" repo_model "gitea.dev/models/repo"
api "gitea.dev/modules/structs" api "gitea.dev/modules/structs"
) )
func WebAssetDownloadURL(repo *repo_model.Repository, attach *repo_model.Attachment) string { func WebAssetDownloadURL(ctx context.Context, repo *repo_model.Repository, attach *repo_model.Attachment) string {
return attach.DownloadURL() return attach.DownloadURL(ctx)
} }
func APIAssetDownloadURL(repo *repo_model.Repository, attach *repo_model.Attachment) string { func APIAssetDownloadURL(ctx context.Context, repo *repo_model.Repository, attach *repo_model.Attachment) string {
return attach.DownloadURL() return attach.DownloadURL(ctx)
} }
// ToAttachment converts models.Attachment to api.Attachment for API usage // ToAttachment converts models.Attachment to api.Attachment for API usage
func ToAttachment(repo *repo_model.Repository, a *repo_model.Attachment) *api.Attachment { func ToAttachment(ctx context.Context, repo *repo_model.Repository, a *repo_model.Attachment) *api.Attachment {
return toAttachment(repo, a, WebAssetDownloadURL) return toAttachment(ctx, repo, a, WebAssetDownloadURL)
} }
// ToAPIAttachment converts models.Attachment to api.Attachment for API usage // ToAPIAttachment converts models.Attachment to api.Attachment for API usage
func ToAPIAttachment(repo *repo_model.Repository, a *repo_model.Attachment) *api.Attachment { func ToAPIAttachment(ctx context.Context, repo *repo_model.Repository, a *repo_model.Attachment) *api.Attachment {
return toAttachment(repo, a, APIAssetDownloadURL) return toAttachment(ctx, repo, a, APIAssetDownloadURL)
} }
// toAttachment converts models.Attachment to api.Attachment for API usage // toAttachment converts models.Attachment to api.Attachment for API usage
func toAttachment(repo *repo_model.Repository, a *repo_model.Attachment, getDownloadURL func(repo *repo_model.Repository, attach *repo_model.Attachment) string) *api.Attachment { func toAttachment(ctx context.Context, repo *repo_model.Repository, a *repo_model.Attachment, getDownloadURL func(ctx context.Context, repo *repo_model.Repository, attach *repo_model.Attachment) string) *api.Attachment {
return &api.Attachment{ return &api.Attachment{
ID: a.ID, ID: a.ID,
Name: a.Name, Name: a.Name,
@@ -35,18 +37,18 @@ func toAttachment(repo *repo_model.Repository, a *repo_model.Attachment, getDown
DownloadCount: a.DownloadCount, DownloadCount: a.DownloadCount,
Size: a.Size, Size: a.Size,
UUID: a.UUID, UUID: a.UUID,
DownloadURL: getDownloadURL(repo, a), // for web request json and api request json, return different download urls DownloadURL: getDownloadURL(ctx, repo, a), // for web/api requests, return different download URLs
} }
} }
func ToAPIAttachments(repo *repo_model.Repository, attachments []*repo_model.Attachment) []*api.Attachment { func ToAPIAttachments(ctx context.Context, repo *repo_model.Repository, attachments []*repo_model.Attachment) []*api.Attachment {
return toAttachments(repo, attachments, APIAssetDownloadURL) return toAttachments(ctx, repo, attachments, APIAssetDownloadURL)
} }
func toAttachments(repo *repo_model.Repository, attachments []*repo_model.Attachment, getDownloadURL func(repo *repo_model.Repository, attach *repo_model.Attachment) string) []*api.Attachment { func toAttachments(ctx context.Context, repo *repo_model.Repository, attachments []*repo_model.Attachment, getDownloadURL func(ctx context.Context, repo *repo_model.Repository, attach *repo_model.Attachment) string) []*api.Attachment {
converted := make([]*api.Attachment, 0, len(attachments)) converted := make([]*api.Attachment, 0, len(attachments))
for _, attachment := range attachments { for _, attachment := range attachments {
converted = append(converted, toAttachment(repo, attachment, getDownloadURL)) converted = append(converted, toAttachment(ctx, repo, attachment, getDownloadURL))
} }
return converted return converted
} }
+2 -2
View File
@@ -33,7 +33,7 @@ func ToAPIIssue(ctx context.Context, doer *user_model.User, issue *issues_model.
return toIssue(ctx, doer, issue, APIAssetDownloadURL) return toIssue(ctx, doer, issue, APIAssetDownloadURL)
} }
func toIssue(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, getDownloadURL func(repo *repo_model.Repository, attach *repo_model.Attachment) string) *api.Issue { func toIssue(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, getDownloadURL func(ctx context.Context, repo *repo_model.Repository, attach *repo_model.Attachment) string) *api.Issue {
if err := issue.LoadPoster(ctx); err != nil { if err := issue.LoadPoster(ctx); err != nil {
return &api.Issue{} return &api.Issue{}
} }
@@ -53,7 +53,7 @@ func toIssue(ctx context.Context, doer *user_model.User, issue *issues_model.Iss
Poster: ToUser(ctx, issue.Poster, doer), Poster: ToUser(ctx, issue.Poster, doer),
Title: issue.Title, Title: issue.Title,
Body: issue.Content, Body: issue.Content,
Attachments: toAttachments(issue.Repo, issue.Attachments, getDownloadURL), Attachments: toAttachments(ctx, issue.Repo, issue.Attachments, getDownloadURL),
Ref: issue.Ref, Ref: issue.Ref,
State: issue.State(), State: issue.State(),
IsLocked: issue.IsLocked, IsLocked: issue.IsLocked,
+1 -1
View File
@@ -23,7 +23,7 @@ func ToAPIComment(ctx context.Context, repo *repo_model.Repository, c *issues_mo
IssueURL: c.IssueURL(ctx), IssueURL: c.IssueURL(ctx),
PRURL: c.PRURL(ctx), PRURL: c.PRURL(ctx),
Body: c.Content, Body: c.Content,
Attachments: ToAPIAttachments(repo, c.Attachments), Attachments: ToAPIAttachments(ctx, repo, c.Attachments),
Created: c.CreatedUnix.AsTime(), Created: c.CreatedUnix.AsTime(),
Updated: c.UpdatedUnix.AsTime(), Updated: c.UpdatedUnix.AsTime(),
} }
+1 -1
View File
@@ -29,6 +29,6 @@ func ToAPIRelease(ctx context.Context, repo *repo_model.Repository, r *repo_mode
CreatedAt: r.CreatedUnix.AsTime(), CreatedAt: r.CreatedUnix.AsTime(),
PublishedAt: util.Iif(r.IsDraft, nil, r.PublishedUnix.AsTimePtr()), PublishedAt: util.Iif(r.IsDraft, nil, r.PublishedUnix.AsTimePtr()),
Publisher: ToUser(ctx, r.Publisher, nil), Publisher: ToUser(ctx, r.Publisher, nil),
Attachments: ToAPIAttachments(repo, r.Attachments), Attachments: ToAPIAttachments(ctx, repo, r.Attachments),
} }
} }
+8 -20
View File
@@ -202,29 +202,17 @@ func getFileContentsByEntryInternal(ctx context.Context, repo *repo_model.Reposi
} }
// Handle links // Handle links
if entry.IsRegular() || entry.IsLink() || entry.IsExecutable() { if entry.IsRegular() || entry.IsLink() || entry.IsExecutable() {
downloadURL, err := url.Parse(repo.HTMLURL() + "/raw/" + refCommit.RefName.RefWebLinkPath() + "/" + util.PathEscapeSegments(opts.TreePath)) downloadURL := repo.HTMLURL(ctx) + "/raw/" + refCommit.RefName.RefWebLinkPath() + "/" + util.PathEscapeSegments(opts.TreePath)
if err != nil { contentsResponse.DownloadURL = &downloadURL
return nil, err
}
downloadURLString := downloadURL.String()
contentsResponse.DownloadURL = &downloadURLString
} }
if !entry.IsSubModule() { if !entry.IsSubModule() {
htmlURL, err := url.Parse(repo.HTMLURL() + "/src/" + refCommit.RefName.RefWebLinkPath() + "/" + util.PathEscapeSegments(opts.TreePath)) htmlURL := repo.HTMLURL(ctx) + "/src/" + refCommit.RefName.RefWebLinkPath() + "/" + util.PathEscapeSegments(opts.TreePath)
if err != nil { contentsResponse.HTMLURL = &htmlURL
return nil, err contentsResponse.Links.HTMLURL = &htmlURL
}
htmlURLString := htmlURL.String()
contentsResponse.HTMLURL = &htmlURLString
contentsResponse.Links.HTMLURL = &htmlURLString
gitURL, err := url.Parse(repo.APIURL() + "/git/blobs/" + url.PathEscape(entry.ID.String())) gitURL := repo.APIURL(ctx) + "/git/blobs/" + url.PathEscape(entry.ID.String())
if err != nil { contentsResponse.GitURL = &gitURL
return nil, err contentsResponse.Links.GitURL = &gitURL
}
gitURLString := gitURL.String()
contentsResponse.GitURL = &gitURLString
contentsResponse.Links.GitURL = &gitURLString
} }
return contentsResponse, nil return contentsResponse, nil
+2 -2
View File
@@ -89,13 +89,13 @@ func GetFileCommitResponse(ctx context.Context, repo *repo_model.Repository, git
} }
} }
} }
commitHTMLURL, _ := url.Parse(repo.HTMLURL() + "/commit/" + url.PathEscape(commit.ID.String())) commitHTMLURL := repo.HTMLURL(ctx) + "/commit/" + url.PathEscape(commit.ID.String())
fileCommit := &api.FileCommitResponse{ fileCommit := &api.FileCommitResponse{
CommitMeta: api.CommitMeta{ CommitMeta: api.CommitMeta{
SHA: commit.ID.String(), SHA: commit.ID.String(),
URL: commitURL.String(), URL: commitURL.String(),
}, },
HTMLURL: commitHTMLURL.String(), HTMLURL: commitHTMLURL,
Author: &api.CommitUser{ Author: &api.CommitUser{
Identity: api.Identity{ Identity: api.Identity{
Name: commit.Author.Name, Name: commit.Author.Name,
+2 -2
View File
@@ -3,8 +3,8 @@
{{$attachments := index $.Page.issuesAttachmentMap .ID}} {{$attachments := index $.Page.issuesAttachmentMap .ID}}
{{if $attachments}} {{if $attachments}}
<div class="card-attachment-images"> <div class="card-attachment-images">
{{range $attachments}} {{range $att := $attachments}}
<img loading="lazy" src="{{.DownloadURL}}" alt="{{.Name}}" /> <img loading="lazy" src="{{$att.DownloadURL ctx}}" alt="{{$att.Name}}" />
{{end}} {{end}}
</div> </div>
{{end}} {{end}}
@@ -3,23 +3,23 @@
<div class="divider"></div> <div class="divider"></div>
{{end}} {{end}}
{{$hasThumbnails := false}} {{$hasThumbnails := false}}
{{- range .Attachments -}} {{- range $att := .Attachments -}}
<div class="tw-flex"> <div class="tw-flex">
<div class="tw-flex-1 tw-p-2"> <div class="tw-flex-1 tw-p-2">
<a target="_blank" href="{{.DownloadURL}}" title="{{ctx.Locale.Tr "repo.issues.attachment.open_tab" .Name}}"> <a target="_blank" href="{{$att.DownloadURL ctx}}" title="{{ctx.Locale.Tr "repo.issues.attachment.open_tab" $att.Name}}">
{{if FilenameIsImage .Name}} {{if FilenameIsImage $att.Name}}
{{if not (StringUtils.Contains (StringUtils.ToString $.RenderedContent) .UUID)}} {{if not (StringUtils.Contains (StringUtils.ToString $.RenderedContent) $att.UUID)}}
{{$hasThumbnails = true}} {{$hasThumbnails = true}}
{{end}} {{end}}
{{svg "octicon-file"}} {{svg "octicon-file"}}
{{else}} {{else}}
{{svg "octicon-desktop-download"}} {{svg "octicon-desktop-download"}}
{{end}} {{end}}
<span><strong>{{.Name}}</strong></span> <span><strong>{{$att.Name}}</strong></span>
</a> </a>
</div> </div>
<div class="flex-text-block tw-p-2"> <div class="flex-text-block tw-p-2">
<span class="ui tw-text-text-light">{{.Size | FormatByteSize}}</span> <span class="ui tw-text-text-light">{{$att.Size | FormatByteSize}}</span>
</div> </div>
</div> </div>
{{end -}} {{end -}}
@@ -30,8 +30,8 @@
{{- range .Attachments -}} {{- range .Attachments -}}
{{if FilenameIsImage .Name}} {{if FilenameIsImage .Name}}
{{if not (StringUtils.Contains (StringUtils.ToString $.RenderedContent) .UUID)}} {{if not (StringUtils.Contains (StringUtils.ToString $.RenderedContent) .UUID)}}
<a target="_blank" href="{{.DownloadURL}}"> <a target="_blank" href="{{.DownloadURL ctx}}">
<img loading="lazy" alt="{{.Name}}" src="{{.DownloadURL}}" title="{{ctx.Locale.Tr "repo.issues.attachment.open_tab" .Name}}"> <img loading="lazy" alt="{{.Name}}" src="{{.DownloadURL ctx}}" title="{{ctx.Locale.Tr "repo.issues.attachment.open_tab" .Name}}">
</a> </a>
{{end}} {{end}}
{{end}} {{end}}
+1 -1
View File
@@ -80,7 +80,7 @@
<ul class="ui divided list attachment-list"> <ul class="ui divided list attachment-list">
{{range $att := $release.Attachments}} {{range $att := $release.Attachments}}
<li class="item"> <li class="item">
<a target="_blank" class="tw-flex-1 gt-ellipsis" rel="nofollow" download href="{{$att.DownloadURL}}"> <a target="_blank" class="tw-flex-1 gt-ellipsis" rel="nofollow" download href="{{$att.DownloadURL ctx}}">
<strong class="flex-text-inline">{{svg "octicon-package" 16 "download-icon"}}<span class="gt-ellipsis">{{$att.Name}}</span></strong> <strong class="flex-text-inline">{{svg "octicon-package" 16 "download-icon"}}<span class="gt-ellipsis">{{$att.Name}}</span></strong>
</a> </a>
<div class="attachment-right-info flex-text-inline"> <div class="attachment-right-info flex-text-inline">
+9 -9
View File
@@ -2,15 +2,14 @@
{{$canReadCode := $.Permission.CanRead ctx.Consts.RepoUnitTypeCode}} {{$canReadCode := $.Permission.CanRead ctx.Consts.RepoUnitTypeCode}}
{{if $canReadReleases}} {{if $canReadReleases}}
<div class="flex-text-block"> <div class="flex-left-right">
<div class="tw-flex-1 tw-flex tw-items-center"> <h2 class="ui compact small menu small-menu-items">
<h2 class="ui compact small menu small-menu-items"> <a class="{{if and .PageIsReleaseList (not .PageIsSingleTag)}}active {{end}}item" href="{{.RepoLink}}/releases">{{ctx.Locale.PrettyNumber .NumReleases}} {{ctx.Locale.TrN .NumReleases "repo.release" "repo.releases"}}</a>
<a class="{{if and .PageIsReleaseList (not .PageIsSingleTag)}}active {{end}}item" href="{{.RepoLink}}/releases">{{ctx.Locale.PrettyNumber .NumReleases}} {{ctx.Locale.TrN .NumReleases "repo.release" "repo.releases"}}</a> {{if $canReadCode}}
{{if $canReadCode}} <a class="{{if or .PageIsTagList .PageIsSingleTag}}active {{end}}item" href="{{.RepoLink}}/tags">{{ctx.Locale.PrettyNumber .NumTags}} {{ctx.Locale.TrN .NumTags "repo.tag" "repo.tags"}}</a>
<a class="{{if or .PageIsTagList .PageIsSingleTag}}active {{end}}item" href="{{.RepoLink}}/tags">{{ctx.Locale.PrettyNumber .NumTags}} {{ctx.Locale.TrN .NumTags "repo.tag" "repo.tags"}}</a> {{end}}
{{end}} </h2>
</h2> <div class="flex-text-block">
</div>
{{if .EnableFeed}} {{if .EnableFeed}}
<a class="ui small button" href="{{.RepoLink}}/{{if .PageIsTagList}}tags{{else}}releases{{end}}.rss"> <a class="ui small button" href="{{.RepoLink}}/{{if .PageIsTagList}}tags{{else}}releases{{end}}.rss">
{{svg "octicon-rss" 16}} {{ctx.Locale.Tr "rss_feed"}} {{svg "octicon-rss" 16}} {{ctx.Locale.Tr "rss_feed"}}
@@ -21,6 +20,7 @@
{{ctx.Locale.Tr "repo.release.new_release"}} {{ctx.Locale.Tr "repo.release.new_release"}}
</a> </a>
{{end}} {{end}}
</div>
</div> </div>
<div class="divider"></div> <div class="divider"></div>
{{else if $canReadCode}} {{else if $canReadCode}}
@@ -52,7 +52,7 @@ func TestAPIGetCommentAttachment(t *testing.T) {
apiAttachment := DecodeJSON(t, resp, &api.Attachment{}) apiAttachment := DecodeJSON(t, resp, &api.Attachment{})
expect := convert.ToAPIAttachment(repo, attachment) expect := convert.ToAPIAttachment(t.Context(), repo, attachment)
assert.Equal(t, expect.ID, apiAttachment.ID) assert.Equal(t, expect.ID, apiAttachment.ID)
assert.Equal(t, expect.Name, apiAttachment.Name) assert.Equal(t, expect.Name, apiAttachment.Name)
assert.Equal(t, expect.UUID, apiAttachment.UUID) assert.Equal(t, expect.UUID, apiAttachment.UUID)