Compare commits

...

5 Commits

Author SHA1 Message Date
Giteabot
87c31c2ffe
Set X-Gitea-Debug header once (#23361) (#23381) 2023-03-09 06:33:05 +08:00
Giteabot
54c674c936
Fix panic when getting notes by ref (#23372) (#23377)
Backport #23372

Fix #23357 .

Now the `/repos/{owner}/{repo}/git/notes/{sha}` API supports getting
notes by a ref or sha
(https://try.gitea.io/api/swagger#/repository/repoGetNote). But the
`GetNote` func can only accept commit ID.

a12f575737/modules/git/notes_nogogit.go (L18)

So we need to convert the query parameter to commit ID before calling
`GetNote`.

Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-authored-by: techknowlogick <techknowlogick@gitea.io>
2023-03-08 15:17:31 -05:00
delvh
2ba58fab22
Pass context to avatar for projects view (#23359) (#23378)
Backport of #23359.

Previously, a 500 response was returned when

- an issue had assignees
- the issue was assigned to a project
- you tried to view this project
2023-03-08 11:57:09 -06:00
wxiaoguang
cd7bd8568c
Fix incorrect display for comment context menu (#23343) (#23344)
Backport #23343

Fix a regression of #23014: the `a` couldn't be used here because
Fomantic UI has style conflicts: `.ui.comments .comment .actions a {
display: inline-block; }`

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2023-03-08 23:18:53 +08:00
Giteabot
cf80f829b4
Do not recognize text files as audio (#23355) (#23368)
Backport #23355

Close #17108

This PR uses a trick (removing the ID3 tag) to detect the content again
to to see whether the content is text type.

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: delvh <dev.lh@web.de>
Co-authored-by: techknowlogick <techknowlogick@gitea.io>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2023-03-08 16:03:05 +08:00
12 changed files with 46 additions and 21 deletions

View File

@ -244,7 +244,7 @@ func APIContexter() func(http.Handler) http.Handler {
}
}
httpcache.AddCacheControlToHeader(ctx.Resp.Header(), 0, "no-transform")
httpcache.SetCacheControlInHeader(ctx.Resp.Header(), 0, "no-transform")
ctx.Resp.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions)
ctx.Data["Context"] = &ctx

View File

@ -388,7 +388,7 @@ func (ctx *Context) SetServeHeaders(opts *ServeHeaderOptions) {
if duration == 0 {
duration = 5 * time.Minute
}
httpcache.AddCacheControlToHeader(header, duration)
httpcache.SetCacheControlInHeader(header, duration)
if !opts.LastModified.IsZero() {
header.Set("Last-Modified", opts.LastModified.UTC().Format(http.TimeFormat))
@ -753,7 +753,7 @@ func Contexter(ctx context.Context) func(next http.Handler) http.Handler {
}
}
httpcache.AddCacheControlToHeader(ctx.Resp.Header(), 0, "no-transform")
httpcache.SetCacheControlInHeader(ctx.Resp.Header(), 0, "no-transform")
ctx.Resp.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions)
ctx.Data["CsrfToken"] = ctx.csrf.GetToken()

View File

@ -15,8 +15,8 @@ import (
"code.gitea.io/gitea/modules/setting"
)
// AddCacheControlToHeader adds suitable cache-control headers to response
func AddCacheControlToHeader(h http.Header, maxAge time.Duration, additionalDirectives ...string) {
// SetCacheControlInHeader sets suitable cache-control headers in the response
func SetCacheControlInHeader(h http.Header, maxAge time.Duration, additionalDirectives ...string) {
directives := make([]string, 0, 2+len(additionalDirectives))
// "max-age=0 + must-revalidate" (aka "no-cache") is preferred instead of "no-store"
@ -31,7 +31,7 @@ func AddCacheControlToHeader(h http.Header, maxAge time.Duration, additionalDire
directives = append(directives, "max-age=0", "private", "must-revalidate")
// to remind users they are using non-prod setting.
h.Add("X-Gitea-Debug", "RUN_MODE="+setting.RunMode)
h.Set("X-Gitea-Debug", "RUN_MODE="+setting.RunMode)
}
h.Set("Cache-Control", strings.Join(append(directives, additionalDirectives...), ", "))
@ -50,7 +50,7 @@ func HandleTimeCache(req *http.Request, w http.ResponseWriter, fi os.FileInfo) (
// HandleGenericTimeCache handles time-based caching for a HTTP request
func HandleGenericTimeCache(req *http.Request, w http.ResponseWriter, lastModified time.Time) (handled bool) {
AddCacheControlToHeader(w.Header(), setting.StaticCacheTime)
SetCacheControlInHeader(w.Header(), setting.StaticCacheTime)
ifModifiedSince := req.Header.Get("If-Modified-Since")
if ifModifiedSince != "" {
@ -81,7 +81,7 @@ func HandleGenericETagCache(req *http.Request, w http.ResponseWriter, etag strin
return true
}
}
AddCacheControlToHeader(w.Header(), setting.StaticCacheTime)
SetCacheControlInHeader(w.Header(), setting.StaticCacheTime)
return false
}
@ -125,6 +125,6 @@ func HandleGenericETagTimeCache(req *http.Request, w http.ResponseWriter, etag s
}
}
}
AddCacheControlToHeader(w.Header(), setting.StaticCacheTime)
SetCacheControlInHeader(w.Header(), setting.StaticCacheTime)
return false
}

View File

@ -106,6 +106,16 @@ func DetectContentType(data []byte) SniffedType {
}
}
if strings.HasPrefix(ct, "audio/") && bytes.HasPrefix(data, []byte("ID3")) {
// The MP3 detection is quite inaccurate, any content with "ID3" prefix will result in "audio/mpeg".
// So remove the "ID3" prefix and detect again, if result is text, then it must be text content.
// This works especially because audio files contain many unprintable/invalid characters like `0x00`
ct2 := http.DetectContentType(data[3:])
if strings.HasPrefix(ct2, "text/") {
ct = ct2
}
}
return SniffedType{ct}
}

View File

@ -109,6 +109,10 @@ func TestIsAudio(t *testing.T) {
mp3, _ := base64.StdEncoding.DecodeString("SUQzBAAAAAABAFRYWFgAAAASAAADbWFqb3JfYnJhbmQAbXA0MgBUWFhYAAAAEQAAA21pbm9yX3Zl")
assert.True(t, DetectContentType(mp3).IsAudio())
assert.False(t, DetectContentType([]byte("plain text")).IsAudio())
assert.True(t, DetectContentType([]byte("ID3Toy\000")).IsAudio())
assert.True(t, DetectContentType([]byte("ID3Toy\n====\t* hi 🌞, ...")).IsText()) // test ID3 tag for plain text
assert.True(t, DetectContentType([]byte("ID3Toy\n====\t* hi 🌞, ..."+"🌛"[0:2])).IsText()) // test ID3 tag with incomplete UTF8 char
}
func TestDetectContentTypeFromReader(t *testing.T) {

View File

@ -58,8 +58,18 @@ func getNote(ctx *context.APIContext, identifier string) {
return
}
commitSHA, err := ctx.Repo.GitRepo.ConvertToSHA1(identifier)
if err != nil {
if git.IsErrNotExist(err) {
ctx.NotFound(err)
} else {
ctx.Error(http.StatusInternalServerError, "ConvertToSHA1", err)
}
return
}
var note git.Note
if err := git.GetNote(ctx, ctx.Repo.GitRepo, identifier, &note); err != nil {
if err := git.GetNote(ctx, ctx.Repo.GitRepo, commitSHA.String(), &note); err != nil {
if git.IsErrNotExist(err) {
ctx.NotFound(identifier)
return

View File

@ -64,7 +64,7 @@ func installRecovery(ctx goctx.Context) func(next http.Handler) http.Handler {
"SignedUserName": "",
}
httpcache.AddCacheControlToHeader(w.Header(), 0, "no-transform")
httpcache.SetCacheControlInHeader(w.Header(), 0, "no-transform")
w.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions)
if !setting.IsProd {

View File

@ -158,7 +158,7 @@ func Recovery(ctx goctx.Context) func(next http.Handler) http.Handler {
store["SignedUserName"] = ""
}
httpcache.AddCacheControlToHeader(w.Header(), 0, "no-transform")
httpcache.SetCacheControlInHeader(w.Header(), 0, "no-transform")
w.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions)
if !setting.IsProd {

View File

@ -17,7 +17,7 @@ func cacheableRedirect(ctx *context.Context, location string) {
// here we should not use `setting.StaticCacheTime`, it is pretty long (default: 6 hours)
// we must make sure the redirection cache time is short enough, otherwise a user won't see the updated avatar in 6 hours
// it's OK to make the cache time short, it is only a redirection, and doesn't cost much to make a new request
httpcache.AddCacheControlToHeader(ctx.Resp.Header(), 5*time.Minute)
httpcache.SetCacheControlInHeader(ctx.Resp.Header(), 5*time.Minute)
ctx.Redirect(location)
}

View File

@ -238,7 +238,7 @@
{{end}}
<div class="right floated">
{{range .Assignees}}
<a class="tooltip" target="_blank" href="{{.HomeLink}}" data-content="{{$.locale.Tr "repo.projects.board.assigned_to"}} {{.Name}}">{{avatar . 28 "mini gt-mr-3"}}</a>
<a class="tooltip" target="_blank" href="{{.HomeLink}}" data-content="{{$.locale.Tr "repo.projects.board.assigned_to"}} {{.Name}}">{{avatar $.Context . 28 "mini gt-mr-3"}}</a>
{{end}}
</div>
</div>

View File

@ -10,16 +10,16 @@
{{else}}
{{$referenceUrl = Printf "%s/files#%s" .ctx.Issue.Link .item.HashTag}}
{{end}}
<a class="item context" data-clipboard-text-type="url" data-clipboard-text="{{AppSubUrl}}{{$referenceUrl}}">{{.ctx.locale.Tr "repo.issues.context.copy_link"}}</a>
<a class="item context quote-reply {{if .diff}}quote-reply-diff{{end}}" data-target="{{.item.HashTag}}-raw">{{.ctx.locale.Tr "repo.issues.context.quote_reply"}}</a>
<div class="item context js-aria-clickable" data-clipboard-text-type="url" data-clipboard-text="{{AppSubUrl}}{{$referenceUrl}}">{{.ctx.locale.Tr "repo.issues.context.copy_link"}}</div>
<div class="item context js-aria-clickable quote-reply {{if .diff}}quote-reply-diff{{end}}" data-target="{{.item.HashTag}}-raw">{{.ctx.locale.Tr "repo.issues.context.quote_reply"}}</div>
{{if not .ctx.UnitIssuesGlobalDisabled}}
<a class="item context reference-issue" data-target="{{.item.HashTag}}-raw" data-modal="#reference-issue-modal" data-poster="{{.item.Poster.GetDisplayName}}" data-poster-username="{{.item.Poster.Name}}" data-reference="{{$referenceUrl}}">{{.ctx.locale.Tr "repo.issues.context.reference_issue"}}</a>
<div class="item context js-aria-clickable reference-issue" data-target="{{.item.HashTag}}-raw" data-modal="#reference-issue-modal" data-poster="{{.item.Poster.GetDisplayName}}" data-poster-username="{{.item.Poster.Name}}" data-reference="{{$referenceUrl}}">{{.ctx.locale.Tr "repo.issues.context.reference_issue"}}</div>
{{end}}
{{if or .ctx.Permission.IsAdmin .IsCommentPoster .ctx.HasIssuesOrPullsWritePermission}}
<div class="divider"></div>
<a class="item context edit-content">{{.ctx.locale.Tr "repo.issues.context.edit"}}</a>
<div class="item context js-aria-clickable edit-content">{{.ctx.locale.Tr "repo.issues.context.edit"}}</div>
{{if .delete}}
<a class="item context delete-comment" data-comment-id={{.item.HashTag}} data-url="{{.ctx.RepoLink}}/comments/{{.item.ID}}/delete" data-locale="{{.ctx.locale.Tr "repo.issues.delete_comment_confirm"}}">{{.ctx.locale.Tr "repo.issues.context.delete"}}</a>
<div class="item context js-aria-clickable delete-comment" data-comment-id={{.item.HashTag}} data-url="{{.ctx.RepoLink}}/comments/{{.item.ID}}/delete" data-locale="{{.ctx.locale.Tr "repo.issues.delete_comment_confirm"}}">{{.ctx.locale.Tr "repo.issues.context.delete"}}</div>
{{end}}
{{end}}
</div>

View File

@ -83,8 +83,9 @@ function attachOneDropdownAria($dropdown) {
if (e.key === 'Enter') {
let $item = $dropdown.dropdown('get item', $dropdown.dropdown('get value'));
if (!$item) $item = $menu.find('> .item.selected'); // when dropdown filters items by input, there is no "value", so query the "selected" item
// if the selected item is clickable, then trigger the click event. in the future there could be a special CSS class for it.
if ($item && $item.is('a')) $item[0].click();
// if the selected item is clickable, then trigger the click event.
// we can not click any item without check, because Fomantic code might also handle the Enter event. that would result in double click.
if ($item && ($item.is('a') || $item.is('.js-aria-clickable'))) $item[0].click();
}
});