From 1d3240887c519a04c13bcd7e852c6d6ad1cb00b5 Mon Sep 17 00:00:00 2001 From: Mai-Lapyst Date: Fri, 15 Mar 2024 13:44:42 +0100 Subject: [PATCH 0001/3435] Render inline file permalinks --- modules/markup/html.go | 267 +++++++++++++++++++++ modules/markup/html_test.go | 57 +++++ modules/markup/renderer.go | 4 + modules/markup/sanitizer.go | 17 ++ services/markup/processorhelper.go | 81 +++++++ web_src/css/index.css | 1 + web_src/css/markup/content.css | 3 +- web_src/css/markup/filepreview.css | 35 +++ web_src/css/repo/linebutton.css | 3 +- web_src/js/features/repo-unicode-escape.js | 4 +- 10 files changed, 468 insertions(+), 4 deletions(-) create mode 100644 web_src/css/markup/filepreview.css diff --git a/modules/markup/html.go b/modules/markup/html.go index b7291823b5..2501f8062d 100644 --- a/modules/markup/html.go +++ b/modules/markup/html.go @@ -10,10 +10,12 @@ import ( "path" "path/filepath" "regexp" + "strconv" "strings" "sync" "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/charset" "code.gitea.io/gitea/modules/emoji" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" @@ -61,6 +63,9 @@ var ( validLinksPattern = regexp.MustCompile(`^[a-z][\w-]+://`) + // filePreviewPattern matches "http://domain/org/repo/src/commit/COMMIT/filepath#L1-L2" + filePreviewPattern = regexp.MustCompile(`https?://((?:\S+/){3})src/commit/([0-9a-f]{7,64})/(\S+)#(L\d+(?:-L\d+)?)`) + // While this email regex is definitely not perfect and I'm sure you can come up // with edge cases, it is still accepted by the CommonMark specification, as // well as the HTML5 spec: @@ -171,6 +176,7 @@ type processor func(ctx *RenderContext, node *html.Node) var defaultProcessors = []processor{ fullIssuePatternProcessor, comparePatternProcessor, + filePreviewPatternProcessor, fullHashPatternProcessor, shortLinkProcessor, linkProcessor, @@ -1054,6 +1060,267 @@ func comparePatternProcessor(ctx *RenderContext, node *html.Node) { } } +func filePreviewPatternProcessor(ctx *RenderContext, node *html.Node) { + if ctx.Metas == nil { + return + } + if DefaultProcessorHelper.GetRepoFileContent == nil || DefaultProcessorHelper.GetLocale == nil { + return + } + + next := node.NextSibling + for node != nil && node != next { + m := filePreviewPattern.FindStringSubmatchIndex(node.Data) + if m == nil { + return + } + + // Ensure that every group (m[0]...m[9]) has a match + for i := 0; i < 10; i++ { + if m[i] == -1 { + return + } + } + + urlFull := node.Data[m[0]:m[1]] + + // Ensure that we only use links to local repositories + if !strings.HasPrefix(urlFull, setting.AppURL+setting.AppSubURL) { + return + } + + projPath := node.Data[m[2]:m[3]] + projPath = strings.TrimSuffix(projPath, "/") + + commitSha := node.Data[m[4]:m[5]] + filePath := node.Data[m[6]:m[7]] + hash := node.Data[m[8]:m[9]] + + start := m[0] + end := m[1] + + // If url ends in '.', it's very likely that it is not part of the + // actual url but used to finish a sentence. + if strings.HasSuffix(urlFull, ".") { + end-- + urlFull = urlFull[:len(urlFull)-1] + hash = hash[:len(hash)-1] + } + + projPathSegments := strings.Split(projPath, "/") + fileContent, err := DefaultProcessorHelper.GetRepoFileContent( + ctx.Ctx, + projPathSegments[len(projPathSegments)-2], + projPathSegments[len(projPathSegments)-1], + commitSha, filePath, + ) + if err != nil { + return + } + + lineSpecs := strings.Split(hash, "-") + lineCount := len(fileContent) + + var subTitle string + var lineOffset int + + if len(lineSpecs) == 1 { + line, _ := strconv.Atoi(strings.TrimPrefix(lineSpecs[0], "L")) + if line < 1 || line > lineCount { + return + } + + fileContent = fileContent[line-1 : line] + subTitle = "Line " + strconv.Itoa(line) + + lineOffset = line - 1 + } else { + startLine, _ := strconv.Atoi(strings.TrimPrefix(lineSpecs[0], "L")) + endLine, _ := strconv.Atoi(strings.TrimPrefix(lineSpecs[1], "L")) + + if startLine < 1 || endLine < 1 || startLine > lineCount || endLine > lineCount || endLine < startLine { + return + } + + fileContent = fileContent[startLine-1 : endLine] + subTitle = "Lines " + strconv.Itoa(startLine) + " to " + strconv.Itoa(endLine) + + lineOffset = startLine - 1 + } + + table := &html.Node{ + Type: html.ElementNode, + Data: atom.Table.String(), + Attr: []html.Attribute{{Key: "class", Val: "file-preview"}}, + } + tbody := &html.Node{ + Type: html.ElementNode, + Data: atom.Tbody.String(), + } + + locale, err := DefaultProcessorHelper.GetLocale(ctx.Ctx) + if err != nil { + log.Error("Unable to get locale. Error: %v", err) + return + } + + status := &charset.EscapeStatus{} + statuses := make([]*charset.EscapeStatus, len(fileContent)) + for i, line := range fileContent { + statuses[i], fileContent[i] = charset.EscapeControlHTML(line, locale, charset.FileviewContext) + status = status.Or(statuses[i]) + } + + for idx, code := range fileContent { + tr := &html.Node{ + Type: html.ElementNode, + Data: atom.Tr.String(), + } + + lineNum := strconv.Itoa(lineOffset + idx + 1) + + tdLinesnum := &html.Node{ + Type: html.ElementNode, + Data: atom.Td.String(), + Attr: []html.Attribute{ + {Key: "id", Val: "L" + lineNum}, + {Key: "class", Val: "lines-num"}, + }, + } + spanLinesNum := &html.Node{ + Type: html.ElementNode, + Data: atom.Span.String(), + Attr: []html.Attribute{ + {Key: "id", Val: "L" + lineNum}, + {Key: "data-line-number", Val: lineNum}, + }, + } + tdLinesnum.AppendChild(spanLinesNum) + tr.AppendChild(tdLinesnum) + + if status.Escaped { + tdLinesEscape := &html.Node{ + Type: html.ElementNode, + Data: atom.Td.String(), + Attr: []html.Attribute{ + {Key: "class", Val: "lines-escape"}, + }, + } + + if statuses[idx].Escaped { + btnTitle := "" + if statuses[idx].HasInvisible { + btnTitle += locale.TrString("repo.invisible_runes_line") + " " + } + if statuses[idx].HasAmbiguous { + btnTitle += locale.TrString("repo.ambiguous_runes_line") + } + + escapeBtn := &html.Node{ + Type: html.ElementNode, + Data: atom.Button.String(), + Attr: []html.Attribute{ + {Key: "class", Val: "toggle-escape-button btn interact-bg"}, + {Key: "title", Val: btnTitle}, + }, + } + tdLinesEscape.AppendChild(escapeBtn) + } + + tr.AppendChild(tdLinesEscape) + } + + tdCode := &html.Node{ + Type: html.ElementNode, + Data: atom.Td.String(), + Attr: []html.Attribute{ + {Key: "rel", Val: "L" + lineNum}, + {Key: "class", Val: "lines-code chroma"}, + }, + } + codeInner := &html.Node{ + Type: html.ElementNode, + Data: atom.Code.String(), + Attr: []html.Attribute{{Key: "class", Val: "code-inner"}}, + } + codeText := &html.Node{ + Type: html.RawNode, + Data: string(code), + } + codeInner.AppendChild(codeText) + tdCode.AppendChild(codeInner) + tr.AppendChild(tdCode) + + tbody.AppendChild(tr) + } + + table.AppendChild(tbody) + + twrapper := &html.Node{ + Type: html.ElementNode, + Data: atom.Div.String(), + Attr: []html.Attribute{{Key: "class", Val: "ui table"}}, + } + twrapper.AppendChild(table) + + header := &html.Node{ + Type: html.ElementNode, + Data: atom.Div.String(), + Attr: []html.Attribute{{Key: "class", Val: "header"}}, + } + afilepath := &html.Node{ + Type: html.ElementNode, + Data: atom.A.String(), + Attr: []html.Attribute{ + {Key: "href", Val: urlFull}, + {Key: "class", Val: "muted"}, + }, + } + afilepath.AppendChild(&html.Node{ + Type: html.TextNode, + Data: filePath, + }) + header.AppendChild(afilepath) + + psubtitle := &html.Node{ + Type: html.ElementNode, + Data: atom.Span.String(), + Attr: []html.Attribute{{Key: "class", Val: "text small grey"}}, + } + psubtitle.AppendChild(&html.Node{ + Type: html.TextNode, + Data: subTitle + " in ", + }) + psubtitle.AppendChild(createLink(urlFull[m[0]:m[5]], commitSha[0:7], "text black")) + header.AppendChild(psubtitle) + + preview := &html.Node{ + Type: html.ElementNode, + Data: atom.Div.String(), + Attr: []html.Attribute{{Key: "class", Val: "file-preview-box"}}, + } + preview.AppendChild(header) + preview.AppendChild(twrapper) + + // Specialized version of replaceContent, so the parent paragraph element is not destroyed from our div + before := node.Data[:start] + after := node.Data[end:] + node.Data = before + nextSibling := node.NextSibling + node.Parent.InsertBefore(&html.Node{ + Type: html.RawNode, + Data: "

", + }, nextSibling) + node.Parent.InsertBefore(preview, nextSibling) + node.Parent.InsertBefore(&html.Node{ + Type: html.RawNode, + Data: "

" + after, + }, nextSibling) + + node = node.NextSibling + } +} + // emojiShortCodeProcessor for rendering text like :smile: into emoji func emojiShortCodeProcessor(ctx *RenderContext, node *html.Node) { start := 0 diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go index 132955c019..652db13e5e 100644 --- a/modules/markup/html_test.go +++ b/modules/markup/html_test.go @@ -5,6 +5,7 @@ package markup_test import ( "context" + "html/template" "io" "os" "strings" @@ -13,10 +14,12 @@ import ( "code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/modules/emoji" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/highlight" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/translation" "code.gitea.io/gitea/modules/util" "github.com/stretchr/testify/assert" @@ -673,3 +676,57 @@ func TestIssue18471(t *testing.T) { assert.NoError(t, err) assert.Equal(t, "783b039...da951ce", res.String()) } + +func TestRender_FilePreview(t *testing.T) { + setting.AppURL = markup.TestAppURL + markup.Init(&markup.ProcessorHelper{ + GetRepoFileContent: func(ctx context.Context, ownerName, repoName, commitSha, filePath string) ([]template.HTML, error) { + buf := []byte("A\nB\nC\nD\n") + return highlight.PlainText(buf), nil + }, + GetLocale: func(ctx context.Context) (translation.Locale, error) { + return translation.NewLocale("en-US"), nil + }, + }) + + sha := "b6dd6210eaebc915fd5be5579c58cce4da2e2579" + commitFilePreview := util.URLJoin(markup.TestRepoURL, "src", "commit", sha, "path", "to", "file.go") + "#L1-L2" + + test := func(input, expected string) { + buffer, err := markup.RenderString(&markup.RenderContext{ + Ctx: git.DefaultContext, + RelativePath: ".md", + Metas: localMetas, + }, input) + assert.NoError(t, err) + assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) + } + + test( + commitFilePreview, + `

`+ + `
`+ + `
`+ + `path/to/file.go`+ + ``+ + `Lines 1 to 2 in b6dd621`+ + ``+ + `
`+ + `
`+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + `
A`+"\n"+`
B`+"\n"+`
`+ + `
`+ + `
`+ + `

`, + ) +} diff --git a/modules/markup/renderer.go b/modules/markup/renderer.go index 5a7adcc553..37d3fde58c 100644 --- a/modules/markup/renderer.go +++ b/modules/markup/renderer.go @@ -8,6 +8,7 @@ import ( "context" "errors" "fmt" + "html/template" "io" "net/url" "path/filepath" @@ -16,6 +17,7 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/translation" "code.gitea.io/gitea/modules/util" "github.com/yuin/goldmark/ast" @@ -31,6 +33,8 @@ const ( type ProcessorHelper struct { IsUsernameMentionable func(ctx context.Context, username string) bool + GetRepoFileContent func(ctx context.Context, ownerName, repoName, commitSha, filePath string) ([]template.HTML, error) + GetLocale func(ctx context.Context) (translation.Locale, error) ElementDir string // the direction of the elements, eg: "ltr", "rtl", "auto", default to no direction attribute } diff --git a/modules/markup/sanitizer.go b/modules/markup/sanitizer.go index ffc33c3b8e..73e17060a7 100644 --- a/modules/markup/sanitizer.go +++ b/modules/markup/sanitizer.go @@ -120,6 +120,23 @@ func createDefaultPolicy() *bluemonday.Policy { // Allow 'color' and 'background-color' properties for the style attribute on text elements. policy.AllowStyles("color", "background-color").OnElements("span", "p") + // Allow classes for file preview links... + policy.AllowAttrs("class").Matching(regexp.MustCompile("^(lines-num|lines-code chroma)$")).OnElements("td") + policy.AllowAttrs("class").Matching(regexp.MustCompile("^code-inner$")).OnElements("code") + policy.AllowAttrs("class").Matching(regexp.MustCompile("^file-preview-box$")).OnElements("div") + policy.AllowAttrs("class").Matching(regexp.MustCompile("^ui table$")).OnElements("div") + policy.AllowAttrs("class").Matching(regexp.MustCompile("^header$")).OnElements("div") + policy.AllowAttrs("data-line-number").Matching(regexp.MustCompile("^[0-9]+$")).OnElements("span") + policy.AllowAttrs("class").Matching(regexp.MustCompile("^text small grey$")).OnElements("span") + policy.AllowAttrs("rel").Matching(regexp.MustCompile("^L[0-9]+$")).OnElements("td") + policy.AllowAttrs("class").Matching(regexp.MustCompile("^file-preview*")).OnElements("table") + policy.AllowAttrs("class").Matching(regexp.MustCompile("^lines-escape$")).OnElements("td") + policy.AllowAttrs("class").Matching(regexp.MustCompile("^toggle-escape-button btn interact-bg$")).OnElements("button") + policy.AllowAttrs("title").OnElements("button") + policy.AllowAttrs("class").Matching(regexp.MustCompile("^ambiguous-code-point$")).OnElements("span") + policy.AllowAttrs("data-tooltip-content").OnElements("span") + policy.AllowAttrs("class").Matching(regexp.MustCompile("muted|(text black)")).OnElements("a") + // Allow generally safe attributes generalSafeAttrs := []string{ "abbr", "accept", "accept-charset", diff --git a/services/markup/processorhelper.go b/services/markup/processorhelper.go index a4378678a0..134b1b5152 100644 --- a/services/markup/processorhelper.go +++ b/services/markup/processorhelper.go @@ -5,10 +5,21 @@ package markup import ( "context" + "fmt" + "html/template" + "io" + "code.gitea.io/gitea/models/perm/access" + "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/gitrepo" + "code.gitea.io/gitea/modules/highlight" + "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup" + "code.gitea.io/gitea/modules/translation" gitea_context "code.gitea.io/gitea/services/context" + file_service "code.gitea.io/gitea/services/repository/files" ) func ProcessorHelper() *markup.ProcessorHelper { @@ -29,5 +40,75 @@ func ProcessorHelper() *markup.ProcessorHelper { // when using gitea context (web context), use user's visibility and user's permission to check return user.IsUserVisibleToViewer(giteaCtx, mentionedUser, giteaCtx.Doer) }, + GetRepoFileContent: func(ctx context.Context, ownerName, repoName, commitSha, filePath string) ([]template.HTML, error) { + repo, err := repo.GetRepositoryByOwnerAndName(ctx, ownerName, repoName) + if err != nil { + return nil, err + } + + var user *user.User + + giteaCtx, ok := ctx.(*gitea_context.Context) + if ok { + user = giteaCtx.Doer + } + + perms, err := access.GetUserRepoPermission(ctx, repo, user) + if err != nil { + return nil, err + } + if !perms.CanRead(unit.TypeCode) { + return nil, fmt.Errorf("cannot access repository code") + } + + gitRepo, err := gitrepo.OpenRepository(ctx, repo) + if err != nil { + return nil, err + } + + commit, err := gitRepo.GetCommit(commitSha) + if err != nil { + return nil, err + } + + language, err := file_service.TryGetContentLanguage(gitRepo, commitSha, filePath) + if err != nil { + log.Error("Unable to get file language for %-v:%s. Error: %v", repo, filePath, err) + } + + blob, err := commit.GetBlobByPath(filePath) + if err != nil { + return nil, err + } + + dataRc, err := blob.DataAsync() + if err != nil { + return nil, err + } + defer dataRc.Close() + + buf, _ := io.ReadAll(dataRc) + + fileContent, _, err := highlight.File(blob.Name(), language, buf) + if err != nil { + log.Error("highlight.File failed, fallback to plain text: %v", err) + fileContent = highlight.PlainText(buf) + } + + return fileContent, nil + }, + GetLocale: func(ctx context.Context) (translation.Locale, error) { + giteaCtx, ok := ctx.(*gitea_context.Context) + if ok { + return giteaCtx.Locale, nil + } + + giteaBaseCtx, ok := ctx.(*gitea_context.Base) + if ok { + return giteaBaseCtx.Locale, nil + } + + return nil, fmt.Errorf("could not retrieve locale from context") + }, } } diff --git a/web_src/css/index.css b/web_src/css/index.css index ab925a4aa0..8d2780ba42 100644 --- a/web_src/css/index.css +++ b/web_src/css/index.css @@ -30,6 +30,7 @@ @import "./markup/content.css"; @import "./markup/codecopy.css"; @import "./markup/asciicast.css"; +@import "./markup/filepreview.css"; @import "./chroma/base.css"; @import "./codemirror/base.css"; diff --git a/web_src/css/markup/content.css b/web_src/css/markup/content.css index 5eeef078a5..430b4802d6 100644 --- a/web_src/css/markup/content.css +++ b/web_src/css/markup/content.css @@ -451,7 +451,8 @@ text-decoration: inherit; } -.markup pre > code { +.markup pre > code, +.markup .file-preview code { padding: 0; margin: 0; font-size: 100%; diff --git a/web_src/css/markup/filepreview.css b/web_src/css/markup/filepreview.css new file mode 100644 index 0000000000..69360e2a70 --- /dev/null +++ b/web_src/css/markup/filepreview.css @@ -0,0 +1,35 @@ +.markup table.file-preview { + margin-bottom: 0; +} + +.markup table.file-preview td { + padding: 0 10px !important; + border: none !important; +} + +.markup table.file-preview tr { + border-top: none; + background-color: inherit !important; +} + +.markup .file-preview-box { + margin-bottom: 16px; +} + +.markup .file-preview-box .header { + padding: .5rem; + padding-left: 1rem; + border: 1px solid var(--color-secondary); + border-bottom: none; + border-radius: 0.28571429rem 0.28571429rem 0 0; + background: var(--color-box-header); +} + +.markup .file-preview-box .header > a { + display: block; +} + +.markup .file-preview-box .table { + margin-top: 0; + border-radius: 0 0 0.28571429rem 0.28571429rem; +} diff --git a/web_src/css/repo/linebutton.css b/web_src/css/repo/linebutton.css index 1e5e51eac5..7780d6a263 100644 --- a/web_src/css/repo/linebutton.css +++ b/web_src/css/repo/linebutton.css @@ -1,4 +1,5 @@ -.code-view .lines-num:hover { +.code-view .lines-num:hover, +.file-preview .lines-num:hover { color: var(--color-text-dark) !important; } diff --git a/web_src/js/features/repo-unicode-escape.js b/web_src/js/features/repo-unicode-escape.js index d878532001..9f0c745223 100644 --- a/web_src/js/features/repo-unicode-escape.js +++ b/web_src/js/features/repo-unicode-escape.js @@ -7,8 +7,8 @@ export function initUnicodeEscapeButton() { e.preventDefault(); - const fileContent = btn.closest('.file-content, .non-diff-file-content'); - const fileView = fileContent?.querySelectorAll('.file-code, .file-view'); + const fileContent = btn.closest('.file-content, .non-diff-file-content, .file-preview-box'); + const fileView = fileContent?.querySelectorAll('.file-code, .file-view, .file-preview'); if (btn.matches('.escape-button')) { for (const el of fileView) el.classList.add('unicode-escaped'); hideElem(btn); From 781a37fbe18c223763f51968862f1c8f61e7e260 Mon Sep 17 00:00:00 2001 From: Mai-Lapyst Date: Fri, 15 Mar 2024 23:49:13 +0100 Subject: [PATCH 0002/3435] Check error in GetRepoFileContent for io.ReadAll --- services/markup/processorhelper.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/services/markup/processorhelper.go b/services/markup/processorhelper.go index 134b1b5152..ab6a66b367 100644 --- a/services/markup/processorhelper.go +++ b/services/markup/processorhelper.go @@ -87,7 +87,10 @@ func ProcessorHelper() *markup.ProcessorHelper { } defer dataRc.Close() - buf, _ := io.ReadAll(dataRc) + buf, err := io.ReadAll(dataRc) + if err != nil { + log.Error("failed to completly read blob for %-v:%s. Error: %v", repo, filePath, err) + } fileContent, _, err := highlight.File(blob.Name(), language, buf) if err != nil { From 8309f008c2721e313e1949ce42ed410e844c16e7 Mon Sep 17 00:00:00 2001 From: Mai-Lapyst Date: Fri, 15 Mar 2024 23:52:38 +0100 Subject: [PATCH 0003/3435] Fix some code issues --- modules/markup/html.go | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/modules/markup/html.go b/modules/markup/html.go index 2501f8062d..631c93fc36 100644 --- a/modules/markup/html.go +++ b/modules/markup/html.go @@ -10,6 +10,7 @@ import ( "path" "path/filepath" "regexp" + "slices" "strconv" "strings" "sync" @@ -64,7 +65,7 @@ var ( validLinksPattern = regexp.MustCompile(`^[a-z][\w-]+://`) // filePreviewPattern matches "http://domain/org/repo/src/commit/COMMIT/filepath#L1-L2" - filePreviewPattern = regexp.MustCompile(`https?://((?:\S+/){3})src/commit/([0-9a-f]{7,64})/(\S+)#(L\d+(?:-L\d+)?)`) + filePreviewPattern = regexp.MustCompile(`https?://((?:\S+/){4,5})src/commit/([0-9a-f]{7,64})/(\S+)#(L\d+(?:-L\d+)?)`) // While this email regex is definitely not perfect and I'm sure you can come up // with edge cases, it is still accepted by the CommonMark specification, as @@ -1075,11 +1076,9 @@ func filePreviewPatternProcessor(ctx *RenderContext, node *html.Node) { return } - // Ensure that every group (m[0]...m[9]) has a match - for i := 0; i < 10; i++ { - if m[i] == -1 { - return - } + // Ensure that every group has a match + if slices.Contains(m, -1) { + return } urlFull := node.Data[m[0]:m[1]] @@ -1089,8 +1088,7 @@ func filePreviewPatternProcessor(ctx *RenderContext, node *html.Node) { return } - projPath := node.Data[m[2]:m[3]] - projPath = strings.TrimSuffix(projPath, "/") + projPath := strings.TrimSuffix(node.Data[m[2]:m[3]], "/") commitSha := node.Data[m[4]:m[5]] filePath := node.Data[m[6]:m[7]] From fae8d9f70d31704af91cbf37bcefcc4772830695 Mon Sep 17 00:00:00 2001 From: Mai-Lapyst Date: Fri, 15 Mar 2024 23:54:07 +0100 Subject: [PATCH 0004/3435] Accept at minimum 4 chars for the commit sha --- modules/markup/html.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/markup/html.go b/modules/markup/html.go index 631c93fc36..7fe7e5fc4a 100644 --- a/modules/markup/html.go +++ b/modules/markup/html.go @@ -65,7 +65,7 @@ var ( validLinksPattern = regexp.MustCompile(`^[a-z][\w-]+://`) // filePreviewPattern matches "http://domain/org/repo/src/commit/COMMIT/filepath#L1-L2" - filePreviewPattern = regexp.MustCompile(`https?://((?:\S+/){4,5})src/commit/([0-9a-f]{7,64})/(\S+)#(L\d+(?:-L\d+)?)`) + filePreviewPattern = regexp.MustCompile(`https?://((?:\S+/){4,5})src/commit/([0-9a-f]{4,64})/(\S+)#(L\d+(?:-L\d+)?)`) // While this email regex is definitely not perfect and I'm sure you can come up // with edge cases, it is still accepted by the CommonMark specification, as From 6721cba75b4997448b618a4b00ef25f142924de0 Mon Sep 17 00:00:00 2001 From: Mai-Lapyst Date: Sat, 16 Mar 2024 00:35:56 +0100 Subject: [PATCH 0005/3435] Fix filePreviewPattern --- modules/markup/html.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/markup/html.go b/modules/markup/html.go index 7fe7e5fc4a..d0d2530735 100644 --- a/modules/markup/html.go +++ b/modules/markup/html.go @@ -65,7 +65,7 @@ var ( validLinksPattern = regexp.MustCompile(`^[a-z][\w-]+://`) // filePreviewPattern matches "http://domain/org/repo/src/commit/COMMIT/filepath#L1-L2" - filePreviewPattern = regexp.MustCompile(`https?://((?:\S+/){4,5})src/commit/([0-9a-f]{4,64})/(\S+)#(L\d+(?:-L\d+)?)`) + filePreviewPattern = regexp.MustCompile(`https?://((?:\S+/){3})src/commit/([0-9a-f]{4,64})/(\S+)#(L\d+(?:-L\d+)?)`) // While this email regex is definitely not perfect and I'm sure you can come up // with edge cases, it is still accepted by the CommonMark specification, as From 562e5cdf324597882b7e6971be1b9a148bbc7839 Mon Sep 17 00:00:00 2001 From: Mai-Lapyst Date: Sat, 16 Mar 2024 01:17:04 +0100 Subject: [PATCH 0006/3435] Get locales directly from context like the other code; add translations for subtitle --- modules/markup/html.go | 34 ++++++++++++++++++------------ modules/markup/html_test.go | 4 ---- modules/markup/renderer.go | 2 -- options/locale/locale_en-US.ini | 4 ++++ services/markup/processorhelper.go | 14 ------------ 5 files changed, 25 insertions(+), 33 deletions(-) diff --git a/modules/markup/html.go b/modules/markup/html.go index d0d2530735..1e83dad701 100644 --- a/modules/markup/html.go +++ b/modules/markup/html.go @@ -5,6 +5,7 @@ package markup import ( "bytes" + "html/template" "io" "net/url" "path" @@ -1065,7 +1066,7 @@ func filePreviewPatternProcessor(ctx *RenderContext, node *html.Node) { if ctx.Metas == nil { return } - if DefaultProcessorHelper.GetRepoFileContent == nil || DefaultProcessorHelper.GetLocale == nil { + if DefaultProcessorHelper.GetRepoFileContent == nil { return } @@ -1119,9 +1120,17 @@ func filePreviewPatternProcessor(ctx *RenderContext, node *html.Node) { lineSpecs := strings.Split(hash, "-") lineCount := len(fileContent) - var subTitle string + commitLinkBuffer := new(bytes.Buffer) + html.Render(commitLinkBuffer, createLink(node.Data[m[0]:m[5]], commitSha[0:7], "text black")) + + var subTitle template.HTML var lineOffset int + locale, ok := ctx.Ctx.Value(translation.ContextKey).(translation.Locale) + if !ok { + locale = translation.NewLocale("en-US") + } + if len(lineSpecs) == 1 { line, _ := strconv.Atoi(strings.TrimPrefix(lineSpecs[0], "L")) if line < 1 || line > lineCount { @@ -1129,7 +1138,10 @@ func filePreviewPatternProcessor(ctx *RenderContext, node *html.Node) { } fileContent = fileContent[line-1 : line] - subTitle = "Line " + strconv.Itoa(line) + subTitle = locale.Tr( + "markup.filepreview.line", line, + template.HTML(commitLinkBuffer.String()), + ) lineOffset = line - 1 } else { @@ -1141,7 +1153,10 @@ func filePreviewPatternProcessor(ctx *RenderContext, node *html.Node) { } fileContent = fileContent[startLine-1 : endLine] - subTitle = "Lines " + strconv.Itoa(startLine) + " to " + strconv.Itoa(endLine) + subTitle = locale.Tr( + "markup.filepreview.lines", startLine, endLine, + template.HTML(commitLinkBuffer.String()), + ) lineOffset = startLine - 1 } @@ -1156,12 +1171,6 @@ func filePreviewPatternProcessor(ctx *RenderContext, node *html.Node) { Data: atom.Tbody.String(), } - locale, err := DefaultProcessorHelper.GetLocale(ctx.Ctx) - if err != nil { - log.Error("Unable to get locale. Error: %v", err) - return - } - status := &charset.EscapeStatus{} statuses := make([]*charset.EscapeStatus, len(fileContent)) for i, line := range fileContent { @@ -1286,10 +1295,9 @@ func filePreviewPatternProcessor(ctx *RenderContext, node *html.Node) { Attr: []html.Attribute{{Key: "class", Val: "text small grey"}}, } psubtitle.AppendChild(&html.Node{ - Type: html.TextNode, - Data: subTitle + " in ", + Type: html.RawNode, + Data: string(subTitle), }) - psubtitle.AppendChild(createLink(urlFull[m[0]:m[5]], commitSha[0:7], "text black")) header.AppendChild(psubtitle) preview := &html.Node{ diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go index 652db13e5e..c43f006266 100644 --- a/modules/markup/html_test.go +++ b/modules/markup/html_test.go @@ -19,7 +19,6 @@ import ( "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/translation" "code.gitea.io/gitea/modules/util" "github.com/stretchr/testify/assert" @@ -684,9 +683,6 @@ func TestRender_FilePreview(t *testing.T) { buf := []byte("A\nB\nC\nD\n") return highlight.PlainText(buf), nil }, - GetLocale: func(ctx context.Context) (translation.Locale, error) { - return translation.NewLocale("en-US"), nil - }, }) sha := "b6dd6210eaebc915fd5be5579c58cce4da2e2579" diff --git a/modules/markup/renderer.go b/modules/markup/renderer.go index 37d3fde58c..b6d742e5ce 100644 --- a/modules/markup/renderer.go +++ b/modules/markup/renderer.go @@ -17,7 +17,6 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/translation" "code.gitea.io/gitea/modules/util" "github.com/yuin/goldmark/ast" @@ -34,7 +33,6 @@ const ( type ProcessorHelper struct { IsUsernameMentionable func(ctx context.Context, username string) bool GetRepoFileContent func(ctx context.Context, ownerName, repoName, commitSha, filePath string) ([]template.HTML, error) - GetLocale func(ctx context.Context) (translation.Locale, error) ElementDir string // the direction of the elements, eg: "ltr", "rtl", "auto", default to no direction attribute } diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index a7f4de48a8..ebc8db24ca 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -3707,3 +3707,7 @@ normal_file = Normal file executable_file = Executable file symbolic_link = Symbolic link submodule = Submodule + +[markup] +filepreview.line = Line %[1]d in %[3]s +filepreview.lines = Lines %[1]d to %[2]d in %[3]s diff --git a/services/markup/processorhelper.go b/services/markup/processorhelper.go index ab6a66b367..df96f25ce9 100644 --- a/services/markup/processorhelper.go +++ b/services/markup/processorhelper.go @@ -17,7 +17,6 @@ import ( "code.gitea.io/gitea/modules/highlight" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup" - "code.gitea.io/gitea/modules/translation" gitea_context "code.gitea.io/gitea/services/context" file_service "code.gitea.io/gitea/services/repository/files" ) @@ -100,18 +99,5 @@ func ProcessorHelper() *markup.ProcessorHelper { return fileContent, nil }, - GetLocale: func(ctx context.Context) (translation.Locale, error) { - giteaCtx, ok := ctx.(*gitea_context.Context) - if ok { - return giteaCtx.Locale, nil - } - - giteaBaseCtx, ok := ctx.(*gitea_context.Base) - if ok { - return giteaBaseCtx.Locale, nil - } - - return nil, fmt.Errorf("could not retrieve locale from context") - }, } } From d789d33229b3998bb33f1505d122504c8039f23d Mon Sep 17 00:00:00 2001 From: Mai-Lapyst Date: Sat, 16 Mar 2024 08:09:49 +0100 Subject: [PATCH 0007/3435] Split filePreviewPatternProcessor into a new type FilePreview and some functions to make code more maintainable --- modules/markup/file_preview.go | 269 +++++++++++++++++++++++++++++++++ modules/markup/html.go | 245 +----------------------------- 2 files changed, 276 insertions(+), 238 deletions(-) create mode 100644 modules/markup/file_preview.go diff --git a/modules/markup/file_preview.go b/modules/markup/file_preview.go new file mode 100644 index 0000000000..646bf83630 --- /dev/null +++ b/modules/markup/file_preview.go @@ -0,0 +1,269 @@ +package markup + +import ( + "bytes" + "html/template" + "regexp" + "slices" + "strconv" + "strings" + + "code.gitea.io/gitea/modules/charset" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/translation" + "golang.org/x/net/html" + "golang.org/x/net/html/atom" +) + +var ( + // filePreviewPattern matches "http://domain/org/repo/src/commit/COMMIT/filepath#L1-L2" + filePreviewPattern = regexp.MustCompile(`https?://((?:\S+/){3})src/commit/([0-9a-f]{4,64})/(\S+)#(L\d+(?:-L\d+)?)`) +) + +type FilePreview struct { + fileContent []template.HTML + subTitle template.HTML + lineOffset int + urlFull string + filePath string + start int + end int +} + +func NewFilePreview(ctx *RenderContext, node *html.Node, locale translation.Locale) *FilePreview { + preview := &FilePreview{} + + m := filePreviewPattern.FindStringSubmatchIndex(node.Data) + if m == nil { + return nil + } + + // Ensure that every group has a match + if slices.Contains(m, -1) { + return nil + } + + preview.urlFull = node.Data[m[0]:m[1]] + + // Ensure that we only use links to local repositories + if !strings.HasPrefix(preview.urlFull, setting.AppURL+setting.AppSubURL) { + return nil + } + + projPath := strings.TrimSuffix(node.Data[m[2]:m[3]], "/") + + commitSha := node.Data[m[4]:m[5]] + preview.filePath = node.Data[m[6]:m[7]] + hash := node.Data[m[8]:m[9]] + + preview.start = m[0] + preview.end = m[1] + + // If url ends in '.', it's very likely that it is not part of the + // actual url but used to finish a sentence. + if strings.HasSuffix(preview.urlFull, ".") { + preview.end-- + preview.urlFull = preview.urlFull[:len(preview.urlFull)-1] + hash = hash[:len(hash)-1] + } + + projPathSegments := strings.Split(projPath, "/") + fileContent, err := DefaultProcessorHelper.GetRepoFileContent( + ctx.Ctx, + projPathSegments[len(projPathSegments)-2], + projPathSegments[len(projPathSegments)-1], + commitSha, preview.filePath, + ) + if err != nil { + return nil + } + + lineSpecs := strings.Split(hash, "-") + lineCount := len(fileContent) + + commitLinkBuffer := new(bytes.Buffer) + html.Render(commitLinkBuffer, createLink(node.Data[m[0]:m[5]], commitSha[0:7], "text black")) + + if len(lineSpecs) == 1 { + line, _ := strconv.Atoi(strings.TrimPrefix(lineSpecs[0], "L")) + if line < 1 || line > lineCount { + return nil + } + + preview.fileContent = fileContent[line-1 : line] + preview.subTitle = locale.Tr( + "markup.filepreview.line", line, + template.HTML(commitLinkBuffer.String()), + ) + + preview.lineOffset = line - 1 + } else { + startLine, _ := strconv.Atoi(strings.TrimPrefix(lineSpecs[0], "L")) + endLine, _ := strconv.Atoi(strings.TrimPrefix(lineSpecs[1], "L")) + + if startLine < 1 || endLine < 1 || startLine > lineCount || endLine > lineCount || endLine < startLine { + return nil + } + + preview.fileContent = fileContent[startLine-1 : endLine] + preview.subTitle = locale.Tr( + "markup.filepreview.lines", startLine, endLine, + template.HTML(commitLinkBuffer.String()), + ) + + preview.lineOffset = startLine - 1 + } + + return preview +} + +func (p *FilePreview) CreateHtml(locale translation.Locale) *html.Node { + table := &html.Node{ + Type: html.ElementNode, + Data: atom.Table.String(), + Attr: []html.Attribute{{Key: "class", Val: "file-preview"}}, + } + tbody := &html.Node{ + Type: html.ElementNode, + Data: atom.Tbody.String(), + } + + status := &charset.EscapeStatus{} + statuses := make([]*charset.EscapeStatus, len(p.fileContent)) + for i, line := range p.fileContent { + statuses[i], p.fileContent[i] = charset.EscapeControlHTML(line, locale, charset.FileviewContext) + status = status.Or(statuses[i]) + } + + for idx, code := range p.fileContent { + tr := &html.Node{ + Type: html.ElementNode, + Data: atom.Tr.String(), + } + + lineNum := strconv.Itoa(p.lineOffset + idx + 1) + + tdLinesnum := &html.Node{ + Type: html.ElementNode, + Data: atom.Td.String(), + Attr: []html.Attribute{ + {Key: "id", Val: "L" + lineNum}, + {Key: "class", Val: "lines-num"}, + }, + } + spanLinesNum := &html.Node{ + Type: html.ElementNode, + Data: atom.Span.String(), + Attr: []html.Attribute{ + {Key: "id", Val: "L" + lineNum}, + {Key: "data-line-number", Val: lineNum}, + }, + } + tdLinesnum.AppendChild(spanLinesNum) + tr.AppendChild(tdLinesnum) + + if status.Escaped { + tdLinesEscape := &html.Node{ + Type: html.ElementNode, + Data: atom.Td.String(), + Attr: []html.Attribute{ + {Key: "class", Val: "lines-escape"}, + }, + } + + if statuses[idx].Escaped { + btnTitle := "" + if statuses[idx].HasInvisible { + btnTitle += locale.TrString("repo.invisible_runes_line") + " " + } + if statuses[idx].HasAmbiguous { + btnTitle += locale.TrString("repo.ambiguous_runes_line") + } + + escapeBtn := &html.Node{ + Type: html.ElementNode, + Data: atom.Button.String(), + Attr: []html.Attribute{ + {Key: "class", Val: "toggle-escape-button btn interact-bg"}, + {Key: "title", Val: btnTitle}, + }, + } + tdLinesEscape.AppendChild(escapeBtn) + } + + tr.AppendChild(tdLinesEscape) + } + + tdCode := &html.Node{ + Type: html.ElementNode, + Data: atom.Td.String(), + Attr: []html.Attribute{ + {Key: "rel", Val: "L" + lineNum}, + {Key: "class", Val: "lines-code chroma"}, + }, + } + codeInner := &html.Node{ + Type: html.ElementNode, + Data: atom.Code.String(), + Attr: []html.Attribute{{Key: "class", Val: "code-inner"}}, + } + codeText := &html.Node{ + Type: html.RawNode, + Data: string(code), + } + codeInner.AppendChild(codeText) + tdCode.AppendChild(codeInner) + tr.AppendChild(tdCode) + + tbody.AppendChild(tr) + } + + table.AppendChild(tbody) + + twrapper := &html.Node{ + Type: html.ElementNode, + Data: atom.Div.String(), + Attr: []html.Attribute{{Key: "class", Val: "ui table"}}, + } + twrapper.AppendChild(table) + + header := &html.Node{ + Type: html.ElementNode, + Data: atom.Div.String(), + Attr: []html.Attribute{{Key: "class", Val: "header"}}, + } + afilepath := &html.Node{ + Type: html.ElementNode, + Data: atom.A.String(), + Attr: []html.Attribute{ + {Key: "href", Val: p.urlFull}, + {Key: "class", Val: "muted"}, + }, + } + afilepath.AppendChild(&html.Node{ + Type: html.TextNode, + Data: p.filePath, + }) + header.AppendChild(afilepath) + + psubtitle := &html.Node{ + Type: html.ElementNode, + Data: atom.Span.String(), + Attr: []html.Attribute{{Key: "class", Val: "text small grey"}}, + } + psubtitle.AppendChild(&html.Node{ + Type: html.RawNode, + Data: string(p.subTitle), + }) + header.AppendChild(psubtitle) + + preview_node := &html.Node{ + Type: html.ElementNode, + Data: atom.Div.String(), + Attr: []html.Attribute{{Key: "class", Val: "file-preview-box"}}, + } + preview_node.AppendChild(header) + preview_node.AppendChild(twrapper) + + return preview_node +} diff --git a/modules/markup/html.go b/modules/markup/html.go index 1e83dad701..2e38c05f58 100644 --- a/modules/markup/html.go +++ b/modules/markup/html.go @@ -5,19 +5,15 @@ package markup import ( "bytes" - "html/template" "io" "net/url" "path" "path/filepath" "regexp" - "slices" - "strconv" "strings" "sync" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/charset" "code.gitea.io/gitea/modules/emoji" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" @@ -65,9 +61,6 @@ var ( validLinksPattern = regexp.MustCompile(`^[a-z][\w-]+://`) - // filePreviewPattern matches "http://domain/org/repo/src/commit/COMMIT/filepath#L1-L2" - filePreviewPattern = regexp.MustCompile(`https?://((?:\S+/){3})src/commit/([0-9a-f]{4,64})/(\S+)#(L\d+(?:-L\d+)?)`) - // While this email regex is definitely not perfect and I'm sure you can come up // with edge cases, it is still accepted by the CommonMark specification, as // well as the HTML5 spec: @@ -1072,252 +1065,28 @@ func filePreviewPatternProcessor(ctx *RenderContext, node *html.Node) { next := node.NextSibling for node != nil && node != next { - m := filePreviewPattern.FindStringSubmatchIndex(node.Data) - if m == nil { - return - } - - // Ensure that every group has a match - if slices.Contains(m, -1) { - return - } - - urlFull := node.Data[m[0]:m[1]] - - // Ensure that we only use links to local repositories - if !strings.HasPrefix(urlFull, setting.AppURL+setting.AppSubURL) { - return - } - - projPath := strings.TrimSuffix(node.Data[m[2]:m[3]], "/") - - commitSha := node.Data[m[4]:m[5]] - filePath := node.Data[m[6]:m[7]] - hash := node.Data[m[8]:m[9]] - - start := m[0] - end := m[1] - - // If url ends in '.', it's very likely that it is not part of the - // actual url but used to finish a sentence. - if strings.HasSuffix(urlFull, ".") { - end-- - urlFull = urlFull[:len(urlFull)-1] - hash = hash[:len(hash)-1] - } - - projPathSegments := strings.Split(projPath, "/") - fileContent, err := DefaultProcessorHelper.GetRepoFileContent( - ctx.Ctx, - projPathSegments[len(projPathSegments)-2], - projPathSegments[len(projPathSegments)-1], - commitSha, filePath, - ) - if err != nil { - return - } - - lineSpecs := strings.Split(hash, "-") - lineCount := len(fileContent) - - commitLinkBuffer := new(bytes.Buffer) - html.Render(commitLinkBuffer, createLink(node.Data[m[0]:m[5]], commitSha[0:7], "text black")) - - var subTitle template.HTML - var lineOffset int - locale, ok := ctx.Ctx.Value(translation.ContextKey).(translation.Locale) if !ok { locale = translation.NewLocale("en-US") } - if len(lineSpecs) == 1 { - line, _ := strconv.Atoi(strings.TrimPrefix(lineSpecs[0], "L")) - if line < 1 || line > lineCount { - return - } - - fileContent = fileContent[line-1 : line] - subTitle = locale.Tr( - "markup.filepreview.line", line, - template.HTML(commitLinkBuffer.String()), - ) - - lineOffset = line - 1 - } else { - startLine, _ := strconv.Atoi(strings.TrimPrefix(lineSpecs[0], "L")) - endLine, _ := strconv.Atoi(strings.TrimPrefix(lineSpecs[1], "L")) - - if startLine < 1 || endLine < 1 || startLine > lineCount || endLine > lineCount || endLine < startLine { - return - } - - fileContent = fileContent[startLine-1 : endLine] - subTitle = locale.Tr( - "markup.filepreview.lines", startLine, endLine, - template.HTML(commitLinkBuffer.String()), - ) - - lineOffset = startLine - 1 + preview := NewFilePreview(ctx, node, locale) + if preview == nil { + return } - table := &html.Node{ - Type: html.ElementNode, - Data: atom.Table.String(), - Attr: []html.Attribute{{Key: "class", Val: "file-preview"}}, - } - tbody := &html.Node{ - Type: html.ElementNode, - Data: atom.Tbody.String(), - } - - status := &charset.EscapeStatus{} - statuses := make([]*charset.EscapeStatus, len(fileContent)) - for i, line := range fileContent { - statuses[i], fileContent[i] = charset.EscapeControlHTML(line, locale, charset.FileviewContext) - status = status.Or(statuses[i]) - } - - for idx, code := range fileContent { - tr := &html.Node{ - Type: html.ElementNode, - Data: atom.Tr.String(), - } - - lineNum := strconv.Itoa(lineOffset + idx + 1) - - tdLinesnum := &html.Node{ - Type: html.ElementNode, - Data: atom.Td.String(), - Attr: []html.Attribute{ - {Key: "id", Val: "L" + lineNum}, - {Key: "class", Val: "lines-num"}, - }, - } - spanLinesNum := &html.Node{ - Type: html.ElementNode, - Data: atom.Span.String(), - Attr: []html.Attribute{ - {Key: "id", Val: "L" + lineNum}, - {Key: "data-line-number", Val: lineNum}, - }, - } - tdLinesnum.AppendChild(spanLinesNum) - tr.AppendChild(tdLinesnum) - - if status.Escaped { - tdLinesEscape := &html.Node{ - Type: html.ElementNode, - Data: atom.Td.String(), - Attr: []html.Attribute{ - {Key: "class", Val: "lines-escape"}, - }, - } - - if statuses[idx].Escaped { - btnTitle := "" - if statuses[idx].HasInvisible { - btnTitle += locale.TrString("repo.invisible_runes_line") + " " - } - if statuses[idx].HasAmbiguous { - btnTitle += locale.TrString("repo.ambiguous_runes_line") - } - - escapeBtn := &html.Node{ - Type: html.ElementNode, - Data: atom.Button.String(), - Attr: []html.Attribute{ - {Key: "class", Val: "toggle-escape-button btn interact-bg"}, - {Key: "title", Val: btnTitle}, - }, - } - tdLinesEscape.AppendChild(escapeBtn) - } - - tr.AppendChild(tdLinesEscape) - } - - tdCode := &html.Node{ - Type: html.ElementNode, - Data: atom.Td.String(), - Attr: []html.Attribute{ - {Key: "rel", Val: "L" + lineNum}, - {Key: "class", Val: "lines-code chroma"}, - }, - } - codeInner := &html.Node{ - Type: html.ElementNode, - Data: atom.Code.String(), - Attr: []html.Attribute{{Key: "class", Val: "code-inner"}}, - } - codeText := &html.Node{ - Type: html.RawNode, - Data: string(code), - } - codeInner.AppendChild(codeText) - tdCode.AppendChild(codeInner) - tr.AppendChild(tdCode) - - tbody.AppendChild(tr) - } - - table.AppendChild(tbody) - - twrapper := &html.Node{ - Type: html.ElementNode, - Data: atom.Div.String(), - Attr: []html.Attribute{{Key: "class", Val: "ui table"}}, - } - twrapper.AppendChild(table) - - header := &html.Node{ - Type: html.ElementNode, - Data: atom.Div.String(), - Attr: []html.Attribute{{Key: "class", Val: "header"}}, - } - afilepath := &html.Node{ - Type: html.ElementNode, - Data: atom.A.String(), - Attr: []html.Attribute{ - {Key: "href", Val: urlFull}, - {Key: "class", Val: "muted"}, - }, - } - afilepath.AppendChild(&html.Node{ - Type: html.TextNode, - Data: filePath, - }) - header.AppendChild(afilepath) - - psubtitle := &html.Node{ - Type: html.ElementNode, - Data: atom.Span.String(), - Attr: []html.Attribute{{Key: "class", Val: "text small grey"}}, - } - psubtitle.AppendChild(&html.Node{ - Type: html.RawNode, - Data: string(subTitle), - }) - header.AppendChild(psubtitle) - - preview := &html.Node{ - Type: html.ElementNode, - Data: atom.Div.String(), - Attr: []html.Attribute{{Key: "class", Val: "file-preview-box"}}, - } - preview.AppendChild(header) - preview.AppendChild(twrapper) + preview_node := preview.CreateHtml(locale) // Specialized version of replaceContent, so the parent paragraph element is not destroyed from our div - before := node.Data[:start] - after := node.Data[end:] + before := node.Data[:preview.start] + after := node.Data[preview.end:] node.Data = before nextSibling := node.NextSibling node.Parent.InsertBefore(&html.Node{ Type: html.RawNode, Data: "

", }, nextSibling) - node.Parent.InsertBefore(preview, nextSibling) + node.Parent.InsertBefore(preview_node, nextSibling) node.Parent.InsertBefore(&html.Node{ Type: html.RawNode, Data: "

" + after, From 8218e80bfc3a1f9ba02ce60f1acafdc0e57c5ae0 Mon Sep 17 00:00:00 2001 From: Mai-Lapyst Date: Sat, 16 Mar 2024 08:18:47 +0100 Subject: [PATCH 0008/3435] Fix linting issues --- modules/markup/file_preview.go | 22 ++++++++++++---------- modules/markup/html.go | 4 ++-- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/modules/markup/file_preview.go b/modules/markup/file_preview.go index 646bf83630..be788aae4b 100644 --- a/modules/markup/file_preview.go +++ b/modules/markup/file_preview.go @@ -9,16 +9,15 @@ import ( "strings" "code.gitea.io/gitea/modules/charset" + "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/translation" "golang.org/x/net/html" "golang.org/x/net/html/atom" ) -var ( - // filePreviewPattern matches "http://domain/org/repo/src/commit/COMMIT/filepath#L1-L2" - filePreviewPattern = regexp.MustCompile(`https?://((?:\S+/){3})src/commit/([0-9a-f]{4,64})/(\S+)#(L\d+(?:-L\d+)?)`) -) +// filePreviewPattern matches "http://domain/org/repo/src/commit/COMMIT/filepath#L1-L2" +var filePreviewPattern = regexp.MustCompile(`https?://((?:\S+/){3})src/commit/([0-9a-f]{4,64})/(\S+)#(L\d+(?:-L\d+)?)`) type FilePreview struct { fileContent []template.HTML @@ -82,7 +81,10 @@ func NewFilePreview(ctx *RenderContext, node *html.Node, locale translation.Loca lineCount := len(fileContent) commitLinkBuffer := new(bytes.Buffer) - html.Render(commitLinkBuffer, createLink(node.Data[m[0]:m[5]], commitSha[0:7], "text black")) + err = html.Render(commitLinkBuffer, createLink(node.Data[m[0]:m[5]], commitSha[0:7], "text black")) + if err != nil { + log.Error("failed to render commitLink: %v", err) + } if len(lineSpecs) == 1 { line, _ := strconv.Atoi(strings.TrimPrefix(lineSpecs[0], "L")) @@ -117,7 +119,7 @@ func NewFilePreview(ctx *RenderContext, node *html.Node, locale translation.Loca return preview } -func (p *FilePreview) CreateHtml(locale translation.Locale) *html.Node { +func (p *FilePreview) CreateHTML(locale translation.Locale) *html.Node { table := &html.Node{ Type: html.ElementNode, Data: atom.Table.String(), @@ -257,13 +259,13 @@ func (p *FilePreview) CreateHtml(locale translation.Locale) *html.Node { }) header.AppendChild(psubtitle) - preview_node := &html.Node{ + node := &html.Node{ Type: html.ElementNode, Data: atom.Div.String(), Attr: []html.Attribute{{Key: "class", Val: "file-preview-box"}}, } - preview_node.AppendChild(header) - preview_node.AppendChild(twrapper) + node.AppendChild(header) + node.AppendChild(twrapper) - return preview_node + return node } diff --git a/modules/markup/html.go b/modules/markup/html.go index 2e38c05f58..9a04e02fb8 100644 --- a/modules/markup/html.go +++ b/modules/markup/html.go @@ -1075,7 +1075,7 @@ func filePreviewPatternProcessor(ctx *RenderContext, node *html.Node) { return } - preview_node := preview.CreateHtml(locale) + previewNode := preview.CreateHTML(locale) // Specialized version of replaceContent, so the parent paragraph element is not destroyed from our div before := node.Data[:preview.start] @@ -1086,7 +1086,7 @@ func filePreviewPatternProcessor(ctx *RenderContext, node *html.Node) { Type: html.RawNode, Data: "

", }, nextSibling) - node.Parent.InsertBefore(preview_node, nextSibling) + node.Parent.InsertBefore(previewNode, nextSibling) node.Parent.InsertBefore(&html.Node{ Type: html.RawNode, Data: "

" + after, From 10bca456a9140519e95559aa7bac2221e1156c5b Mon Sep 17 00:00:00 2001 From: Mai-Lapyst Date: Mon, 18 Mar 2024 06:19:27 +0100 Subject: [PATCH 0009/3435] Remove `rel` and `id` attributes that only add the linenumber to elements --- modules/markup/file_preview.go | 3 --- modules/markup/sanitizer.go | 1 - 2 files changed, 4 deletions(-) diff --git a/modules/markup/file_preview.go b/modules/markup/file_preview.go index be788aae4b..167bbd1997 100644 --- a/modules/markup/file_preview.go +++ b/modules/markup/file_preview.go @@ -149,7 +149,6 @@ func (p *FilePreview) CreateHTML(locale translation.Locale) *html.Node { Type: html.ElementNode, Data: atom.Td.String(), Attr: []html.Attribute{ - {Key: "id", Val: "L" + lineNum}, {Key: "class", Val: "lines-num"}, }, } @@ -157,7 +156,6 @@ func (p *FilePreview) CreateHTML(locale translation.Locale) *html.Node { Type: html.ElementNode, Data: atom.Span.String(), Attr: []html.Attribute{ - {Key: "id", Val: "L" + lineNum}, {Key: "data-line-number", Val: lineNum}, }, } @@ -200,7 +198,6 @@ func (p *FilePreview) CreateHTML(locale translation.Locale) *html.Node { Type: html.ElementNode, Data: atom.Td.String(), Attr: []html.Attribute{ - {Key: "rel", Val: "L" + lineNum}, {Key: "class", Val: "lines-code chroma"}, }, } diff --git a/modules/markup/sanitizer.go b/modules/markup/sanitizer.go index 73e17060a7..c37027b843 100644 --- a/modules/markup/sanitizer.go +++ b/modules/markup/sanitizer.go @@ -128,7 +128,6 @@ func createDefaultPolicy() *bluemonday.Policy { policy.AllowAttrs("class").Matching(regexp.MustCompile("^header$")).OnElements("div") policy.AllowAttrs("data-line-number").Matching(regexp.MustCompile("^[0-9]+$")).OnElements("span") policy.AllowAttrs("class").Matching(regexp.MustCompile("^text small grey$")).OnElements("span") - policy.AllowAttrs("rel").Matching(regexp.MustCompile("^L[0-9]+$")).OnElements("td") policy.AllowAttrs("class").Matching(regexp.MustCompile("^file-preview*")).OnElements("table") policy.AllowAttrs("class").Matching(regexp.MustCompile("^lines-escape$")).OnElements("td") policy.AllowAttrs("class").Matching(regexp.MustCompile("^toggle-escape-button btn interact-bg$")).OnElements("button") From db6f6281fcf568ae8e35330a4a93c9be1cb46efd Mon Sep 17 00:00:00 2001 From: Mai-Lapyst Date: Mon, 18 Mar 2024 06:21:35 +0100 Subject: [PATCH 0010/3435] Add copyright & license header to file_preview.go --- modules/markup/file_preview.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/markup/file_preview.go b/modules/markup/file_preview.go index 167bbd1997..377809529d 100644 --- a/modules/markup/file_preview.go +++ b/modules/markup/file_preview.go @@ -1,3 +1,6 @@ +// Copyright The Forgejo Authors. +// SPDX-License-Identifier: MIT + package markup import ( From ed8e8a792e75b930074cd3cf1bab580a09ff8485 Mon Sep 17 00:00:00 2001 From: Mai-Lapyst Date: Mon, 18 Mar 2024 06:23:12 +0100 Subject: [PATCH 0011/3435] Run make fmt --- modules/markup/file_preview.go | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/markup/file_preview.go b/modules/markup/file_preview.go index 377809529d..2702cb7ce3 100644 --- a/modules/markup/file_preview.go +++ b/modules/markup/file_preview.go @@ -15,6 +15,7 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/translation" + "golang.org/x/net/html" "golang.org/x/net/html/atom" ) From d6428f92ce7ce67d127cbd5bb4977aa92abf071c Mon Sep 17 00:00:00 2001 From: Mai-Lapyst Date: Mon, 25 Mar 2024 14:33:30 +0100 Subject: [PATCH 0012/3435] Fix typo in language files --- options/locale/locale_en-US.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index ebc8db24ca..efaf8b72c9 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -3709,5 +3709,5 @@ symbolic_link = Symbolic link submodule = Submodule [markup] -filepreview.line = Line %[1]d in %[3]s +filepreview.line = Line %[1]d in %[2]s filepreview.lines = Lines %[1]d to %[2]d in %[3]s From 069d87b80f909e91626249afbb240a1df339a8fd Mon Sep 17 00:00:00 2001 From: Mai-Lapyst Date: Mon, 25 Mar 2024 14:33:54 +0100 Subject: [PATCH 0013/3435] Remove unneeded case for a trailing dot --- modules/markup/file_preview.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/modules/markup/file_preview.go b/modules/markup/file_preview.go index 2702cb7ce3..3e76dcb8a4 100644 --- a/modules/markup/file_preview.go +++ b/modules/markup/file_preview.go @@ -62,14 +62,6 @@ func NewFilePreview(ctx *RenderContext, node *html.Node, locale translation.Loca preview.start = m[0] preview.end = m[1] - // If url ends in '.', it's very likely that it is not part of the - // actual url but used to finish a sentence. - if strings.HasSuffix(preview.urlFull, ".") { - preview.end-- - preview.urlFull = preview.urlFull[:len(preview.urlFull)-1] - hash = hash[:len(hash)-1] - } - projPathSegments := strings.Split(projPath, "/") fileContent, err := DefaultProcessorHelper.GetRepoFileContent( ctx.Ctx, From 2b6546adc954d450a9c6befccd407ce2ca1636a0 Mon Sep 17 00:00:00 2001 From: Mai-Lapyst Date: Mon, 25 Mar 2024 16:05:01 +0100 Subject: [PATCH 0014/3435] Add setting to restrict count of lines being displayed & only highlight those lines --- custom/conf/app.example.ini | 2 + modules/markup/file_preview.go | 103 ++++++++++++++++++++++++----- modules/markup/html.go | 2 +- modules/markup/renderer.go | 4 +- modules/markup/sanitizer.go | 1 + modules/setting/markup.go | 2 + options/locale/locale_en-US.ini | 1 + services/markup/processorhelper.go | 24 ++++--- web_src/css/markup/filepreview.css | 6 ++ 9 files changed, 117 insertions(+), 28 deletions(-) diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index b3896bc31c..91f86da5f8 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -2338,6 +2338,8 @@ LEVEL = Info ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Set the maximum number of characters in a mermaid source. (Set to -1 to disable limits) ;MERMAID_MAX_SOURCE_CHARACTERS = 5000 +;; Set the maximum number of lines allowed for a filepreview. (Set to -1 to disable limits; set to 0 to disable the feature) +;FILEPREVIEW_MAX_LINES = 50 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/modules/markup/file_preview.go b/modules/markup/file_preview.go index 3e76dcb8a4..32683c317c 100644 --- a/modules/markup/file_preview.go +++ b/modules/markup/file_preview.go @@ -4,6 +4,7 @@ package markup import ( + "bufio" "bytes" "html/template" "regexp" @@ -12,6 +13,7 @@ import ( "strings" "code.gitea.io/gitea/modules/charset" + "code.gitea.io/gitea/modules/highlight" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/translation" @@ -31,9 +33,15 @@ type FilePreview struct { filePath string start int end int + isTruncated bool } func NewFilePreview(ctx *RenderContext, node *html.Node, locale translation.Locale) *FilePreview { + if (setting.FilePreviewMaxLines == 0) { + // Feature is disabled + return nil + } + preview := &FilePreview{} m := filePreviewPattern.FindStringSubmatchIndex(node.Data) @@ -63,18 +71,20 @@ func NewFilePreview(ctx *RenderContext, node *html.Node, locale translation.Loca preview.end = m[1] projPathSegments := strings.Split(projPath, "/") - fileContent, err := DefaultProcessorHelper.GetRepoFileContent( + var language string + fileBlob, err := DefaultProcessorHelper.GetRepoFileBlob( ctx.Ctx, projPathSegments[len(projPathSegments)-2], projPathSegments[len(projPathSegments)-1], commitSha, preview.filePath, + &language, ) if err != nil { return nil } lineSpecs := strings.Split(hash, "-") - lineCount := len(fileContent) + // lineCount := len(fileContent) commitLinkBuffer := new(bytes.Buffer) err = html.Render(commitLinkBuffer, createLink(node.Data[m[0]:m[5]], commitSha[0:7], "text black")) @@ -82,28 +92,31 @@ func NewFilePreview(ctx *RenderContext, node *html.Node, locale translation.Loca log.Error("failed to render commitLink: %v", err) } - if len(lineSpecs) == 1 { - line, _ := strconv.Atoi(strings.TrimPrefix(lineSpecs[0], "L")) - if line < 1 || line > lineCount { - return nil - } + var startLine, endLine int - preview.fileContent = fileContent[line-1 : line] + if len(lineSpecs) == 1 { + startLine, _ = strconv.Atoi(strings.TrimPrefix(lineSpecs[0], "L")) + endLine = startLine + // if line < 1 || line > lineCount { + // return nil + // } + + // preview.fileContent = fileContent[line-1 : line] preview.subTitle = locale.Tr( - "markup.filepreview.line", line, + "markup.filepreview.line", startLine, template.HTML(commitLinkBuffer.String()), ) - preview.lineOffset = line - 1 + preview.lineOffset = startLine - 1 } else { - startLine, _ := strconv.Atoi(strings.TrimPrefix(lineSpecs[0], "L")) - endLine, _ := strconv.Atoi(strings.TrimPrefix(lineSpecs[1], "L")) + startLine, _ = strconv.Atoi(strings.TrimPrefix(lineSpecs[0], "L")) + endLine, _ = strconv.Atoi(strings.TrimPrefix(lineSpecs[1], "L")) - if startLine < 1 || endLine < 1 || startLine > lineCount || endLine > lineCount || endLine < startLine { - return nil - } + // if startLine < 1 || endLine < 1 || startLine > lineCount || endLine > lineCount || endLine < startLine { + // return nil + // } - preview.fileContent = fileContent[startLine-1 : endLine] + // preview.fileContent = fileContent[startLine-1 : endLine] preview.subTitle = locale.Tr( "markup.filepreview.lines", startLine, endLine, template.HTML(commitLinkBuffer.String()), @@ -112,6 +125,50 @@ func NewFilePreview(ctx *RenderContext, node *html.Node, locale translation.Loca preview.lineOffset = startLine - 1 } + lineCount := endLine - (startLine-1) + if startLine < 1 || endLine < 1 || lineCount < 1 { + return nil + } + + if setting.FilePreviewMaxLines > 0 && lineCount > setting.FilePreviewMaxLines { + preview.isTruncated = true + lineCount = setting.FilePreviewMaxLines + } + + dataRc, err := fileBlob.DataAsync() + if err != nil { + return nil + } + defer dataRc.Close() + + reader := bufio.NewReader(dataRc) + + // skip all lines until we find our startLine + for i := 1; i < startLine; i++ { + _, err := reader.ReadBytes('\n') + if err != nil { + return nil + } + } + + // capture the lines we're interested in + lineBuffer := new(bytes.Buffer) + for i := 0; i < lineCount; i++ { + buf, err := reader.ReadBytes('\n') + if err != nil { + break; + } + lineBuffer.Write(buf) + } + + // highlight the file... + fileContent, _, err := highlight.File(fileBlob.Name(), language, lineBuffer.Bytes()) + if err != nil { + log.Error("highlight.File failed, fallback to plain text: %v", err) + fileContent = highlight.PlainText(lineBuffer.Bytes()) + } + preview.fileContent = fileContent + return preview } @@ -258,6 +315,20 @@ func (p *FilePreview) CreateHTML(locale translation.Locale) *html.Node { Attr: []html.Attribute{{Key: "class", Val: "file-preview-box"}}, } node.AppendChild(header) + + if (p.isTruncated) { + warning := &html.Node{ + Type: html.ElementNode, + Data: atom.Div.String(), + Attr: []html.Attribute{{Key: "class", Val: "ui warning message tw-text-left"}}, + } + warning.AppendChild(&html.Node{ + Type: html.TextNode, + Data: locale.TrString("markup.filepreview.truncated"), + }) + node.AppendChild(warning) + } + node.AppendChild(twrapper) return node diff --git a/modules/markup/html.go b/modules/markup/html.go index 9a04e02fb8..4c74a81ba7 100644 --- a/modules/markup/html.go +++ b/modules/markup/html.go @@ -1059,7 +1059,7 @@ func filePreviewPatternProcessor(ctx *RenderContext, node *html.Node) { if ctx.Metas == nil { return } - if DefaultProcessorHelper.GetRepoFileContent == nil { + if DefaultProcessorHelper.GetRepoFileBlob == nil { return } diff --git a/modules/markup/renderer.go b/modules/markup/renderer.go index b6d742e5ce..b08c9eb230 100644 --- a/modules/markup/renderer.go +++ b/modules/markup/renderer.go @@ -8,7 +8,7 @@ import ( "context" "errors" "fmt" - "html/template" + // "html/template" "io" "net/url" "path/filepath" @@ -32,7 +32,7 @@ const ( type ProcessorHelper struct { IsUsernameMentionable func(ctx context.Context, username string) bool - GetRepoFileContent func(ctx context.Context, ownerName, repoName, commitSha, filePath string) ([]template.HTML, error) + GetRepoFileBlob func(ctx context.Context, ownerName, repoName, commitSha, filePath string, language *string) (*git.Blob, error) ElementDir string // the direction of the elements, eg: "ltr", "rtl", "auto", default to no direction attribute } diff --git a/modules/markup/sanitizer.go b/modules/markup/sanitizer.go index c37027b843..1048f0e374 100644 --- a/modules/markup/sanitizer.go +++ b/modules/markup/sanitizer.go @@ -135,6 +135,7 @@ func createDefaultPolicy() *bluemonday.Policy { policy.AllowAttrs("class").Matching(regexp.MustCompile("^ambiguous-code-point$")).OnElements("span") policy.AllowAttrs("data-tooltip-content").OnElements("span") policy.AllowAttrs("class").Matching(regexp.MustCompile("muted|(text black)")).OnElements("a") + policy.AllowAttrs("class").Matching(regexp.MustCompile("^ui warning message tw-text-left$")).OnElements("div") // Allow generally safe attributes generalSafeAttrs := []string{ diff --git a/modules/setting/markup.go b/modules/setting/markup.go index 6c2246342b..e893c1c2f1 100644 --- a/modules/setting/markup.go +++ b/modules/setting/markup.go @@ -15,6 +15,7 @@ var ( ExternalMarkupRenderers []*MarkupRenderer ExternalSanitizerRules []MarkupSanitizerRule MermaidMaxSourceCharacters int + FilePreviewMaxLines int ) const ( @@ -62,6 +63,7 @@ func loadMarkupFrom(rootCfg ConfigProvider) { mustMapSetting(rootCfg, "markdown", &Markdown) MermaidMaxSourceCharacters = rootCfg.Section("markup").Key("MERMAID_MAX_SOURCE_CHARACTERS").MustInt(5000) + FilePreviewMaxLines = rootCfg.Section("markup").Key("FILEPREVIEW_MAX_LINES").MustInt(50) ExternalMarkupRenderers = make([]*MarkupRenderer, 0, 10) ExternalSanitizerRules = make([]MarkupSanitizerRule, 0, 10) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index efaf8b72c9..8533cf0650 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -3711,3 +3711,4 @@ submodule = Submodule [markup] filepreview.line = Line %[1]d in %[2]s filepreview.lines = Lines %[1]d to %[2]d in %[3]s +filepreview.truncated = Preview has been truncated diff --git a/services/markup/processorhelper.go b/services/markup/processorhelper.go index df96f25ce9..98a7824a6e 100644 --- a/services/markup/processorhelper.go +++ b/services/markup/processorhelper.go @@ -6,15 +6,17 @@ package markup import ( "context" "fmt" - "html/template" - "io" + + // "html/template" + // "io" "code.gitea.io/gitea/models/perm/access" "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" - "code.gitea.io/gitea/modules/highlight" + // "code.gitea.io/gitea/modules/highlight" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup" gitea_context "code.gitea.io/gitea/services/context" @@ -39,7 +41,7 @@ func ProcessorHelper() *markup.ProcessorHelper { // when using gitea context (web context), use user's visibility and user's permission to check return user.IsUserVisibleToViewer(giteaCtx, mentionedUser, giteaCtx.Doer) }, - GetRepoFileContent: func(ctx context.Context, ownerName, repoName, commitSha, filePath string) ([]template.HTML, error) { + GetRepoFileBlob: func(ctx context.Context, ownerName, repoName, commitSha, filePath string, language *string) (*git.Blob, error) { repo, err := repo.GetRepositoryByOwnerAndName(ctx, ownerName, repoName) if err != nil { return nil, err @@ -70,9 +72,11 @@ func ProcessorHelper() *markup.ProcessorHelper { return nil, err } - language, err := file_service.TryGetContentLanguage(gitRepo, commitSha, filePath) - if err != nil { - log.Error("Unable to get file language for %-v:%s. Error: %v", repo, filePath, err) + if language != nil { + *language, err = file_service.TryGetContentLanguage(gitRepo, commitSha, filePath) + if err != nil { + log.Error("Unable to get file language for %-v:%s. Error: %v", repo, filePath, err) + } } blob, err := commit.GetBlobByPath(filePath) @@ -80,7 +84,9 @@ func ProcessorHelper() *markup.ProcessorHelper { return nil, err } - dataRc, err := blob.DataAsync() + return blob, nil + + /*dataRc, err := blob.DataAsync() if err != nil { return nil, err } @@ -97,7 +103,7 @@ func ProcessorHelper() *markup.ProcessorHelper { fileContent = highlight.PlainText(buf) } - return fileContent, nil + return fileContent, nil*/ }, } } diff --git a/web_src/css/markup/filepreview.css b/web_src/css/markup/filepreview.css index 69360e2a70..d2ec16ea8b 100644 --- a/web_src/css/markup/filepreview.css +++ b/web_src/css/markup/filepreview.css @@ -25,6 +25,12 @@ background: var(--color-box-header); } +.markup .file-preview-box .warning { + border-radius: 0; + margin: 0; + padding: .5rem .5rem .5rem 1rem; +} + .markup .file-preview-box .header > a { display: block; } From 4f43b7338b956e7850927ba452492fe1b4f33238 Mon Sep 17 00:00:00 2001 From: Leo Heitmann Ruiz Date: Tue, 26 Mar 2024 22:01:53 +0100 Subject: [PATCH 0015/3435] "Plaintext" => "Text" --- modules/highlight/highlight.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/highlight/highlight.go b/modules/highlight/highlight.go index d7ab3f7afd..8fa2de6daf 100644 --- a/modules/highlight/highlight.go +++ b/modules/highlight/highlight.go @@ -217,7 +217,7 @@ func PlainText(code []byte) []template.HTML { func formatLexerName(name string) string { if name == "fallback" { - return "Plaintext" + return "Text" } return util.ToTitleCaseNoLower(name) From c340e020786b1451c1693853fc3c42102729a10a Mon Sep 17 00:00:00 2001 From: Leo Heitmann Ruiz Date: Tue, 26 Mar 2024 22:25:34 +0100 Subject: [PATCH 0016/3435] "Plaintext" => "Text" --- modules/highlight/highlight_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/highlight/highlight_test.go b/modules/highlight/highlight_test.go index 659688bd0f..dd15b97847 100644 --- a/modules/highlight/highlight_test.go +++ b/modules/highlight/highlight_test.go @@ -58,7 +58,7 @@ func TestFile(t *testing.T) { name: "tags.txt", code: "<>", want: lines("<>"), - lexerName: "Plaintext", + lexerName: "Text", }, { name: "tags.py", From 4c7cb0a5d20e8973b03e35d91119cf917eed125e Mon Sep 17 00:00:00 2001 From: Mai-Lapyst Date: Wed, 27 Mar 2024 18:25:37 +0100 Subject: [PATCH 0017/3435] Close git.Repository when GetRepoFileBlob returns --- services/markup/processorhelper.go | 1 + 1 file changed, 1 insertion(+) diff --git a/services/markup/processorhelper.go b/services/markup/processorhelper.go index 98a7824a6e..7466e962d2 100644 --- a/services/markup/processorhelper.go +++ b/services/markup/processorhelper.go @@ -66,6 +66,7 @@ func ProcessorHelper() *markup.ProcessorHelper { if err != nil { return nil, err } + defer gitRepo.Close() commit, err := gitRepo.GetCommit(commitSha) if err != nil { From 7e0014dd1391e123d95f2537c3b2165fef7122ef Mon Sep 17 00:00:00 2001 From: Mai-Lapyst Date: Wed, 27 Mar 2024 18:36:12 +0100 Subject: [PATCH 0018/3435] Fix formating & remove commented out code --- modules/markup/file_preview.go | 20 ++++---------------- modules/markup/renderer.go | 2 +- services/markup/processorhelper.go | 19 ------------------- 3 files changed, 5 insertions(+), 36 deletions(-) diff --git a/modules/markup/file_preview.go b/modules/markup/file_preview.go index 32683c317c..95c94e0c14 100644 --- a/modules/markup/file_preview.go +++ b/modules/markup/file_preview.go @@ -37,7 +37,7 @@ type FilePreview struct { } func NewFilePreview(ctx *RenderContext, node *html.Node, locale translation.Locale) *FilePreview { - if (setting.FilePreviewMaxLines == 0) { + if setting.FilePreviewMaxLines == 0 { // Feature is disabled return nil } @@ -84,7 +84,6 @@ func NewFilePreview(ctx *RenderContext, node *html.Node, locale translation.Loca } lineSpecs := strings.Split(hash, "-") - // lineCount := len(fileContent) commitLinkBuffer := new(bytes.Buffer) err = html.Render(commitLinkBuffer, createLink(node.Data[m[0]:m[5]], commitSha[0:7], "text black")) @@ -97,11 +96,6 @@ func NewFilePreview(ctx *RenderContext, node *html.Node, locale translation.Loca if len(lineSpecs) == 1 { startLine, _ = strconv.Atoi(strings.TrimPrefix(lineSpecs[0], "L")) endLine = startLine - // if line < 1 || line > lineCount { - // return nil - // } - - // preview.fileContent = fileContent[line-1 : line] preview.subTitle = locale.Tr( "markup.filepreview.line", startLine, template.HTML(commitLinkBuffer.String()), @@ -111,12 +105,6 @@ func NewFilePreview(ctx *RenderContext, node *html.Node, locale translation.Loca } else { startLine, _ = strconv.Atoi(strings.TrimPrefix(lineSpecs[0], "L")) endLine, _ = strconv.Atoi(strings.TrimPrefix(lineSpecs[1], "L")) - - // if startLine < 1 || endLine < 1 || startLine > lineCount || endLine > lineCount || endLine < startLine { - // return nil - // } - - // preview.fileContent = fileContent[startLine-1 : endLine] preview.subTitle = locale.Tr( "markup.filepreview.lines", startLine, endLine, template.HTML(commitLinkBuffer.String()), @@ -125,7 +113,7 @@ func NewFilePreview(ctx *RenderContext, node *html.Node, locale translation.Loca preview.lineOffset = startLine - 1 } - lineCount := endLine - (startLine-1) + lineCount := endLine - (startLine - 1) if startLine < 1 || endLine < 1 || lineCount < 1 { return nil } @@ -156,7 +144,7 @@ func NewFilePreview(ctx *RenderContext, node *html.Node, locale translation.Loca for i := 0; i < lineCount; i++ { buf, err := reader.ReadBytes('\n') if err != nil { - break; + break } lineBuffer.Write(buf) } @@ -316,7 +304,7 @@ func (p *FilePreview) CreateHTML(locale translation.Locale) *html.Node { } node.AppendChild(header) - if (p.isTruncated) { + if p.isTruncated { warning := &html.Node{ Type: html.ElementNode, Data: atom.Div.String(), diff --git a/modules/markup/renderer.go b/modules/markup/renderer.go index b08c9eb230..163cd5d688 100644 --- a/modules/markup/renderer.go +++ b/modules/markup/renderer.go @@ -32,7 +32,7 @@ const ( type ProcessorHelper struct { IsUsernameMentionable func(ctx context.Context, username string) bool - GetRepoFileBlob func(ctx context.Context, ownerName, repoName, commitSha, filePath string, language *string) (*git.Blob, error) + GetRepoFileBlob func(ctx context.Context, ownerName, repoName, commitSha, filePath string, language *string) (*git.Blob, error) ElementDir string // the direction of the elements, eg: "ltr", "rtl", "auto", default to no direction attribute } diff --git a/services/markup/processorhelper.go b/services/markup/processorhelper.go index 7466e962d2..ac751d0e62 100644 --- a/services/markup/processorhelper.go +++ b/services/markup/processorhelper.go @@ -86,25 +86,6 @@ func ProcessorHelper() *markup.ProcessorHelper { } return blob, nil - - /*dataRc, err := blob.DataAsync() - if err != nil { - return nil, err - } - defer dataRc.Close() - - buf, err := io.ReadAll(dataRc) - if err != nil { - log.Error("failed to completly read blob for %-v:%s. Error: %v", repo, filePath, err) - } - - fileContent, _, err := highlight.File(blob.Name(), language, buf) - if err != nil { - log.Error("highlight.File failed, fallback to plain text: %v", err) - fileContent = highlight.PlainText(buf) - } - - return fileContent, nil*/ }, } } From 5785ae72c75ea66cdadfc260d59084e0bb0bf0bb Mon Sep 17 00:00:00 2001 From: oliverpool Date: Wed, 27 Mar 2024 22:02:51 +0100 Subject: [PATCH 0019/3435] [TESTS] prevent overriding testlogger when calling mainApp --- modules/testlogger/testlogger.go | 5 +++ tests/integration/cmd_forgejo_actions_test.go | 32 +++++++++------- tests/integration/cmd_forgejo_test.go | 36 ------------------ tests/integration/cmd_keys_test.go | 27 +++++++------ tests/integration/integration_test.go | 38 +++++++++++++++++++ 5 files changed, 75 insertions(+), 63 deletions(-) delete mode 100644 tests/integration/cmd_forgejo_test.go diff --git a/modules/testlogger/testlogger.go b/modules/testlogger/testlogger.go index c5ca906220..43fbaf0afd 100644 --- a/modules/testlogger/testlogger.go +++ b/modules/testlogger/testlogger.go @@ -128,6 +128,11 @@ func (w *testLoggerWriterCloser) recordError(msg string) { err = w.errs[len(w.errs)-1] } + if len(w.t) > 0 { + // format error message to easily add it to the ignore list + msg = fmt.Sprintf("// %s\n\t`%s`,", w.t[len(w.t)-1].Name(), msg) + } + err = errors.Join(err, errors.New(msg)) if len(w.errs) > 0 { diff --git a/tests/integration/cmd_forgejo_actions_test.go b/tests/integration/cmd_forgejo_actions_test.go index 44211007f5..e45526ac7a 100644 --- a/tests/integration/cmd_forgejo_actions_test.go +++ b/tests/integration/cmd_forgejo_actions_test.go @@ -4,8 +4,11 @@ package integration import ( gocontext "context" + "errors" + "io" "net/url" "os" + "os/exec" "strings" "testing" @@ -19,16 +22,18 @@ import ( func Test_CmdForgejo_Actions(t *testing.T) { onGiteaRun(t, func(*testing.T, *url.URL) { - token, err := cmdForgejoCaptureOutput(t, []string{"forgejo", "forgejo-cli", "actions", "generate-runner-token"}) + token, err := runMainApp("forgejo-cli", "actions", "generate-runner-token") assert.NoError(t, err) assert.EqualValues(t, 40, len(token)) - secret, err := cmdForgejoCaptureOutput(t, []string{"forgejo", "forgejo-cli", "actions", "generate-secret"}) + secret, err := runMainApp("forgejo-cli", "actions", "generate-secret") assert.NoError(t, err) assert.EqualValues(t, 40, len(secret)) - _, err = cmdForgejoCaptureOutput(t, []string{"forgejo", "forgejo-cli", "actions", "register"}) - assert.ErrorContains(t, err, "at least one of the --secret") + _, err = runMainApp("forgejo-cli", "actions", "register") + var exitErr *exec.ExitError + assert.True(t, errors.As(err, &exitErr)) + assert.Contains(t, string(exitErr.Stderr), "at least one of the --secret") for _, testCase := range []struct { testName string @@ -62,10 +67,12 @@ func Test_CmdForgejo_Actions(t *testing.T) { }, } { t.Run(testCase.testName, func(t *testing.T) { - cmd := []string{"forgejo", "forgejo-cli", "actions", "register", "--secret", testCase.secret, "--scope", testCase.scope} - output, err := cmdForgejoCaptureOutput(t, cmd) - assert.ErrorContains(t, err, testCase.errorMessage) + output, err := runMainApp("forgejo-cli", "actions", "register", "--secret", testCase.secret, "--scope", testCase.scope) assert.EqualValues(t, "", output) + + var exitErr *exec.ExitError + assert.True(t, errors.As(err, &exitErr)) + assert.Contains(t, string(exitErr.Stderr), testCase.errorMessage) }) } @@ -75,7 +82,7 @@ func Test_CmdForgejo_Actions(t *testing.T) { for _, testCase := range []struct { testName string secretOption func() string - stdin []string + stdin io.Reader }{ { testName: "secret from argument", @@ -88,7 +95,7 @@ func Test_CmdForgejo_Actions(t *testing.T) { secretOption: func() string { return "--secret-stdin" }, - stdin: []string{secret}, + stdin: strings.NewReader(secret), }, { testName: "secret from file", @@ -100,8 +107,7 @@ func Test_CmdForgejo_Actions(t *testing.T) { }, } { t.Run(testCase.testName, func(t *testing.T) { - cmd := []string{"forgejo", "forgejo-cli", "actions", "register", testCase.secretOption(), "--scope=org26"} - uuid, err := cmdForgejoCaptureOutput(t, cmd, testCase.stdin...) + uuid, err := runMainAppWithStdin(testCase.stdin, "forgejo-cli", "actions", "register", testCase.secretOption(), "--scope=org26") assert.NoError(t, err) assert.EqualValues(t, expecteduuid, uuid) }) @@ -161,7 +167,7 @@ func Test_CmdForgejo_Actions(t *testing.T) { } { t.Run(testCase.testName, func(t *testing.T) { cmd := []string{ - "forgejo", "forgejo-cli", "actions", "register", + "actions", "register", "--secret", testCase.secret, "--scope", testCase.scope, } if testCase.name != "" { @@ -177,7 +183,7 @@ func Test_CmdForgejo_Actions(t *testing.T) { // Run twice to verify it is idempotent // for i := 0; i < 2; i++ { - uuid, err := cmdForgejoCaptureOutput(t, cmd) + uuid, err := runMainApp("forgejo-cli", cmd...) assert.NoError(t, err) if assert.EqualValues(t, testCase.uuid, uuid) { ownerName, repoName, found := strings.Cut(testCase.scope, "/") diff --git a/tests/integration/cmd_forgejo_test.go b/tests/integration/cmd_forgejo_test.go deleted file mode 100644 index 76f5a6fc08..0000000000 --- a/tests/integration/cmd_forgejo_test.go +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-License-Identifier: MIT - -package integration - -import ( - "bytes" - "context" - "strings" - "testing" - - "code.gitea.io/gitea/cmd/forgejo" - - "github.com/urfave/cli/v2" -) - -func cmdForgejoCaptureOutput(t *testing.T, args []string, stdin ...string) (string, error) { - buf := new(bytes.Buffer) - - app := cli.NewApp() - app.Writer = buf - app.ErrWriter = buf - ctx := context.Background() - ctx = forgejo.ContextSetNoInit(ctx, true) - ctx = forgejo.ContextSetNoExit(ctx, true) - ctx = forgejo.ContextSetStdout(ctx, buf) - ctx = forgejo.ContextSetStderr(ctx, buf) - if len(stdin) > 0 { - ctx = forgejo.ContextSetStdin(ctx, strings.NewReader(strings.Join(stdin, ""))) - } - app.Commands = []*cli.Command{ - forgejo.CmdForgejo(ctx), - } - err := app.Run(args) - - return buf.String(), err -} diff --git a/tests/integration/cmd_keys_test.go b/tests/integration/cmd_keys_test.go index 61f11c58b0..a3220c13ce 100644 --- a/tests/integration/cmd_keys_test.go +++ b/tests/integration/cmd_keys_test.go @@ -4,16 +4,15 @@ package integration import ( - "bytes" + "errors" "net/url" + "os/exec" "testing" - "code.gitea.io/gitea/cmd" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" "github.com/stretchr/testify/assert" - "github.com/urfave/cli/v2" ) func Test_CmdKeys(t *testing.T) { @@ -24,30 +23,30 @@ func Test_CmdKeys(t *testing.T) { wantErr bool expectedOutput string }{ - {"test_empty_1", []string{"keys", "--username=git", "--type=test", "--content=test"}, true, ""}, - {"test_empty_2", []string{"keys", "-e", "git", "-u", "git", "-t", "test", "-k", "test"}, true, ""}, + {"test_empty_1", []string{"--username=git", "--type=test", "--content=test"}, true, ""}, + {"test_empty_2", []string{"-e", "git", "-u", "git", "-t", "test", "-k", "test"}, true, ""}, { "with_key", - []string{"keys", "-e", "git", "-u", "git", "-t", "ssh-rsa", "-k", "AAAAB3NzaC1yc2EAAAADAQABAAABgQDWVj0fQ5N8wNc0LVNA41wDLYJ89ZIbejrPfg/avyj3u/ZohAKsQclxG4Ju0VirduBFF9EOiuxoiFBRr3xRpqzpsZtnMPkWVWb+akZwBFAx8p+jKdy4QXR/SZqbVobrGwip2UjSrri1CtBxpJikojRIZfCnDaMOyd9Jp6KkujvniFzUWdLmCPxUE9zhTaPu0JsEP7MW0m6yx7ZUhHyfss+NtqmFTaDO+QlMR7L2QkDliN2Jl3Xa3PhuWnKJfWhdAq1Cw4oraKUOmIgXLkuiuxVQ6mD3AiFupkmfqdHq6h+uHHmyQqv3gU+/sD8GbGAhf6ftqhTsXjnv1Aj4R8NoDf9BS6KRkzkeun5UisSzgtfQzjOMEiJtmrep2ZQrMGahrXa+q4VKr0aKJfm+KlLfwm/JztfsBcqQWNcTURiCFqz+fgZw0Ey/de0eyMzldYTdXXNRYCKjs9bvBK+6SSXRM7AhftfQ0ZuoW5+gtinPrnmoOaSCEJbAiEiTO/BzOHgowiM="}, + []string{"-e", "git", "-u", "git", "-t", "ssh-rsa", "-k", "AAAAB3NzaC1yc2EAAAADAQABAAABgQDWVj0fQ5N8wNc0LVNA41wDLYJ89ZIbejrPfg/avyj3u/ZohAKsQclxG4Ju0VirduBFF9EOiuxoiFBRr3xRpqzpsZtnMPkWVWb+akZwBFAx8p+jKdy4QXR/SZqbVobrGwip2UjSrri1CtBxpJikojRIZfCnDaMOyd9Jp6KkujvniFzUWdLmCPxUE9zhTaPu0JsEP7MW0m6yx7ZUhHyfss+NtqmFTaDO+QlMR7L2QkDliN2Jl3Xa3PhuWnKJfWhdAq1Cw4oraKUOmIgXLkuiuxVQ6mD3AiFupkmfqdHq6h+uHHmyQqv3gU+/sD8GbGAhf6ftqhTsXjnv1Aj4R8NoDf9BS6KRkzkeun5UisSzgtfQzjOMEiJtmrep2ZQrMGahrXa+q4VKr0aKJfm+KlLfwm/JztfsBcqQWNcTURiCFqz+fgZw0Ey/de0eyMzldYTdXXNRYCKjs9bvBK+6SSXRM7AhftfQ0ZuoW5+gtinPrnmoOaSCEJbAiEiTO/BzOHgowiM="}, false, "# gitea public key\ncommand=\"" + setting.AppPath + " --config=" + util.ShellEscape(setting.CustomConf) + " serv key-1\",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty,no-user-rc,restrict ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDWVj0fQ5N8wNc0LVNA41wDLYJ89ZIbejrPfg/avyj3u/ZohAKsQclxG4Ju0VirduBFF9EOiuxoiFBRr3xRpqzpsZtnMPkWVWb+akZwBFAx8p+jKdy4QXR/SZqbVobrGwip2UjSrri1CtBxpJikojRIZfCnDaMOyd9Jp6KkujvniFzUWdLmCPxUE9zhTaPu0JsEP7MW0m6yx7ZUhHyfss+NtqmFTaDO+QlMR7L2QkDliN2Jl3Xa3PhuWnKJfWhdAq1Cw4oraKUOmIgXLkuiuxVQ6mD3AiFupkmfqdHq6h+uHHmyQqv3gU+/sD8GbGAhf6ftqhTsXjnv1Aj4R8NoDf9BS6KRkzkeun5UisSzgtfQzjOMEiJtmrep2ZQrMGahrXa+q4VKr0aKJfm+KlLfwm/JztfsBcqQWNcTURiCFqz+fgZw0Ey/de0eyMzldYTdXXNRYCKjs9bvBK+6SSXRM7AhftfQ0ZuoW5+gtinPrnmoOaSCEJbAiEiTO/BzOHgowiM= user2@localhost\n", }, - {"invalid", []string{"keys", "--not-a-flag=git"}, true, "Incorrect Usage: flag provided but not defined: -not-a-flag\n\n"}, + {"invalid", []string{"--not-a-flag=git"}, true, "Incorrect Usage: flag provided but not defined: -not-a-flag\n\n"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - out := new(bytes.Buffer) - app := cli.NewApp() - app.Writer = out - app.Commands = []*cli.Command{cmd.CmdKeys} - cmd.CmdKeys.HideHelp = true - err := app.Run(append([]string{"prog"}, tt.args...)) + out, err := runMainApp("keys", tt.args...) + + var exitErr *exec.ExitError + if errors.As(err, &exitErr) { + t.Log(string(exitErr.Stderr)) + } if tt.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } - assert.Equal(t, tt.expectedOutput, out.String()) + assert.Equal(t, tt.expectedOutput, out) }) } }) diff --git a/tests/integration/integration_test.go b/tests/integration/integration_test.go index e8f28105c1..b087281ff4 100644 --- a/tests/integration/integration_test.go +++ b/tests/integration/integration_test.go @@ -17,6 +17,7 @@ import ( "net/http/httptest" "net/url" "os" + "os/exec" "path/filepath" "strconv" "strings" @@ -24,6 +25,7 @@ import ( "testing" "time" + "code.gitea.io/gitea/cmd" "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" @@ -93,7 +95,43 @@ func NewNilResponseHashSumRecorder() *NilResponseHashSumRecorder { } } +// runMainApp runs the subcommand and returns its standard output. Any returned error will usually be of type *ExitError. If c.Stderr was nil, Output populates ExitError.Stderr. +func runMainApp(subcommand string, args ...string) (string, error) { + return runMainAppWithStdin(nil, subcommand, args...) +} + +// runMainAppWithStdin runs the subcommand and returns its standard output. Any returned error will usually be of type *ExitError. If c.Stderr was nil, Output populates ExitError.Stderr. +func runMainAppWithStdin(stdin io.Reader, subcommand string, args ...string) (string, error) { + // running the main app directly will very likely mess with the testing setup (logger & co.) + // hence we run it as a subprocess and capture its output + args = append([]string{subcommand}, args...) + cmd := exec.Command(os.Args[0], args...) + cmd.Env = append(os.Environ(), + "GITEA_TEST_CLI=true", + "GITEA_CONF="+setting.CustomConf, + "GITEA_WORK_DIR="+setting.AppWorkPath) + cmd.Stdin = stdin + out, err := cmd.Output() + return string(out), err +} + func TestMain(m *testing.M) { + // GITEA_TEST_CLI is set by runMainAppWithStdin + // inspired by https://abhinavg.net/2022/05/15/hijack-testmain/ + if testCLI := os.Getenv("GITEA_TEST_CLI"); testCLI == "true" { + app := cmd.NewMainApp("test-version", "integration-test") + args := append([]string{ + "executable-name", // unused, but expected at position 1 + "--config", os.Getenv("GITEA_CONF"), + }, + os.Args[1:]..., // skip the executable name + ) + if err := cmd.RunMainApp(app, args...); err != nil { + panic(err) // should never happen since RunMainApp exits on error + } + return + } + defer log.GetManager().Close() managerCtx, cancel := context.WithCancel(context.Background()) From 16a8658878a2656cb131453b728b65a89271f11f Mon Sep 17 00:00:00 2001 From: Mai-Lapyst Date: Thu, 28 Mar 2024 04:20:13 +0100 Subject: [PATCH 0020/3435] Update test --- modules/markup/html_test.go | 40 ++++++++++++------ .../markup/tests/repo/repo1_filepreview/HEAD | 1 + .../tests/repo/repo1_filepreview/config | 6 +++ .../tests/repo/repo1_filepreview/description | 1 + .../tests/repo/repo1_filepreview/info/exclude | 6 +++ .../19/0d9492934af498c3f669d6a2431dc5459e5b20 | Bin 0 -> 120 bytes .../4b/825dc642cb6eb9a060e54bf8d69288fbee4904 | Bin 0 -> 15 bytes .../83/57a737d04385bb7f2ab59ff184be94756e7972 | Bin 0 -> 44 bytes .../84/22d40f12717e1ebd5cef2449f6c09d1f775969 | Bin 0 -> 23 bytes .../d4/490327def9658be036d6a52c4417d84e74dd4c | Bin 0 -> 46 bytes .../ee/2b1253d9cf407796e2e724926cbe3a974b214d | 1 + .../repo/repo1_filepreview/refs/heads/master | 1 + 12 files changed, 43 insertions(+), 13 deletions(-) create mode 100644 modules/markup/tests/repo/repo1_filepreview/HEAD create mode 100644 modules/markup/tests/repo/repo1_filepreview/config create mode 100644 modules/markup/tests/repo/repo1_filepreview/description create mode 100644 modules/markup/tests/repo/repo1_filepreview/info/exclude create mode 100644 modules/markup/tests/repo/repo1_filepreview/objects/19/0d9492934af498c3f669d6a2431dc5459e5b20 create mode 100644 modules/markup/tests/repo/repo1_filepreview/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 create mode 100644 modules/markup/tests/repo/repo1_filepreview/objects/83/57a737d04385bb7f2ab59ff184be94756e7972 create mode 100644 modules/markup/tests/repo/repo1_filepreview/objects/84/22d40f12717e1ebd5cef2449f6c09d1f775969 create mode 100644 modules/markup/tests/repo/repo1_filepreview/objects/d4/490327def9658be036d6a52c4417d84e74dd4c create mode 100644 modules/markup/tests/repo/repo1_filepreview/objects/ee/2b1253d9cf407796e2e724926cbe3a974b214d create mode 100644 modules/markup/tests/repo/repo1_filepreview/refs/heads/master diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go index c43f006266..3583894bae 100644 --- a/modules/markup/html_test.go +++ b/modules/markup/html_test.go @@ -5,7 +5,6 @@ package markup_test import ( "context" - "html/template" "io" "os" "strings" @@ -14,14 +13,15 @@ import ( "code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/modules/emoji" "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/highlight" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/translation" "code.gitea.io/gitea/modules/util" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) var localMetas = map[string]string{ @@ -677,16 +677,30 @@ func TestIssue18471(t *testing.T) { } func TestRender_FilePreview(t *testing.T) { + setting.StaticRootPath = "../../" + setting.Names = []string{"english"} + setting.Langs = []string{"en-US"} + translation.InitLocales(context.Background()) + setting.AppURL = markup.TestAppURL markup.Init(&markup.ProcessorHelper{ - GetRepoFileContent: func(ctx context.Context, ownerName, repoName, commitSha, filePath string) ([]template.HTML, error) { - buf := []byte("A\nB\nC\nD\n") - return highlight.PlainText(buf), nil + GetRepoFileBlob: func(ctx context.Context, ownerName, repoName, commitSha, filePath string, language *string) (*git.Blob, error) { + gitRepo, err := git.OpenRepository(git.DefaultContext, "./tests/repo/repo1_filepreview") + require.NoError(t, err) + defer gitRepo.Close() + + commit, err := gitRepo.GetCommit("HEAD") + require.NoError(t, err) + + blob, err := commit.GetBlobByPath("path/to/file.go") + require.NoError(t, err) + + return blob, nil }, }) - sha := "b6dd6210eaebc915fd5be5579c58cce4da2e2579" - commitFilePreview := util.URLJoin(markup.TestRepoURL, "src", "commit", sha, "path", "to", "file.go") + "#L1-L2" + sha := "190d9492934af498c3f669d6a2431dc5459e5b20" + commitFilePreview := util.URLJoin(markup.TestRepoURL, "src", "commit", sha, "path", "to", "file.go") + "#L2-L3" test := func(input, expected string) { buffer, err := markup.RenderString(&markup.RenderContext{ @@ -703,21 +717,21 @@ func TestRender_FilePreview(t *testing.T) { `

`+ `
`+ `
`+ - `path/to/file.go`+ + `path/to/file.go`+ ``+ - `Lines 1 to 2 in b6dd621`+ + `Lines 2 to 3 in 190d949`+ ``+ `
`+ `
`+ ``+ ``+ ``+ - ``+ - ``+ + ``+ + ``+ ``+ ``+ - ``+ - ``+ + ``+ + ``+ ``+ ``+ `
A`+"\n"+`B`+"\n"+`
B`+"\n"+`C`+"\n"+`
`+ diff --git a/modules/markup/tests/repo/repo1_filepreview/HEAD b/modules/markup/tests/repo/repo1_filepreview/HEAD new file mode 100644 index 0000000000..cb089cd89a --- /dev/null +++ b/modules/markup/tests/repo/repo1_filepreview/HEAD @@ -0,0 +1 @@ +ref: refs/heads/master diff --git a/modules/markup/tests/repo/repo1_filepreview/config b/modules/markup/tests/repo/repo1_filepreview/config new file mode 100644 index 0000000000..42cc799c8d --- /dev/null +++ b/modules/markup/tests/repo/repo1_filepreview/config @@ -0,0 +1,6 @@ +[core] + repositoryformatversion = 0 + filemode = true + bare = true +[remote "origin"] + url = /home/mai/projects/codeark/forgejo/forgejo/modules/markup/tests/repo/repo1_filepreview/../../__test_repo diff --git a/modules/markup/tests/repo/repo1_filepreview/description b/modules/markup/tests/repo/repo1_filepreview/description new file mode 100644 index 0000000000..498b267a8c --- /dev/null +++ b/modules/markup/tests/repo/repo1_filepreview/description @@ -0,0 +1 @@ +Unnamed repository; edit this file 'description' to name the repository. diff --git a/modules/markup/tests/repo/repo1_filepreview/info/exclude b/modules/markup/tests/repo/repo1_filepreview/info/exclude new file mode 100644 index 0000000000..a5196d1be8 --- /dev/null +++ b/modules/markup/tests/repo/repo1_filepreview/info/exclude @@ -0,0 +1,6 @@ +# git ls-files --others --exclude-from=.git/info/exclude +# Lines that start with '#' are comments. +# For a project mostly in C, the following would be a good set of +# exclude patterns (uncomment them if you want to use them): +# *.[oa] +# *~ diff --git a/modules/markup/tests/repo/repo1_filepreview/objects/19/0d9492934af498c3f669d6a2431dc5459e5b20 b/modules/markup/tests/repo/repo1_filepreview/objects/19/0d9492934af498c3f669d6a2431dc5459e5b20 new file mode 100644 index 0000000000000000000000000000000000000000..161d0bafc6731f5fe0b3b3c29ffe5463b056e840 GIT binary patch literal 120 zcmV-;0Ehp00hNtO4#F@D06FIsz9S(!b&^)95RZTdgxH8kluC)q`&oX#X-+d!)@7*% z6w=O`DhTt0gHNKjDTeW?I7Ep#_`*y{M%Kh4TwLDlzBagYZ3Of7#i>`*Lw&yTqskE| a(I?AD9`;CxuKZr6|5@&=-P{|T!ZCX0g*&(a literal 0 HcmV?d00001 diff --git a/modules/markup/tests/repo/repo1_filepreview/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 b/modules/markup/tests/repo/repo1_filepreview/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 new file mode 100644 index 0000000000000000000000000000000000000000..adf64119a33d7621aeeaa505d30adb58afaa5559 GIT binary patch literal 15 Wcmb)%hIiUR!8gx4luvu~TxC+uKC9{8ioO8p9WH2!R0)>Lak_?9C@a5(goLhI-Yi*tXv1Q+s(!9zd00ff{ EldH%Sg8%>k literal 0 HcmV?d00001 diff --git a/modules/markup/tests/repo/repo1_filepreview/objects/ee/2b1253d9cf407796e2e724926cbe3a974b214d b/modules/markup/tests/repo/repo1_filepreview/objects/ee/2b1253d9cf407796e2e724926cbe3a974b214d new file mode 100644 index 0000000000..e13ca647db --- /dev/null +++ b/modules/markup/tests/repo/repo1_filepreview/objects/ee/2b1253d9cf407796e2e724926cbe3a974b214d @@ -0,0 +1 @@ +x+)JMU06e040031QHIKghQ/TX'7潊s#3 \ No newline at end of file diff --git a/modules/markup/tests/repo/repo1_filepreview/refs/heads/master b/modules/markup/tests/repo/repo1_filepreview/refs/heads/master new file mode 100644 index 0000000000..49c348b41c --- /dev/null +++ b/modules/markup/tests/repo/repo1_filepreview/refs/heads/master @@ -0,0 +1 @@ +190d9492934af498c3f669d6a2431dc5459e5b20 From 6e98bacbbd3c089b2ccfa725c58184f4dfe5e7fe Mon Sep 17 00:00:00 2001 From: Mai-Lapyst Date: Thu, 28 Mar 2024 05:42:25 +0100 Subject: [PATCH 0021/3435] Format code --- modules/markup/html_test.go | 2 +- modules/markup/renderer.go | 1 - services/markup/processorhelper.go | 4 ---- 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go index 3583894bae..1ecf519f46 100644 --- a/modules/markup/html_test.go +++ b/modules/markup/html_test.go @@ -688,7 +688,7 @@ func TestRender_FilePreview(t *testing.T) { gitRepo, err := git.OpenRepository(git.DefaultContext, "./tests/repo/repo1_filepreview") require.NoError(t, err) defer gitRepo.Close() - + commit, err := gitRepo.GetCommit("HEAD") require.NoError(t, err) diff --git a/modules/markup/renderer.go b/modules/markup/renderer.go index 163cd5d688..6781d2e552 100644 --- a/modules/markup/renderer.go +++ b/modules/markup/renderer.go @@ -8,7 +8,6 @@ import ( "context" "errors" "fmt" - // "html/template" "io" "net/url" "path/filepath" diff --git a/services/markup/processorhelper.go b/services/markup/processorhelper.go index ac751d0e62..40bf1d65da 100644 --- a/services/markup/processorhelper.go +++ b/services/markup/processorhelper.go @@ -7,16 +7,12 @@ import ( "context" "fmt" - // "html/template" - // "io" - "code.gitea.io/gitea/models/perm/access" "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" - // "code.gitea.io/gitea/modules/highlight" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup" gitea_context "code.gitea.io/gitea/services/context" From cbd067e759b1de054da17c18f92fc934c10eacf8 Mon Sep 17 00:00:00 2001 From: 0ko <0ko@noreply.codeberg.org> Date: Thu, 28 Mar 2024 19:48:56 +0500 Subject: [PATCH 0022/3435] Fix accessibility and translatability of repo explore counters Progression of: https://codeberg.org/forgejo/forgejo/commit/9e69ef9c51cded6321e4cca39d33a64e9801d910 Regression of: https://codeberg.org/forgejo/forgejo/commit/65e190ae8bd6c72d8701a58d67b256c87b92c189#diff-8d94e33cfe70fa6443d059b9c34e3f8064514816 --- templates/explore/repo_list.tmpl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/templates/explore/repo_list.tmpl b/templates/explore/repo_list.tmpl index 9e5042d650..99c9bc1736 100644 --- a/templates/explore/repo_list.tmpl +++ b/templates/explore/repo_list.tmpl @@ -38,15 +38,15 @@ {{end}} {{if not $.DisableStars}} - - {{svg "octicon-star" 16}} - {{CountFmt .NumStars}} + + {{svg "octicon-star" 16}} + {{if ge .NumStars 1000}}data-tooltip-content="{{.NumStars}}"{{end}}{{CountFmt .NumStars}} {{end}} {{if not $.DisableForks}} - - {{svg "octicon-git-branch" 16}} - {{CountFmt .NumForks}} + + {{svg "octicon-git-branch" 16}} + {{if ge .NumForks 1000}}data-tooltip-content="{{.NumForks}}"{{end}}{{CountFmt .NumForks}} {{end}}
From 869795a530f6a5a4e28687c003300d39fbaee6e2 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Thu, 28 Mar 2024 15:45:51 +0100 Subject: [PATCH 0023/3435] [RELEASE] GITEA_VERSION is a fallback for FORGEJO_VERSION Existing Forgejo packages may rely on setting GITEA_VERSION to specify the version to build if: * they do not build from the git repository with the proper tag * they build from a source tarbal that does not have a VERSION file With 7.0 the logic of setting the version was modified in the `[RELEASE] Gitea version is for interoperability only` commit and ignores this variable which creates an unecessary breaking change. If GITEA_VERSION is set, the versions will be set on 7.0 exactly as they would have with version before and included 1.21. * If GITEA_VERSION is not set, all versions are the same * If GITEA_VERSION is set, there is a distinction between the version set in the binary are returned by the Gitea API and the version returned by the Forgejo API which includes metadata. Before: $ make GITEA_VERSION=7.0.0 show-version-full 7.0.0-dev-1809-cd6fa771ab+gitea-1.22.0 $ make GITEA_VERSION=7.0.0 show-version-api 7.0.0-dev-1809-cd6fa771ab+gitea-1.22.0 After: $ make GITEA_VERSION=7.0.0 show-version-full 7.0.0 $ make GITEA_VERSION=7.0.0 show-version-api 7.0.0+gitea-1.22.0 --- Makefile | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index b1c4864c7a..16d7edb2dd 100644 --- a/Makefile +++ b/Makefile @@ -88,8 +88,13 @@ STORED_VERSION=$(shell cat $(STORED_VERSION_FILE) 2>/dev/null) ifneq ($(STORED_VERSION),) FORGEJO_VERSION ?= $(STORED_VERSION) else - # drop the "g" prefix prepended by git describe to the commit hash - FORGEJO_VERSION ?= $(shell git describe --exclude '*-test' --tags --always | sed 's/^v//' | sed 's/\-g/-/')+${GITEA_COMPATIBILITY} + ifneq ($(GITEA_VERSION),) + FORGEJO_VERSION ?= $(GITEA_VERSION) + FORGEJO_VERSION_API ?= $(GITEA_VERSION)+${GITEA_COMPATIBILITY} + else + # drop the "g" prefix prepended by git describe to the commit hash + FORGEJO_VERSION ?= $(shell git describe --exclude '*-test' --tags --always | sed 's/^v//' | sed 's/\-g/-/')+${GITEA_COMPATIBILITY} + endif endif FORGEJO_VERSION_MAJOR=$(shell echo $(FORGEJO_VERSION) | sed -e 's/\..*//') FORGEJO_VERSION_MINOR=$(shell echo $(FORGEJO_VERSION) | sed -E -e 's/^([0-9]+\.[0-9]+).*/\1/') @@ -106,7 +111,12 @@ show-version-minor: RELEASE_VERSION ?= ${FORGEJO_VERSION} VERSION ?= ${RELEASE_VERSION} -LDFLAGS := $(LDFLAGS) -X "main.ReleaseVersion=$(RELEASE_VERSION)" -X "main.MakeVersion=$(MAKE_VERSION)" -X "main.Version=$(FORGEJO_VERSION)" -X "main.Tags=$(TAGS)" -X "main.ForgejoVersion=$(FORGEJO_VERSION)" +FORGEJO_VERSION_API ?= ${FORGEJO_VERSION} + +show-version-api: + @echo ${FORGEJO_VERSION_API} + +LDFLAGS := $(LDFLAGS) -X "main.ReleaseVersion=$(RELEASE_VERSION)" -X "main.MakeVersion=$(MAKE_VERSION)" -X "main.Version=$(FORGEJO_VERSION)" -X "main.Tags=$(TAGS)" -X "main.ForgejoVersion=$(FORGEJO_VERSION_API)" LINUX_ARCHS ?= linux/amd64,linux/386,linux/arm-5,linux/arm-6,linux/arm64 From 79b70893601c33a33d8d44eb0421797dfd846a47 Mon Sep 17 00:00:00 2001 From: Gusted Date: Thu, 28 Mar 2024 21:41:52 +0100 Subject: [PATCH 0024/3435] [FEAT] Configure if protected branch rule should apply to admins - Currently protected branch rules do not apply to admins, however in some cases (like in the case of Forgejo project) you might also want to apply these rules to admins to avoid accidental merges. - Add new option to configure this on a per-rule basis. - Adds integration tests. - Resolves #65 --- models/forgejo_migrations/migrate.go | 2 + models/forgejo_migrations/v1_22/v9.go | 15 ++++ models/git/protected_branch.go | 1 + modules/structs/repo_branch.go | 3 + options/locale/locale_en-US.ini | 3 + routers/api/v1/repo/branch.go | 5 ++ routers/private/hook_pre_receive.go | 16 ++-- routers/web/repo/setting/protected_branch.go | 1 + services/convert/convert.go | 1 + services/forms/repo_form.go | 1 + services/pull/check.go | 7 +- services/pull/merge.go | 29 ++++--- templates/repo/issue/view_content/pull.tmpl | 2 +- templates/repo/settings/protected_branch.tmpl | 8 ++ templates/swagger/v1_json.tmpl | 12 +++ tests/integration/proctected_branch_test.go | 87 +++++++++++++++++++ 16 files changed, 167 insertions(+), 26 deletions(-) create mode 100644 models/forgejo_migrations/v1_22/v9.go create mode 100644 tests/integration/proctected_branch_test.go diff --git a/models/forgejo_migrations/migrate.go b/models/forgejo_migrations/migrate.go index c2ffda5eb7..965b748ac9 100644 --- a/models/forgejo_migrations/migrate.go +++ b/models/forgejo_migrations/migrate.go @@ -54,6 +54,8 @@ var migrations = []*Migration{ NewMigration("Add the `enable_repo_unit_hints` column to the `user` table", forgejo_v1_22.AddUserRepoUnitHintsSetting), // v7 -> v8 NewMigration("Modify the `release`.`note` content to remove SSH signatures", forgejo_v1_22.RemoveSSHSignaturesFromReleaseNotes), + // v8 -> v9 + NewMigration("Add the `apply_to_admins` column to the `protected_branch` table", forgejo_v1_22.AddApplyToAdminsSetting), } // GetCurrentDBVersion returns the current Forgejo database version. diff --git a/models/forgejo_migrations/v1_22/v9.go b/models/forgejo_migrations/v1_22/v9.go new file mode 100644 index 0000000000..34c2844c39 --- /dev/null +++ b/models/forgejo_migrations/v1_22/v9.go @@ -0,0 +1,15 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_22 //nolint + +import "xorm.io/xorm" + +func AddApplyToAdminsSetting(x *xorm.Engine) error { + type ProtectedBranch struct { + ID int64 `xorm:"pk autoincr"` + ApplyToAdmins bool `xorm:"NOT NULL DEFAULT false"` + } + + return x.Sync(&ProtectedBranch{}) +} diff --git a/models/git/protected_branch.go b/models/git/protected_branch.go index e0ff4d1542..a8b8c81bbe 100644 --- a/models/git/protected_branch.go +++ b/models/git/protected_branch.go @@ -58,6 +58,7 @@ type ProtectedBranch struct { RequireSignedCommits bool `xorm:"NOT NULL DEFAULT false"` ProtectedFilePatterns string `xorm:"TEXT"` UnprotectedFilePatterns string `xorm:"TEXT"` + ApplyToAdmins bool `xorm:"NOT NULL DEFAULT false"` CreatedUnix timeutil.TimeStamp `xorm:"created"` UpdatedUnix timeutil.TimeStamp `xorm:"updated"` diff --git a/modules/structs/repo_branch.go b/modules/structs/repo_branch.go index e96d276b29..0b3b0bb030 100644 --- a/modules/structs/repo_branch.go +++ b/modules/structs/repo_branch.go @@ -47,6 +47,7 @@ type BranchProtection struct { RequireSignedCommits bool `json:"require_signed_commits"` ProtectedFilePatterns string `json:"protected_file_patterns"` UnprotectedFilePatterns string `json:"unprotected_file_patterns"` + ApplyToAdmins bool `json:"apply_to_admins"` // swagger:strfmt date-time Created time.Time `json:"created_at"` // swagger:strfmt date-time @@ -80,6 +81,7 @@ type CreateBranchProtectionOption struct { RequireSignedCommits bool `json:"require_signed_commits"` ProtectedFilePatterns string `json:"protected_file_patterns"` UnprotectedFilePatterns string `json:"unprotected_file_patterns"` + ApplyToAdmins bool `json:"apply_to_admins"` } // EditBranchProtectionOption options for editing a branch protection @@ -106,4 +108,5 @@ type EditBranchProtectionOption struct { RequireSignedCommits *bool `json:"require_signed_commits"` ProtectedFilePatterns *string `json:"protected_file_patterns"` UnprotectedFilePatterns *string `json:"unprotected_file_patterns"` + ApplyToAdmins *bool `json:"apply_to_admins"` } diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 17a8180ec9..5042e5467e 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -2348,6 +2348,7 @@ settings.event_pull_request_review_request = Pull request review requested settings.event_pull_request_review_request_desc = Pull request review requested or review request removed. settings.event_pull_request_approvals = Pull request approvals settings.event_pull_request_merge = Pull request merge +settings.event_pull_request_enforcement = Enforcement settings.event_package = Package settings.event_package_desc = Package created or deleted in a repository. settings.branch_filter = Branch filter @@ -2462,6 +2463,8 @@ settings.block_on_official_review_requests = Block merge on official review requ settings.block_on_official_review_requests_desc = Merging will not be possible when it has official review requests, even if there are enough approvals. settings.block_outdated_branch = Block merge if pull request is outdated settings.block_outdated_branch_desc = Merging will not be possible when head branch is behind base branch. +settings.enforce_on_admins = Enforce this rule for repository admins +settings.enforce_on_admins_desc = Repository admins cannot bypass this rule. settings.default_branch_desc = Select a default repository branch for pull requests and code commits: settings.merge_style_desc = Merge styles settings.default_merge_style_desc = Default merge style diff --git a/routers/api/v1/repo/branch.go b/routers/api/v1/repo/branch.go index 5e6b6a8658..c33beee0ae 100644 --- a/routers/api/v1/repo/branch.go +++ b/routers/api/v1/repo/branch.go @@ -621,6 +621,7 @@ func CreateBranchProtection(ctx *context.APIContext) { ProtectedFilePatterns: form.ProtectedFilePatterns, UnprotectedFilePatterns: form.UnprotectedFilePatterns, BlockOnOutdatedBranch: form.BlockOnOutdatedBranch, + ApplyToAdmins: form.ApplyToAdmins, } err = git_model.UpdateProtectBranch(ctx, ctx.Repo.Repository, protectBranch, git_model.WhitelistOptions{ @@ -808,6 +809,10 @@ func EditBranchProtection(ctx *context.APIContext) { protectBranch.BlockOnOutdatedBranch = *form.BlockOnOutdatedBranch } + if form.ApplyToAdmins != nil { + protectBranch.ApplyToAdmins = *form.ApplyToAdmins + } + var whitelistUsers []int64 if form.PushWhitelistUsernames != nil { whitelistUsers, err = user_model.GetUserIDsByNames(ctx, form.PushWhitelistUsernames, false) diff --git a/routers/private/hook_pre_receive.go b/routers/private/hook_pre_receive.go index f45e57b9e3..0613492845 100644 --- a/routers/private/hook_pre_receive.go +++ b/routers/private/hook_pre_receive.go @@ -337,13 +337,9 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID string, r return } - // If we're an admin for the repository we can ignore status checks, reviews and override protected files - if ctx.userPerm.IsAdmin() { - return - } - - // Now if we're not an admin - we can't overwrite protected files so fail now - if changedProtectedfiles { + // It's not allowed t overwrite protected files. Unless if the user is an + // admin and the protected branch rule doesn't apply to admins. + if changedProtectedfiles && (!ctx.user.IsAdmin || protectBranch.ApplyToAdmins) { log.Warn("Forbidden: Branch: %s in %-v is protected from changing file %s", branchName, repo, protectedFilePath) ctx.JSON(http.StatusForbidden, private.Response{ UserMsg: fmt.Sprintf("branch %s is protected from changing file %s", branchName, protectedFilePath), @@ -352,8 +348,12 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID string, r } // Check all status checks and reviews are ok - if err := pull_service.CheckPullBranchProtections(ctx, pr, true); err != nil { + if pb, err := pull_service.CheckPullBranchProtections(ctx, pr, true); err != nil { if models.IsErrDisallowedToMerge(err) { + // Allow this if the rule doesn't apply to admins and the user is an admin. + if ctx.user.IsAdmin && !pb.ApplyToAdmins { + return + } log.Warn("Forbidden: User %d is not allowed push to protected branch %s in %-v and pr #%d is not ready to be merged: %s", ctx.opts.UserID, branchName, repo, pr.Index, err.Error()) ctx.JSON(http.StatusForbidden, private.Response{ UserMsg: fmt.Sprintf("Not allowed to push to protected branch %s and pr #%d is not ready to be merged: %s", branchName, ctx.opts.PullRequestID, err.Error()), diff --git a/routers/web/repo/setting/protected_branch.go b/routers/web/repo/setting/protected_branch.go index 7ee67e5925..25146779de 100644 --- a/routers/web/repo/setting/protected_branch.go +++ b/routers/web/repo/setting/protected_branch.go @@ -237,6 +237,7 @@ func SettingsProtectedBranchPost(ctx *context.Context) { protectBranch.ProtectedFilePatterns = f.ProtectedFilePatterns protectBranch.UnprotectedFilePatterns = f.UnprotectedFilePatterns protectBranch.BlockOnOutdatedBranch = f.BlockOnOutdatedBranch + protectBranch.ApplyToAdmins = f.ApplyToAdmins err = git_model.UpdateProtectBranch(ctx, ctx.Repo.Repository, protectBranch, git_model.WhitelistOptions{ UserIDs: whitelistUsers, diff --git a/services/convert/convert.go b/services/convert/convert.go index ca3ec32a40..dd2239458e 100644 --- a/services/convert/convert.go +++ b/services/convert/convert.go @@ -162,6 +162,7 @@ func ToBranchProtection(ctx context.Context, bp *git_model.ProtectedBranch) *api RequireSignedCommits: bp.RequireSignedCommits, ProtectedFilePatterns: bp.ProtectedFilePatterns, UnprotectedFilePatterns: bp.UnprotectedFilePatterns, + ApplyToAdmins: bp.ApplyToAdmins, Created: bp.CreatedUnix.AsTime(), Updated: bp.UpdatedUnix.AsTime(), } diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index 0f7665804d..b5ff031f4b 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -219,6 +219,7 @@ type ProtectBranchForm struct { RequireSignedCommits bool ProtectedFilePatterns string UnprotectedFilePatterns string + ApplyToAdmins bool } // Validate validates the fields diff --git a/services/pull/check.go b/services/pull/check.go index f4dd332b14..9aab3c94f3 100644 --- a/services/pull/check.go +++ b/services/pull/check.go @@ -104,7 +104,7 @@ func CheckPullMergable(stdCtx context.Context, doer *user_model.User, perm *acce return ErrIsChecking } - if err := CheckPullBranchProtections(ctx, pr, false); err != nil { + if pb, err := CheckPullBranchProtections(ctx, pr, false); err != nil { if !models.IsErrDisallowedToMerge(err) { log.Error("Error whilst checking pull branch protection for %-v: %v", pr, err) return err @@ -117,8 +117,9 @@ func CheckPullMergable(stdCtx context.Context, doer *user_model.User, perm *acce err = nil } - // * if the doer is admin, they could skip the branch protection check - if adminSkipProtectionCheck { + // * if the doer is admin, they could skip the branch protection check, + // if that's allowed by the protected branch rule. + if adminSkipProtectionCheck && !pb.ApplyToAdmins { if isRepoAdmin, errCheckAdmin := access_model.IsUserRepoAdmin(ctx, pr.BaseRepo, doer); errCheckAdmin != nil { log.Error("Unable to check if %-v is a repo admin in %-v: %v", doer, pr.BaseRepo, errCheckAdmin) return errCheckAdmin diff --git a/services/pull/merge.go b/services/pull/merge.go index df8d66e2d4..7f79eca2aa 100644 --- a/services/pull/merge.go +++ b/services/pull/merge.go @@ -424,63 +424,64 @@ func IsUserAllowedToMerge(ctx context.Context, pr *issues_model.PullRequest, p a return false, nil } -// CheckPullBranchProtections checks whether the PR is ready to be merged (reviews and status checks) -func CheckPullBranchProtections(ctx context.Context, pr *issues_model.PullRequest, skipProtectedFilesCheck bool) (err error) { +// CheckPullBranchProtections checks whether the PR is ready to be merged (reviews and status checks). +// Returns the protected branch rule when `ErrDisallowedToMerge` is returned as error. +func CheckPullBranchProtections(ctx context.Context, pr *issues_model.PullRequest, skipProtectedFilesCheck bool) (protectedBranchRule *git_model.ProtectedBranch, err error) { if err = pr.LoadBaseRepo(ctx); err != nil { - return fmt.Errorf("LoadBaseRepo: %w", err) + return nil, fmt.Errorf("LoadBaseRepo: %w", err) } pb, err := git_model.GetFirstMatchProtectedBranchRule(ctx, pr.BaseRepoID, pr.BaseBranch) if err != nil { - return fmt.Errorf("LoadProtectedBranch: %v", err) + return nil, fmt.Errorf("LoadProtectedBranch: %v", err) } if pb == nil { - return nil + return nil, nil } isPass, err := IsPullCommitStatusPass(ctx, pr) if err != nil { - return err + return nil, err } if !isPass { - return models.ErrDisallowedToMerge{ + return pb, models.ErrDisallowedToMerge{ Reason: "Not all required status checks successful", } } if !issues_model.HasEnoughApprovals(ctx, pb, pr) { - return models.ErrDisallowedToMerge{ + return pb, models.ErrDisallowedToMerge{ Reason: "Does not have enough approvals", } } if issues_model.MergeBlockedByRejectedReview(ctx, pb, pr) { - return models.ErrDisallowedToMerge{ + return pb, models.ErrDisallowedToMerge{ Reason: "There are requested changes", } } if issues_model.MergeBlockedByOfficialReviewRequests(ctx, pb, pr) { - return models.ErrDisallowedToMerge{ + return pb, models.ErrDisallowedToMerge{ Reason: "There are official review requests", } } if issues_model.MergeBlockedByOutdatedBranch(pb, pr) { - return models.ErrDisallowedToMerge{ + return pb, models.ErrDisallowedToMerge{ Reason: "The head branch is behind the base branch", } } if skipProtectedFilesCheck { - return nil + return nil, nil } if pb.MergeBlockedByProtectedFiles(pr.ChangedProtectedFiles) { - return models.ErrDisallowedToMerge{ + return pb, models.ErrDisallowedToMerge{ Reason: "Changed protected files", } } - return nil + return nil, nil } // MergedManually mark pr as merged manually diff --git a/templates/repo/issue/view_content/pull.tmpl b/templates/repo/issue/view_content/pull.tmpl index 19f1d3f91d..08f666d210 100644 --- a/templates/repo/issue/view_content/pull.tmpl +++ b/templates/repo/issue/view_content/pull.tmpl @@ -158,7 +158,7 @@ {{$notAllOverridableChecksOk := or .IsBlockedByApprovals .IsBlockedByRejection .IsBlockedByOfficialReviewRequests .IsBlockedByOutdatedBranch .IsBlockedByChangedProtectedFiles (and .EnableStatusCheck (not .RequiredStatusCheckState.IsSuccess))}} {{/* admin can merge without checks, writer can merge when checks succeed */}} - {{$canMergeNow := and (or $.IsRepoAdmin (not $notAllOverridableChecksOk)) (or (not .AllowMerge) (not .RequireSigned) .WillSign)}} + {{$canMergeNow := and (or (and $.IsRepoAdmin (not .ProtectedBranch.ApplyToAdmins)) (not $notAllOverridableChecksOk)) (or (not .AllowMerge) (not .RequireSigned) .WillSign)}} {{/* admin and writer both can make an auto merge schedule */}} {{if $canMergeNow}} diff --git a/templates/repo/settings/protected_branch.tmpl b/templates/repo/settings/protected_branch.tmpl index e95dd831c9..e1ee7b36f5 100644 --- a/templates/repo/settings/protected_branch.tmpl +++ b/templates/repo/settings/protected_branch.tmpl @@ -260,6 +260,14 @@

{{ctx.Locale.Tr "repo.settings.block_outdated_branch_desc"}}

+
{{ctx.Locale.Tr "repo.settings.event_pull_request_enforcement"}}
+
+
+ + +

{{ctx.Locale.Tr "repo.settings.enforce_on_admins_desc"}}

+
+
diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 6034a8fbce..ee1ed64eaf 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -17756,6 +17756,10 @@ "description": "BranchProtection represents a branch protection for a repository", "type": "object", "properties": { + "apply_to_admins": { + "type": "boolean", + "x-go-name": "ApplyToAdmins" + }, "approvals_whitelist_teams": { "type": "array", "items": { @@ -18406,6 +18410,10 @@ "description": "CreateBranchProtectionOption options for creating a branch protection", "type": "object", "properties": { + "apply_to_admins": { + "type": "boolean", + "x-go-name": "ApplyToAdmins" + }, "approvals_whitelist_teams": { "type": "array", "items": { @@ -19577,6 +19585,10 @@ "description": "EditBranchProtectionOption options for editing a branch protection", "type": "object", "properties": { + "apply_to_admins": { + "type": "boolean", + "x-go-name": "ApplyToAdmins" + }, "approvals_whitelist_teams": { "type": "array", "items": { diff --git a/tests/integration/proctected_branch_test.go b/tests/integration/proctected_branch_test.go new file mode 100644 index 0000000000..9c6e5e3cae --- /dev/null +++ b/tests/integration/proctected_branch_test.go @@ -0,0 +1,87 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package integration + +import ( + "net/http" + "net/url" + "strconv" + "strings" + "testing" + + git_model "code.gitea.io/gitea/models/git" + repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/models/unittest" + "code.gitea.io/gitea/tests" + + "github.com/stretchr/testify/assert" +) + +func TestProtectedBranch_AdminEnforcement(t *testing.T) { + onGiteaRun(t, func(t *testing.T, u *url.URL) { + session := loginUser(t, "user1") + testRepoFork(t, session, "user2", "repo1", "user1", "repo1") + testEditFileToNewBranch(t, session, "user1", "repo1", "master", "add-readme", "README.md", "WIP") + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: 1, Name: "repo1"}) + + req := NewRequestWithValues(t, "POST", "user1/repo1/compare/master...add-readme", map[string]string{ + "_csrf": GetCSRF(t, session, "user1/repo1/compare/master...add-readme"), + "title": "pull request", + }) + session.MakeRequest(t, req, http.StatusOK) + + t.Run("No protected branch", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req = NewRequest(t, "GET", "/user1/repo1/pulls/1") + resp := session.MakeRequest(t, req, http.StatusOK) + doc := NewHTMLParser(t, resp.Body) + + text := strings.TrimSpace(doc.doc.Find(".merge-section").Text()) + assert.Contains(t, text, "This pull request can be merged automatically.") + assert.Contains(t, text, "'canMergeNow': true") + }) + + t.Run("Without admin enforcement", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req := NewRequestWithValues(t, "POST", "/user1/repo1/settings/branches/edit", map[string]string{ + "_csrf": GetCSRF(t, session, "/user1/repo1/settings/branches/edit"), + "rule_name": "master", + "required_approvals": "1", + }) + session.MakeRequest(t, req, http.StatusSeeOther) + + req = NewRequest(t, "GET", "/user1/repo1/pulls/1") + resp := session.MakeRequest(t, req, http.StatusOK) + doc := NewHTMLParser(t, resp.Body) + + text := strings.TrimSpace(doc.doc.Find(".merge-section").Text()) + assert.Contains(t, text, "This pull request doesn't have enough approvals yet. 0 of 1 approvals granted.") + assert.Contains(t, text, "'canMergeNow': true") + }) + + t.Run("With admin enforcement", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + protectedBranch := unittest.AssertExistsAndLoadBean(t, &git_model.ProtectedBranch{RuleName: "master", RepoID: repo.ID}) + req := NewRequestWithValues(t, "POST", "/user1/repo1/settings/branches/edit", map[string]string{ + "_csrf": GetCSRF(t, session, "/user1/repo1/settings/branches/edit"), + "rule_name": "master", + "rule_id": strconv.FormatInt(protectedBranch.ID, 10), + "required_approvals": "1", + "apply_to_admins": "true", + }) + session.MakeRequest(t, req, http.StatusSeeOther) + + req = NewRequest(t, "GET", "/user1/repo1/pulls/1") + resp := session.MakeRequest(t, req, http.StatusOK) + doc := NewHTMLParser(t, resp.Body) + + text := strings.TrimSpace(doc.doc.Find(".merge-section").Text()) + assert.Contains(t, text, "This pull request doesn't have enough approvals yet. 0 of 1 approvals granted.") + assert.Contains(t, text, "'canMergeNow': false") + }) + }) +} From 39b53ef56f5d0d0cef6ff5a9a1204b04eb7f05e7 Mon Sep 17 00:00:00 2001 From: 0ko <0ko@noreply.codeberg.org> Date: Tue, 19 Mar 2024 16:27:01 +0500 Subject: [PATCH 0025/3435] Provide plural support for issue participants --- options/locale/locale_ar.ini | 2 +- options/locale/locale_bg.ini | 2 +- options/locale/locale_cs-CZ.ini | 2 +- options/locale/locale_de-DE.ini | 2 +- options/locale/locale_el-GR.ini | 2 +- options/locale/locale_en-US.ini | 3 ++- options/locale/locale_es-ES.ini | 2 +- options/locale/locale_fa-IR.ini | 2 +- options/locale/locale_fi-FI.ini | 2 +- options/locale/locale_fr-FR.ini | 3 ++- options/locale/locale_hu-HU.ini | 2 +- options/locale/locale_id-ID.ini | 2 +- options/locale/locale_it-IT.ini | 2 +- options/locale/locale_ja-JP.ini | 2 +- options/locale/locale_ko-KR.ini | 2 +- options/locale/locale_lv-LV.ini | 2 +- options/locale/locale_nl-NL.ini | 2 +- options/locale/locale_pl-PL.ini | 2 +- options/locale/locale_pt-BR.ini | 2 +- options/locale/locale_pt-PT.ini | 2 +- options/locale/locale_ru-RU.ini | 3 ++- options/locale/locale_si-LK.ini | 2 +- options/locale/locale_sv-SE.ini | 2 +- options/locale/locale_tr-TR.ini | 2 +- options/locale/locale_uk-UA.ini | 2 +- options/locale/locale_zh-CN.ini | 2 +- options/locale/locale_zh-HK.ini | 2 +- options/locale/locale_zh-TW.ini | 2 +- templates/repo/issue/view_content/sidebar.tmpl | 2 +- 29 files changed, 32 insertions(+), 29 deletions(-) diff --git a/options/locale/locale_ar.ini b/options/locale/locale_ar.ini index 38e18bec5a..4e0c617907 100644 --- a/options/locale/locale_ar.ini +++ b/options/locale/locale_ar.ini @@ -680,7 +680,7 @@ issues.self_assign_at = `كلّف نفسه بها %s` issues.label_deletion = احذف التصنيف issues.filter_milestone_all = كل الأهداف issues.unlock.notice_2 = - يمكنك دوما إقفال هذه المسألة من جديد في المستقبل. -issues.num_participants = %d متحاور +issues.num_participants_few = %d متحاور release.title = عنوان الإصدار issues.closed_at = `أغلق هذه المسألة %[2]s` issues.lock.title = إقفال التحاور في هذه المسألة. diff --git a/options/locale/locale_bg.ini b/options/locale/locale_bg.ini index 5b9bb0715e..cea90012b1 100644 --- a/options/locale/locale_bg.ini +++ b/options/locale/locale_bg.ini @@ -629,7 +629,7 @@ issues.filter_milestone_all = Всички етапи issues.filter_milestone_open = Отворени етапи issues.filter_milestone_none = Без етапи issues.filter_project = Проект -issues.num_participants = %d участващи +issues.num_participants_few = %d участващи issues.filter_assignee = Изпълнител issues.filter_milestone_closed = Затворени етапи issues.filter_assginee_no_select = Всички изпълнители diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini index 3747ae28c9..03ee22944f 100644 --- a/options/locale/locale_cs-CZ.ini +++ b/options/locale/locale_cs-CZ.ini @@ -1603,7 +1603,7 @@ issues.label.filter_sort.alphabetically=Od začátku abecedy issues.label.filter_sort.reverse_alphabetically=Od konce abecedy issues.label.filter_sort.by_size=Nejmenší velikost issues.label.filter_sort.reverse_by_size=Největší velikost -issues.num_participants=%d účastníků +issues.num_participants_few=%d účastníků issues.attachment.open_tab=`Klikněte pro zobrazení „%s“ v nové záložce` issues.attachment.download=`Klikněte pro stažení „%s“` issues.subscribe=Odebírat diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index f5aafdf5c9..1bdf6c4d0b 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -1596,7 +1596,7 @@ issues.label.filter_sort.alphabetically=Alphabetisch issues.label.filter_sort.reverse_alphabetically=Umgekehrt alphabetisch issues.label.filter_sort.by_size=Kleinste Größe issues.label.filter_sort.reverse_by_size=Größte Größe -issues.num_participants=%d Beteiligte +issues.num_participants_few=%d Beteiligte issues.attachment.open_tab=`Klicken, um „%s“ in einem neuen Tab zu öffnen` issues.attachment.download=`Klicken, um „%s“ herunterzuladen` issues.subscribe=Abonnieren diff --git a/options/locale/locale_el-GR.ini b/options/locale/locale_el-GR.ini index 36b7518c62..17362a6036 100644 --- a/options/locale/locale_el-GR.ini +++ b/options/locale/locale_el-GR.ini @@ -1580,7 +1580,7 @@ issues.label.filter_sort.alphabetically=Αλφαβητικά issues.label.filter_sort.reverse_alphabetically=Αντίστροφα αλφαβητικά issues.label.filter_sort.by_size=Μικρότερο μέγεθος issues.label.filter_sort.reverse_by_size=Μεγαλύτερο μέγεθος -issues.num_participants=%d Συμμετέχοντες +issues.num_participants_few=%d Συμμετέχοντες issues.attachment.open_tab=`Πατήστε εδώ για να ανοίξετε το «%s» σε μια νέα καρτέλα` issues.attachment.download=`Πατήστε εδώ για να κατεβάσετε το «%s»` issues.subscribe=Εγγραφή diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 17a8180ec9..bb7c716e49 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1636,7 +1636,8 @@ issues.label.filter_sort.alphabetically = Alphabetically issues.label.filter_sort.reverse_alphabetically = Reverse alphabetically issues.label.filter_sort.by_size = Smallest size issues.label.filter_sort.reverse_by_size = Largest size -issues.num_participants = %d participants +issues.num_participants_one = %d participant +issues.num_participants_few = %d participants issues.attachment.open_tab = `Click to see "%s" in a new tab` issues.attachment.download = `Click to download "%s"` issues.subscribe = Subscribe diff --git a/options/locale/locale_es-ES.ini b/options/locale/locale_es-ES.ini index 3dbca87381..78e06b8f09 100644 --- a/options/locale/locale_es-ES.ini +++ b/options/locale/locale_es-ES.ini @@ -1561,7 +1561,7 @@ issues.label.filter_sort.alphabetically=Alfabéticamente issues.label.filter_sort.reverse_alphabetically=Invertir alfabéticamente issues.label.filter_sort.by_size=Tamaño más pequeño issues.label.filter_sort.reverse_by_size=Tamaño más grande -issues.num_participants=%d participantes +issues.num_participants_few=%d participantes issues.attachment.open_tab='Haga clic para ver "%s" en una pestaña nueva' issues.attachment.download=`Haga clic para descargar "%s"` issues.subscribe=Suscribir diff --git a/options/locale/locale_fa-IR.ini b/options/locale/locale_fa-IR.ini index 598c1636dc..7f394176d0 100644 --- a/options/locale/locale_fa-IR.ini +++ b/options/locale/locale_fa-IR.ini @@ -1189,7 +1189,7 @@ issues.label.filter_sort.alphabetically=الفبایی issues.label.filter_sort.reverse_alphabetically=برعکس ترتیب الفبا issues.label.filter_sort.by_size=کوچکترین اندازه issues.label.filter_sort.reverse_by_size=بزرگترین اندازه -issues.num_participants=%d مشارکت کننده +issues.num_participants_few=%d مشارکت کننده issues.attachment.open_tab=برای مشاهده "%s" در زبانه جدید، کلیک کنید issues.attachment.download=`برای دریافت "%s" کلیک کنید` issues.subscribe=مشترک شدن diff --git a/options/locale/locale_fi-FI.ini b/options/locale/locale_fi-FI.ini index c6c64ad6ce..60b6e93f00 100644 --- a/options/locale/locale_fi-FI.ini +++ b/options/locale/locale_fi-FI.ini @@ -920,7 +920,7 @@ issues.label.filter_sort.alphabetically=Aakkosjärjestyksessä issues.label.filter_sort.reverse_alphabetically=Käänteisessä aakkosjärjestyksessä issues.label.filter_sort.by_size=Pienin koko issues.label.filter_sort.reverse_by_size=Suurin koko -issues.num_participants=%d osallistujaa +issues.num_participants_few=%d osallistujaa issues.subscribe=Tilaa issues.unsubscribe=Lopeta tilaus issues.lock=Lukitse keskustelu diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini index 659615e6ff..fe527fe3d3 100644 --- a/options/locale/locale_fr-FR.ini +++ b/options/locale/locale_fr-FR.ini @@ -1599,7 +1599,7 @@ issues.label.filter_sort.alphabetically=Par ordre alphabétique issues.label.filter_sort.reverse_alphabetically=Par ordre alphabétique inversé issues.label.filter_sort.by_size=Plus petite taille issues.label.filter_sort.reverse_by_size=Plus grande taille -issues.num_participants=%d participants +issues.num_participants_few=%d participants issues.attachment.open_tab=`Cliquez ici pour voir « %s » dans un nouvel onglet.` issues.attachment.download=`Cliquez pour télécharger « %s ».` issues.subscribe=S’abonner @@ -3754,6 +3754,7 @@ component_failed_to_load = Une erreur inattendue s'est produite. contributors.what = contributions component_loading = Chargement %s... component_loading_failed = Échec de chargement de %s + code_frequency.what = fŕequence de code recent_commits.what = commits récents diff --git a/options/locale/locale_hu-HU.ini b/options/locale/locale_hu-HU.ini index 3764f6fa61..5be3fa9c6f 100644 --- a/options/locale/locale_hu-HU.ini +++ b/options/locale/locale_hu-HU.ini @@ -857,7 +857,7 @@ issues.label.filter_sort.alphabetically=Betűrendben issues.label.filter_sort.reverse_alphabetically=Fordított betűrendben issues.label.filter_sort.by_size=Legkisebb méret issues.label.filter_sort.reverse_by_size=Legnagyobb méret -issues.num_participants=%d Résztvevő +issues.num_participants_few=%d Résztvevő issues.attachment.open_tab=`A(z) "%s" megnyitása új fülön` issues.attachment.download=`Kattintson a(z) "%s" letöltéséhez` issues.subscribe=Feliratkozás diff --git a/options/locale/locale_id-ID.ini b/options/locale/locale_id-ID.ini index 574063bcaa..3ab9991f4a 100644 --- a/options/locale/locale_id-ID.ini +++ b/options/locale/locale_id-ID.ini @@ -729,7 +729,7 @@ issues.label_edit=Sunting issues.label_delete=Hapus issues.label.filter_sort.alphabetically=Urutan abjad issues.label.filter_sort.reverse_alphabetically=Membalikkan menurut abjad -issues.num_participants=%d peserta +issues.num_participants_few=%d peserta issues.attachment.open_tab=`Klik untuk melihat "%s" di tab baru` issues.attachment.download=`Klik untuk mengunduh "%s"` issues.subscribe=Berlangganan diff --git a/options/locale/locale_it-IT.ini b/options/locale/locale_it-IT.ini index dc6f4c127e..cb30d51e2f 100644 --- a/options/locale/locale_it-IT.ini +++ b/options/locale/locale_it-IT.ini @@ -1478,7 +1478,7 @@ issues.label.filter_sort.alphabetically=In ordine alfabetico issues.label.filter_sort.reverse_alphabetically=In ordine alfabetico inverso issues.label.filter_sort.by_size=Dimensione più piccola issues.label.filter_sort.reverse_by_size=Dimensione più grande -issues.num_participants=%d partecipanti +issues.num_participants_few=%d partecipanti issues.attachment.open_tab=`Clicca per vedere "%s" in una nuova scheda` issues.attachment.download=`Clicca qui per scaricare "%s"` issues.subscribe=Iscriviti diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini index 7f3e1ded36..4774ef78cb 100644 --- a/options/locale/locale_ja-JP.ini +++ b/options/locale/locale_ja-JP.ini @@ -1587,7 +1587,7 @@ issues.label.filter_sort.alphabetically=アルファベット順 issues.label.filter_sort.reverse_alphabetically=逆アルファベット順 issues.label.filter_sort.by_size=サイズの小さい順 issues.label.filter_sort.reverse_by_size=サイズの大きい順 -issues.num_participants=%d 人の参加者 +issues.num_participants_few=%d 人の参加者 issues.attachment.open_tab=`クリックして新しいタブで "%s" を見る` issues.attachment.download=`クリックして "%s" をダウンロード` issues.subscribe=購読する diff --git a/options/locale/locale_ko-KR.ini b/options/locale/locale_ko-KR.ini index fa6df2d2a1..0903f5eb3e 100644 --- a/options/locale/locale_ko-KR.ini +++ b/options/locale/locale_ko-KR.ini @@ -782,7 +782,7 @@ issues.label_deletion_desc=라벨을 삭제하면 모든 이슈로부터도 삭 issues.label_deletion_success=라벨이 삭제되었습니다. issues.label.filter_sort.alphabetically=알파벳순 issues.label.filter_sort.reverse_alphabetically=이름 역순으로 정렬 -issues.num_participants=참여자 %d명 +issues.num_participants_few=참여자 %d명 issues.attachment.open_tab=`클릭하여 "%s" 새탭으로 보기` issues.attachment.download=' "%s"를 다운로드 하려면 클릭 하십시오 ' issues.subscribe=구독하기 diff --git a/options/locale/locale_lv-LV.ini b/options/locale/locale_lv-LV.ini index ce8f05e4b5..587143c9c3 100644 --- a/options/locale/locale_lv-LV.ini +++ b/options/locale/locale_lv-LV.ini @@ -1540,7 +1540,7 @@ issues.label.filter_sort.alphabetically=Alfabētiski issues.label.filter_sort.reverse_alphabetically=Pretēji alfabētiski issues.label.filter_sort.by_size=Mazākais izmērs issues.label.filter_sort.reverse_by_size=Lielākais izmērs -issues.num_participants=%d dalībnieki +issues.num_participants_few=%d dalībnieki issues.attachment.open_tab=`Noklikšķiniet, lai apskatītos "%s" jaunā logā` issues.attachment.download=`Noklikšķiniet, lai lejupielādētu "%s"` issues.subscribe=Abonēt diff --git a/options/locale/locale_nl-NL.ini b/options/locale/locale_nl-NL.ini index 4358cc1e81..d5071d98bf 100644 --- a/options/locale/locale_nl-NL.ini +++ b/options/locale/locale_nl-NL.ini @@ -1482,7 +1482,7 @@ issues.label.filter_sort.alphabetically=Alfabetisch issues.label.filter_sort.reverse_alphabetically=Omgekeerd alfabetisch issues.label.filter_sort.by_size=Kleinste grootte issues.label.filter_sort.reverse_by_size=Grootste grootte -issues.num_participants=%d deelnemers +issues.num_participants_few=%d deelnemers issues.attachment.open_tab=`Klik om "%s" in een nieuw tabblad te bekijken` issues.attachment.download=`Klik om "%s" te downloaden` issues.subscribe=Abonneren diff --git a/options/locale/locale_pl-PL.ini b/options/locale/locale_pl-PL.ini index a254a912bd..f23f68f7c8 100644 --- a/options/locale/locale_pl-PL.ini +++ b/options/locale/locale_pl-PL.ini @@ -1179,7 +1179,7 @@ issues.label.filter_sort.alphabetically=Alfabetycznie issues.label.filter_sort.reverse_alphabetically=Alfabetycznie odwrotnie issues.label.filter_sort.by_size=Najmniejszy rozmiar issues.label.filter_sort.reverse_by_size=Największy rozmiar -issues.num_participants=Uczestnicy %d +issues.num_participants_few=Uczestnicy %d issues.attachment.open_tab=`Kliknij, aby zobaczyć "%s" w nowej karcie` issues.attachment.download=`Kliknij, aby pobrać "%s"` issues.subscribe=Subskrybuj diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini index 086cea74fa..ac30ff3e74 100644 --- a/options/locale/locale_pt-BR.ini +++ b/options/locale/locale_pt-BR.ini @@ -1552,7 +1552,7 @@ issues.label.filter_sort.alphabetically=Alfabeticamente issues.label.filter_sort.reverse_alphabetically=Alfabeticamente inverso issues.label.filter_sort.by_size=Menor tamanho issues.label.filter_sort.reverse_by_size=Maior tamanho -issues.num_participants=%d participante(s) +issues.num_participants_few=%d participante(s) issues.attachment.open_tab=`Clique para ver "%s" em uma nova aba` issues.attachment.download=`Clique para baixar "%s"` issues.subscribe=Inscrever-se diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini index 3538ac9460..09b3fc3153 100644 --- a/options/locale/locale_pt-PT.ini +++ b/options/locale/locale_pt-PT.ini @@ -1548,7 +1548,7 @@ issues.label.filter_sort.alphabetically=por ordem alfabética issues.label.filter_sort.reverse_alphabetically=por ordem alfabética inversa issues.label.filter_sort.by_size=Menor tamanho issues.label.filter_sort.reverse_by_size=Maior tamanho -issues.num_participants=%d Participantes +issues.num_participants_few=%d Participantes issues.attachment.open_tab=`Clique para ver "%s" num separador novo` issues.attachment.download=`Clique para descarregar "%s"` issues.subscribe=Subscrever diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini index addc2f8110..34321b18f9 100644 --- a/options/locale/locale_ru-RU.ini +++ b/options/locale/locale_ru-RU.ini @@ -1581,7 +1581,8 @@ issues.label.filter_sort.alphabetically=По алфавиту issues.label.filter_sort.reverse_alphabetically=С конца алфавита issues.label.filter_sort.by_size=Меньший размер issues.label.filter_sort.reverse_by_size=Больший размер -issues.num_participants=%d участвующих +issues.num_participants_one=%d участник +issues.num_participants_few=%d участников issues.attachment.open_tab=`Нажмите, чтобы увидеть «%s» в новой вкладке` issues.attachment.download=`Нажмите, чтобы скачать «%s»` issues.subscribe=Подписаться diff --git a/options/locale/locale_si-LK.ini b/options/locale/locale_si-LK.ini index 7cb1768d22..ac837173e4 100644 --- a/options/locale/locale_si-LK.ini +++ b/options/locale/locale_si-LK.ini @@ -1132,7 +1132,7 @@ issues.label.filter_sort.alphabetically=අකාරාදී issues.label.filter_sort.reverse_alphabetically=අකාරාදී ප්රතිවිකුණුම් issues.label.filter_sort.by_size=කුඩාම ප්‍රමාණය issues.label.filter_sort.reverse_by_size=විශාලම ප්‍රමාණය -issues.num_participants=සහභාගිවන්නන් %d +issues.num_participants_few=සහභාගිවන්නන් %d issues.attachment.open_tab=`නව වගුවක "%s" බැලීමට ක්ලික් කරන්න` issues.attachment.download=`"%s" බාගැනීමට ඔබන්න` issues.subscribe=දායක වන්න diff --git a/options/locale/locale_sv-SE.ini b/options/locale/locale_sv-SE.ini index 38382a6f66..e7cb248aaa 100644 --- a/options/locale/locale_sv-SE.ini +++ b/options/locale/locale_sv-SE.ini @@ -972,7 +972,7 @@ issues.label.filter_sort.alphabetically=Alfabetiskt A-Ö issues.label.filter_sort.reverse_alphabetically=Alfabetiskt Ö-A issues.label.filter_sort.by_size=Minsta storlek issues.label.filter_sort.reverse_by_size=Största storlek -issues.num_participants=%d Deltagare +issues.num_participants_few=%d Deltagare issues.attachment.open_tab=`Klicka för att se "%s" i en ny flik` issues.attachment.download=`Klicka för att hämta "%s"` issues.subscribe=Prenumerera diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini index 74ef77eb19..fa8a1b687f 100644 --- a/options/locale/locale_tr-TR.ini +++ b/options/locale/locale_tr-TR.ini @@ -1539,7 +1539,7 @@ issues.label.filter_sort.alphabetically=Alfabetik issues.label.filter_sort.reverse_alphabetically=Ters alfabetik issues.label.filter_sort.by_size=En küçük boyut issues.label.filter_sort.reverse_by_size=En büyük boyut -issues.num_participants=%d Katılımcı +issues.num_participants_few=%d Katılımcı issues.attachment.open_tab=`Yeni bir sekmede "%s" görmek için tıkla` issues.attachment.download=`"%s" indirmek için tıkla` issues.subscribe=Abone Ol diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini index 0a79e54010..d1040ac2b5 100644 --- a/options/locale/locale_uk-UA.ini +++ b/options/locale/locale_uk-UA.ini @@ -1248,7 +1248,7 @@ issues.label.filter_sort.alphabetically=За алфавітом issues.label.filter_sort.reverse_alphabetically=З кінця алфавіту issues.label.filter_sort.by_size=Найменший розмір issues.label.filter_sort.reverse_by_size=Найбільший розмір -issues.num_participants=%d учасників +issues.num_participants_few=%d учасників issues.attachment.open_tab=`Натисніть щоб побачити "%s" у новій вкладці` issues.attachment.download=`Натисніть щоб завантажити "%s"` issues.subscribe=Підписатися diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index 4ca2e70f21..b0e3b383b6 100644 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -1597,7 +1597,7 @@ issues.label.filter_sort.alphabetically=按字母顺序排序 issues.label.filter_sort.reverse_alphabetically=按字母逆序排序 issues.label.filter_sort.by_size=最小尺寸 issues.label.filter_sort.reverse_by_size=最大尺寸 -issues.num_participants=%d 名参与者 +issues.num_participants_few=%d 名参与者 issues.attachment.open_tab=`在新的标签页中查看 '%s'` issues.attachment.download=`点击下载 '%s'` issues.subscribe=订阅 diff --git a/options/locale/locale_zh-HK.ini b/options/locale/locale_zh-HK.ini index 5c1e234392..ab0a075d42 100644 --- a/options/locale/locale_zh-HK.ini +++ b/options/locale/locale_zh-HK.ini @@ -467,7 +467,7 @@ issues.label_edit=編輯 issues.label_delete=刪除 issues.label.filter_sort.alphabetically=按字母顺序排序 issues.label.filter_sort.reverse_alphabetically=按字母反向排序 -issues.num_participants=%d 參與者 +issues.num_participants_few=%d 參與者 issues.attachment.open_tab=`在新的標籤頁中查看 '%s'` issues.attachment.download=`點擊下載 '%s'` issues.subscribe=訂閱 diff --git a/options/locale/locale_zh-TW.ini b/options/locale/locale_zh-TW.ini index 5bfff77fd2..25e98ed025 100644 --- a/options/locale/locale_zh-TW.ini +++ b/options/locale/locale_zh-TW.ini @@ -1433,7 +1433,7 @@ issues.label.filter_sort.alphabetically=按字母順序排序 issues.label.filter_sort.reverse_alphabetically=按字母反向排序 issues.label.filter_sort.by_size=檔案由小到大 issues.label.filter_sort.reverse_by_size=檔案由大到小 -issues.num_participants=%d 參與者 +issues.num_participants_few=%d 參與者 issues.attachment.open_tab=`在新分頁中查看「%s」` issues.attachment.download=`點擊下載「%s」` issues.subscribe=訂閱 diff --git a/templates/repo/issue/view_content/sidebar.tmpl b/templates/repo/issue/view_content/sidebar.tmpl index 1414ac45ee..badad6ac47 100644 --- a/templates/repo/issue/view_content/sidebar.tmpl +++ b/templates/repo/issue/view_content/sidebar.tmpl @@ -263,7 +263,7 @@
{{if .Participants}} - {{ctx.Locale.Tr "repo.issues.num_participants" .NumParticipants}} + {{ctx.Locale.TrN .NumParticipants "repo.issues.num_participants_one" "repo.issues.num_participants_few" .NumParticipants}}
{{range .Participants}} From a517e4aeb1146aea392d6e7f5bdfe5ed8513a61d Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sat, 30 Mar 2024 06:05:07 +0000 Subject: [PATCH 0026/3435] Update module github.com/minio/minio-go/v7 to v7.0.69 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 5c39436eca..db3560d3fb 100644 --- a/go.mod +++ b/go.mod @@ -74,7 +74,7 @@ require ( github.com/meilisearch/meilisearch-go v0.26.1 github.com/mholt/archiver/v3 v3.5.1 github.com/microcosm-cc/bluemonday v1.0.26 - github.com/minio/minio-go/v7 v7.0.66 + github.com/minio/minio-go/v7 v7.0.69 github.com/msteinert/pam v1.2.0 github.com/nektos/act v0.2.52 github.com/niklasfasching/go-org v1.7.0 diff --git a/go.sum b/go.sum index 5ab9cfc5f9..44d5f1a268 100644 --- a/go.sum +++ b/go.sum @@ -614,8 +614,8 @@ github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= -github.com/minio/minio-go/v7 v7.0.66 h1:bnTOXOHjOqv/gcMuiVbN9o2ngRItvqE774dG9nq0Dzw= -github.com/minio/minio-go/v7 v7.0.66/go.mod h1:DHAgmyQEGdW3Cif0UooKOyrT3Vxs82zNdV6tkKhRtbs= +github.com/minio/minio-go/v7 v7.0.69 h1:l8AnsQFyY1xiwa/DaQskY4NXSLA2yrGsW5iD9nRPVS0= +github.com/minio/minio-go/v7 v7.0.69/go.mod h1:XAvOPJQ5Xlzk5o3o/ArO2NMbhSGkimC+bpW/ngRKDmQ= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= From 02dcd07437c49430e4e858c36c12a954112ff1f1 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sat, 30 Mar 2024 10:11:45 +0000 Subject: [PATCH 0027/3435] Update module github.com/felixge/fgprof to v0.9.4 --- go.mod | 4 ++-- go.sum | 24 +++++++++++++++++------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index cc3528800d..41acfdcf39 100644 --- a/go.mod +++ b/go.mod @@ -31,7 +31,7 @@ require ( github.com/editorconfig/editorconfig-core-go/v2 v2.6.1 github.com/emersion/go-imap v1.2.1 github.com/emirpasic/gods v1.18.1 - github.com/felixge/fgprof v0.9.3 + github.com/felixge/fgprof v0.9.4 github.com/fsnotify/fsnotify v1.7.0 github.com/gliderlabs/ssh v0.3.7 github.com/go-ap/activitypub v0.0.0-20231114162308-e219254dc5c9 @@ -53,7 +53,7 @@ require ( github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85 github.com/golang-jwt/jwt/v5 v5.2.0 github.com/google/go-github/v57 v57.0.0 - github.com/google/pprof v0.0.0-20240117000934-35fc243c5815 + github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7 github.com/google/uuid v1.6.0 github.com/gorilla/feeds v1.1.2 github.com/gorilla/sessions v1.2.2 diff --git a/go.sum b/go.sum index b3fae2a8d8..3bc626042e 100644 --- a/go.sum +++ b/go.sum @@ -186,9 +186,15 @@ github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chi-middleware/proxy v1.1.1 h1:4HaXUp8o2+bhHr1OhVy+VjN0+L7/07JDcn6v7YrTjrQ= github.com/chi-middleware/proxy v1.1.1/go.mod h1:jQwMEJct2tz9VmtCELxvnXoMfa+SOdikvbVJVHv/M+0= +github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs= +github.com/chromedp/chromedp v0.9.2/go.mod h1:LkSXJKONWTCHAfQasKFUZI+mxqS4tZqhmtGzzhLsnLs= +github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= @@ -256,8 +262,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= -github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g= -github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw= +github.com/felixge/fgprof v0.9.4 h1:ocDNwMFlnA0NU0zSB3I52xkO4sFXk80VK9lXjLClu88= +github.com/felixge/fgprof v0.9.4/go.mod h1:yKl+ERSa++RYOs32d8K6WEXCB4uXdLls4ZaZPpayhMM= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= @@ -353,6 +359,9 @@ github.com/go-webauthn/x v0.1.6 h1:QNAX+AWeqRt9loE8mULeWJCqhVG5D/jvdmJ47fIWCkQ= github.com/go-webauthn/x v0.1.6/go.mod h1:W8dFVZ79o4f+nY1eOUICy/uq5dhrRl7mxQkYhXTo0FA= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= +github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= github.com/goccy/go-json v0.9.5/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.9.6/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= @@ -441,9 +450,8 @@ github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200905233945-acf8798be1f7/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= -github.com/google/pprof v0.0.0-20240117000934-35fc243c5815 h1:WzfWbQz/Ze8v6l++GGbGNFZnUShVpP/0xffCPLL+ax8= -github.com/google/pprof v0.0.0-20240117000934-35fc243c5815/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= +github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7 h1:y3N7Bm7Y9/CtpiVkw/ZWj6lSlDF3F74SfKwfTCer72Q= +github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -494,7 +502,7 @@ github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= +github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= @@ -567,6 +575,7 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y= github.com/lestrrat-go/blackmagic v1.0.0/go.mod h1:TNgH//0vYSs8VXDCfkZLgIrVTTXQELZffUV0tz3MtdQ= github.com/lestrrat-go/httpcc v1.0.0/go.mod h1:tGS/u00Vh5N6FHNkExqGGNId8e0Big+++0Gf8MBnAvE= @@ -670,6 +679,7 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= +github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= github.com/paulmach/orb v0.11.1 h1:3koVegMC4X/WeiXYz9iswopaTwMem53NzTJuTF20JzU= github.com/paulmach/orb v0.11.1/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU= github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY= @@ -1034,9 +1044,9 @@ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= From ddc24177dd080229ab3b7d80a1231e59d270a064 Mon Sep 17 00:00:00 2001 From: Gusted Date: Fri, 29 Mar 2024 20:41:13 +0100 Subject: [PATCH 0028/3435] [BUG] Render emojis in labels in issue info popup - Currently emojis that are part of the label's name aren't rendered when shown in the popup that you get when you hover over issue references. - This patch fixes that by rendering the emoji. - Adds CSS to not make the emoji big in the label. - Resolves #1531 --- package-lock.json | 171 ++++++++++++++++++++- package.json | 1 + web_src/css/repo.css | 4 + web_src/js/components/ContextPopup.test.js | 39 +++++ web_src/js/components/ContextPopup.vue | 18 +-- web_src/js/vitest.setup.js | 1 + 6 files changed, 221 insertions(+), 13 deletions(-) create mode 100644 web_src/js/components/ContextPopup.test.js diff --git a/package-lock.json b/package-lock.json index 5358428509..3b2f868ec8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -70,6 +70,7 @@ "@stylistic/eslint-plugin-js": "1.7.0", "@stylistic/stylelint-plugin": "2.1.0", "@vitejs/plugin-vue": "5.0.4", + "@vue/test-utils": "2.4.5", "eslint": "8.57.0", "eslint-plugin-array-func": "4.0.0", "eslint-plugin-github": "4.10.2", @@ -1329,6 +1330,12 @@ "node": ">= 8" } }, + "node_modules/@one-ini/wasm": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", + "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", + "dev": true + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -2680,6 +2687,16 @@ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.21.tgz", "integrity": "sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==" }, + "node_modules/@vue/test-utils": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-2.4.5.tgz", + "integrity": "sha512-oo2u7vktOyKUked36R93NB7mg2B+N7Plr8lxp2JBGwr18ch6EggFjixSCdIVVLkT6Qr0z359Xvnafc9dcKyDUg==", + "dev": true, + "dependencies": { + "js-beautify": "^1.14.9", + "vue-component-type-helpers": "^2.0.0" + } + }, "node_modules/@webassemblyjs/ast": { "version": "1.12.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", @@ -2862,6 +2879,15 @@ "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" }, + "node_modules/abbrev": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/abort-controller": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", @@ -3799,6 +3825,22 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "dev": true, + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "node_modules/config-chain/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, "node_modules/core-js-compat": { "version": "3.36.1", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.36.1.tgz", @@ -4775,6 +4817,48 @@ "marked": "^4.1.0" } }, + "node_modules/editorconfig": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.4.tgz", + "integrity": "sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==", + "dev": true, + "dependencies": { + "@one-ini/wasm": "0.1.1", + "commander": "^10.0.0", + "minimatch": "9.0.1", + "semver": "^7.5.3" + }, + "bin": { + "editorconfig": "bin/editorconfig" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/editorconfig/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/editorconfig/node_modules/minimatch": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", + "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/electron-to-chromium": { "version": "1.4.716", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.716.tgz", @@ -7398,6 +7482,58 @@ "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==" }, + "node_modules/js-beautify": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.1.tgz", + "integrity": "sha512-ESjNzSlt/sWE8sciZH8kBF8BPlwXPwhR6pWKAw8bw4Bwj+iZcnKW6ONWUutJ7eObuBZQpiIb8S7OYspWrKt7rA==", + "dev": true, + "dependencies": { + "config-chain": "^1.1.13", + "editorconfig": "^1.0.4", + "glob": "^10.3.3", + "js-cookie": "^3.0.5", + "nopt": "^7.2.0" + }, + "bin": { + "css-beautify": "js/bin/css-beautify.js", + "html-beautify": "js/bin/html-beautify.js", + "js-beautify": "js/bin/js-beautify.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/js-beautify/node_modules/glob": { + "version": "10.3.12", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz", + "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.6", + "minimatch": "^9.0.1", + "minipass": "^7.0.4", + "path-scurry": "^1.10.2" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "dev": true, + "engines": { + "node": ">=14" + } + }, "node_modules/js-levenshtein-esm": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/js-levenshtein-esm/-/js-levenshtein-esm-1.2.0.tgz", @@ -8801,6 +8937,21 @@ "resolved": "https://registry.npmjs.org/non-layered-tidy-tree-layout/-/non-layered-tidy-tree-layout-2.0.2.tgz", "integrity": "sha512-gkXMxRzUH+PB0ax9dUN0yYF0S25BqeAYqhgMaLUFmpXLEk7Fcu8f4emJuOAY0V8kjDICxROIKsTAKsV/v355xw==" }, + "node_modules/nopt": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.0.tgz", + "integrity": "sha512-CVDtwCdhYIvnAzFoJ6NJ6dX3oga9/HyciQDnG1vQDjSLMeKLJ4A93ZqYKDrgYSr1FBY5/hMYC+2VCi24pgpkGA==", + "dev": true, + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -9140,11 +9291,11 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "node_modules/path-scurry": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", - "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz", + "integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==", "dependencies": { - "lru-cache": "^9.1.1 || ^10.0.0", + "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { @@ -9727,6 +9878,12 @@ "integrity": "sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ==", "dev": true }, + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", + "dev": true + }, "node_modules/proto-props": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/proto-props/-/proto-props-2.0.0.tgz", @@ -12089,6 +12246,12 @@ "vue": "^3.0.0-0 || ^2.7.0" } }, + "node_modules/vue-component-type-helpers": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/vue-component-type-helpers/-/vue-component-type-helpers-2.0.7.tgz", + "integrity": "sha512-7e12Evdll7JcTIocojgnCgwocX4WzIYStGClBQ+QuWPinZo/vQolv2EMq4a3lg16TKfwWafLimG77bxb56UauA==", + "dev": true + }, "node_modules/vue-eslint-parser": { "version": "9.4.2", "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.2.tgz", diff --git a/package.json b/package.json index b5bfda9dc6..7b40976968 100644 --- a/package.json +++ b/package.json @@ -69,6 +69,7 @@ "@stylistic/eslint-plugin-js": "1.7.0", "@stylistic/stylelint-plugin": "2.1.0", "@vitejs/plugin-vue": "5.0.4", + "@vue/test-utils": "2.4.5", "eslint": "8.57.0", "eslint-plugin-array-func": "4.0.0", "eslint-plugin-github": "4.10.2", diff --git a/web_src/css/repo.css b/web_src/css/repo.css index 0dfd132a73..d28dc4b96d 100644 --- a/web_src/css/repo.css +++ b/web_src/css/repo.css @@ -3010,3 +3010,7 @@ tbody.commit-list { margin-top: -1px; border-top: 1px solid var(--color-secondary); } +#issue-info-popup .emoji { + font-size: inherit; + line-height: inherit; +} diff --git a/web_src/js/components/ContextPopup.test.js b/web_src/js/components/ContextPopup.test.js new file mode 100644 index 0000000000..1db6c38301 --- /dev/null +++ b/web_src/js/components/ContextPopup.test.js @@ -0,0 +1,39 @@ +import {mount, flushPromises} from '@vue/test-utils'; +import ContextPopup from './ContextPopup.vue'; + +test('renders a issue info popup', async () => { + const owner = 'user2'; + const repo = 'repo1'; + const index = 1; + vi.spyOn(global, 'fetch').mockResolvedValue({ + json: vi.fn().mockResolvedValue({ + ok: true, + created_at: '2023-09-30T19:00:00Z', + repository: {full_name: owner}, + pull_request: null, + state: 'open', + title: 'Normal issue', + body: 'Lorem ipsum...', + number: index, + labels: [{color: 'ee0701', name: "Bug :+1: "}], + }), + ok: true, + }); + + const wrapper = mount(ContextPopup); + wrapper.vm.$el.dispatchEvent(new CustomEvent('ce-load-context-popup', {detail: {owner, repo, index}})); + await flushPromises(); + + // Header + expect(wrapper.get('p:nth-of-type(1)').text()).toEqual('user2 on Sep 30, 2023'); + // Title + expect(wrapper.get('p:nth-of-type(2)').text()).toEqual('Normal issue #1'); + // Body + expect(wrapper.get('p:nth-of-type(3)').text()).toEqual('Lorem ipsum...'); + // Check that the state is correct. + expect(wrapper.get('svg').classes()).toContain('octicon-issue-opened'); + // Ensure that script is not an element. + expect(() => wrapper.get('.evil')).toThrowError(); + // Check content of label + expect(wrapper.get('.ui.label').text()).toContain("Bug 👍 "); +}); diff --git a/web_src/js/components/ContextPopup.vue b/web_src/js/components/ContextPopup.vue index d87eb1a180..ac6a8f3bb6 100644 --- a/web_src/js/components/ContextPopup.vue +++ b/web_src/js/components/ContextPopup.vue @@ -3,6 +3,8 @@ import {SvgIcon} from '../svg.js'; import {useLightTextOnBackground} from '../utils/color.js'; import tinycolor from 'tinycolor2'; import {GET} from '../modules/fetch.js'; +import {emojiHTML} from '../features/emoji.js'; +import {htmlEscape} from 'escape-goat'; const {appSubUrl, i18n} = window.config; @@ -67,6 +69,10 @@ export default { } else { textColor = '#111111'; } + label.name = htmlEscape(label.name); + label.name = label.name.replaceAll(/:[-+\w]+:/g, (emoji) => { + return emojiHTML(emoji.substring(1, emoji.length - 1)); + }); return {name: label.name, color: `#${label.color}`, textColor}; }); }, @@ -104,19 +110,13 @@ export default { From 4b69aa5c1067495532b2cce8a42703dc63a256cc Mon Sep 17 00:00:00 2001 From: mirko Date: Tue, 24 Sep 2024 16:03:51 +0200 Subject: [PATCH 2428/3435] Add integration test for downloading a patch/diff file in compare page --- tests/integration/compare_test.go | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/integration/compare_test.go b/tests/integration/compare_test.go index 2d27a430ac..36c4d03e90 100644 --- a/tests/integration/compare_test.go +++ b/tests/integration/compare_test.go @@ -23,6 +23,7 @@ import ( files_service "code.gitea.io/gitea/services/repository/files" "code.gitea.io/gitea/tests" + "github.com/PuerkitoBio/goquery" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -67,6 +68,32 @@ func inspectCompare(t *testing.T, htmlDoc *HTMLDoc, diffCount int, diffChanges [ } } +func TestComparePatchAndDiffMenuEntries(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + session := loginUser(t, "user2") + req := NewRequest(t, "GET", "/user2/repo-release/compare/v1.0...v2.0") + resp := session.MakeRequest(t, req, http.StatusOK) + htmlDoc := NewHTMLParser(t, resp.Body) + diffOptions := htmlDoc.doc.Find("[data-tooltip-content=\"Diff options\"]") + + var patchDownloadEntryPresent bool + var diffDownloadEntryPresent bool + diffOptions.Children().Each(func (idx int, c *goquery.Selection) { + value, exists := c.Attr("download") + if exists && strings.HasSuffix(value, ".patch") { + patchDownloadEntryPresent = true + } + + if exists && strings.HasSuffix(value, ".diff") { + diffDownloadEntryPresent = true + } + }) + + assert.True(t, patchDownloadEntryPresent, "Patch file download entry should be present") + assert.True(t, diffDownloadEntryPresent, "Diff file download entry should be present") +} + // Git commit graph for repo20 // * 8babce9 (origin/remove-files-b) Add a dummy file // * b67e43a Delete test.csv and link_hi From 513285460356084daf27c97efddcc585b2531c16 Mon Sep 17 00:00:00 2001 From: mirko Date: Sun, 15 Dec 2024 19:46:21 +0100 Subject: [PATCH 2429/3435] Add support for diff and patch compare --- routers/web/repo/compare.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index 03d49fa692..27b1da13c2 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -231,6 +231,10 @@ func ParseCompareInfo(ctx *context.Context) *common.CompareInfo { if infoPath == "" { infos = []string{baseRepo.DefaultBranch, baseRepo.DefaultBranch} } else { + infoPath, isDiff := strings.CutSuffix(infoPath, ".diff") + ctx.Data["DownloadDiff"] = isDiff + infoPath, isPatch := strings.CutSuffix(infoPath, ".patch") + ctx.Data["DownloadPatch"] = isPatch infos = strings.SplitN(infoPath, "...", 2) if len(infos) != 2 { if infos = strings.SplitN(infoPath, "..", 2); len(infos) == 2 { @@ -717,6 +721,22 @@ func CompareDiff(ctx *context.Context) { return } + if ctx.Data["DownloadDiff"].(bool) { + err := git.GetRepoRawDiffForFile(ci.HeadGitRepo, ci.BaseBranch, ci.HeadBranch,git.RawDiffNormal,"", ctx.Resp) + if err != nil { + ctx.ServerError("GetDiff", err) + return + } + } + + if ctx.Data["DownloadPatch"].(bool) { + err := git.GetRepoRawDiffForFile(ci.HeadGitRepo, ci.BaseBranch, ci.HeadBranch,git.RawDiffPatch,"", ctx.Resp) + if err != nil { + ctx.ServerError("GetPatch", err) + return + } + } + ctx.Data["PullRequestWorkInProgressPrefixes"] = setting.Repository.PullRequest.WorkInProgressPrefixes ctx.Data["DirectComparison"] = ci.DirectComparison ctx.Data["OtherCompareSeparator"] = ".." From 9195d6a318ff84d2bd8fc35e749c8883d975fb77 Mon Sep 17 00:00:00 2001 From: mirko Date: Thu, 19 Dec 2024 20:20:39 +0100 Subject: [PATCH 2430/3435] Add patch/diff compare download --- routers/web/repo/compare.go | 15 ++++++++------- templates/repo/diff/options_dropdown.tmpl | 3 +++ tests/integration/compare_test.go | 7 ++++--- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index 27b1da13c2..cddbdf82b1 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -232,9 +232,9 @@ func ParseCompareInfo(ctx *context.Context) *common.CompareInfo { infos = []string{baseRepo.DefaultBranch, baseRepo.DefaultBranch} } else { infoPath, isDiff := strings.CutSuffix(infoPath, ".diff") - ctx.Data["DownloadDiff"] = isDiff + ctx.Data["ComparingDiff"] = isDiff infoPath, isPatch := strings.CutSuffix(infoPath, ".patch") - ctx.Data["DownloadPatch"] = isPatch + ctx.Data["ComparingPatch"] = isPatch infos = strings.SplitN(infoPath, "...", 2) if len(infos) != 2 { if infos = strings.SplitN(infoPath, "..", 2); len(infos) == 2 { @@ -721,18 +721,18 @@ func CompareDiff(ctx *context.Context) { return } - if ctx.Data["DownloadDiff"].(bool) { + if ctx.Data["ComparingDiff"].(bool) { err := git.GetRepoRawDiffForFile(ci.HeadGitRepo, ci.BaseBranch, ci.HeadBranch,git.RawDiffNormal,"", ctx.Resp) if err != nil { - ctx.ServerError("GetDiff", err) + ctx.ServerError("ComparingDiff", err) return } } - if ctx.Data["DownloadPatch"].(bool) { + if ctx.Data["ComparingPatch"].(bool) { err := git.GetRepoRawDiffForFile(ci.HeadGitRepo, ci.BaseBranch, ci.HeadBranch,git.RawDiffPatch,"", ctx.Resp) if err != nil { - ctx.ServerError("GetPatch", err) + ctx.ServerError("ComparingPatch", err) return } } @@ -822,7 +822,8 @@ func CompareDiff(ctx *context.Context) { if ci.DirectComparison { separator = ".." } - ctx.Data["Title"] = "Comparing " + base.ShortSha(beforeCommitID) + separator + base.ShortSha(afterCommitID) + ctx.Data["Comparing"] = base.ShortSha(beforeCommitID) + separator + base.ShortSha(afterCommitID) + ctx.Data["Title"] = "Comparing " + ctx.Data["Comparing"].(string) ctx.Data["IsDiffCompare"] = true _, templateErrs := setTemplateIfExists(ctx, pullRequestTemplateKey, pullRequestTemplateCandidates) diff --git a/templates/repo/diff/options_dropdown.tmpl b/templates/repo/diff/options_dropdown.tmpl index 09b7b80e41..97dc3ba940 100644 --- a/templates/repo/diff/options_dropdown.tmpl +++ b/templates/repo/diff/options_dropdown.tmpl @@ -11,6 +11,9 @@ {{else if .Commit.ID.String}} {{ctx.Locale.Tr "repo.diff.download_patch"}} {{ctx.Locale.Tr "repo.diff.download_diff"}} + {{else if .Diff}} + {{ctx.Locale.Tr "repo.diff.download_patch"}} + {{ctx.Locale.Tr "repo.diff.download_diff"}} {{end}} {{ctx.Locale.Tr "repo.pulls.expand_files"}} {{ctx.Locale.Tr "repo.pulls.collapse_files"}} diff --git a/tests/integration/compare_test.go b/tests/integration/compare_test.go index 36c4d03e90..fbeeb31e1d 100644 --- a/tests/integration/compare_test.go +++ b/tests/integration/compare_test.go @@ -75,11 +75,11 @@ func TestComparePatchAndDiffMenuEntries(t *testing.T) { req := NewRequest(t, "GET", "/user2/repo-release/compare/v1.0...v2.0") resp := session.MakeRequest(t, req, http.StatusOK) htmlDoc := NewHTMLParser(t, resp.Body) - diffOptions := htmlDoc.doc.Find("[data-tooltip-content=\"Diff options\"]") - + downloadOptions := htmlDoc.doc.Find("a.item[download]") + var patchDownloadEntryPresent bool var diffDownloadEntryPresent bool - diffOptions.Children().Each(func (idx int, c *goquery.Selection) { + downloadOptions.Each(func (idx int, c *goquery.Selection) { value, exists := c.Attr("download") if exists && strings.HasSuffix(value, ".patch") { patchDownloadEntryPresent = true @@ -88,6 +88,7 @@ func TestComparePatchAndDiffMenuEntries(t *testing.T) { if exists && strings.HasSuffix(value, ".diff") { diffDownloadEntryPresent = true } + }) assert.True(t, patchDownloadEntryPresent, "Patch file download entry should be present") From 4a7ec0f9a888e9142009e9517e48b260a4b7cdba Mon Sep 17 00:00:00 2001 From: mirko Date: Thu, 19 Dec 2024 20:38:36 +0100 Subject: [PATCH 2431/3435] Fix formatting --- routers/web/repo/compare.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index cddbdf82b1..2afc432e35 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -722,7 +722,7 @@ func CompareDiff(ctx *context.Context) { } if ctx.Data["ComparingDiff"].(bool) { - err := git.GetRepoRawDiffForFile(ci.HeadGitRepo, ci.BaseBranch, ci.HeadBranch,git.RawDiffNormal,"", ctx.Resp) + err := git.GetRepoRawDiffForFile(ci.HeadGitRepo, ci.BaseBranch, ci.HeadBranch,git.RawDiffNormal, "", ctx.Resp) if err != nil { ctx.ServerError("ComparingDiff", err) return @@ -730,7 +730,7 @@ func CompareDiff(ctx *context.Context) { } if ctx.Data["ComparingPatch"].(bool) { - err := git.GetRepoRawDiffForFile(ci.HeadGitRepo, ci.BaseBranch, ci.HeadBranch,git.RawDiffPatch,"", ctx.Resp) + err := git.GetRepoRawDiffForFile(ci.HeadGitRepo, ci.BaseBranch, ci.HeadBranch,git.RawDiffPatch, "", ctx.Resp) if err != nil { ctx.ServerError("ComparingPatch", err) return From d490738d5bf8e777b519d90216f61b55d94c5e2f Mon Sep 17 00:00:00 2001 From: mirko Date: Thu, 19 Dec 2024 20:41:55 +0100 Subject: [PATCH 2432/3435] Fix formatting --- routers/web/repo/compare.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index 2afc432e35..bd13cf4ed9 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -722,7 +722,7 @@ func CompareDiff(ctx *context.Context) { } if ctx.Data["ComparingDiff"].(bool) { - err := git.GetRepoRawDiffForFile(ci.HeadGitRepo, ci.BaseBranch, ci.HeadBranch,git.RawDiffNormal, "", ctx.Resp) + err := git.GetRepoRawDiffForFile(ci.HeadGitRepo, ci.BaseBranch, ci.HeadBranch, git.RawDiffNormal, "", ctx.Resp) if err != nil { ctx.ServerError("ComparingDiff", err) return @@ -730,7 +730,7 @@ func CompareDiff(ctx *context.Context) { } if ctx.Data["ComparingPatch"].(bool) { - err := git.GetRepoRawDiffForFile(ci.HeadGitRepo, ci.BaseBranch, ci.HeadBranch,git.RawDiffPatch, "", ctx.Resp) + err := git.GetRepoRawDiffForFile(ci.HeadGitRepo, ci.BaseBranch, ci.HeadBranch, git.RawDiffPatch, "", ctx.Resp) if err != nil { ctx.ServerError("ComparingPatch", err) return From 4e819f1b3c598a9132a213f927e08691ea297930 Mon Sep 17 00:00:00 2001 From: mirko Date: Tue, 24 Dec 2024 18:36:05 +0100 Subject: [PATCH 2433/3435] Test compare patch download --- tests/integration/compare_test.go | 45 +++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/tests/integration/compare_test.go b/tests/integration/compare_test.go index fbeeb31e1d..083324c691 100644 --- a/tests/integration/compare_test.go +++ b/tests/integration/compare_test.go @@ -95,6 +95,51 @@ func TestComparePatchAndDiffMenuEntries(t *testing.T) { assert.True(t, diffDownloadEntryPresent, "Diff file download entry should be present") } +func TestComparePatchDownload(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + session := loginUser(t, "user2") + req := NewRequest(t, "GET", "/user2/repo-release/compare/v1.0...v2.0.patch") + attendedResponse := ` + diff --git a/README.md b/README.md +index 6dfe48a..bc7068d 100644 +--- a/README.md ++++ b/README.md +@@ -1,3 +1,3 @@ + # Releases test repo + +-With a v1.0 ++With a v1.0 and a v2.0 +diff --git a/bugfix b/bugfix +new file mode 100644 +index 0000000..e69de29 +diff --git a/feature b/feature +new file mode 100644 +index 0000000..e69de29` + + resp := session.MakeRequest(t, req, http.StatusOK) + assert.Equal(t, attendedResponse, resp.Body.String()) + // htmlDoc := NewHTMLParser(t, resp.Body) + // downloadOptions := htmlDoc.doc.Find("a.item[download]") + + // var patchDownloadEntryPresent bool + // var diffDownloadEntryPresent bool + // downloadOptions.Each(func (idx int, c *goquery.Selection) { + // value, exists := c.Attr("download") + // if exists && strings.HasSuffix(value, ".patch") { + // patchDownloadEntryPresent = true + // } + + // if exists && strings.HasSuffix(value, ".diff") { + // diffDownloadEntryPresent = true + // } + //}) + + // assert.True(t, patchDownloadEntryPresent, "Patch file download entry should be present") + // assert.True(t, diffDownloadEntryPresent, "Diff file download entry should be present") +} + + // Git commit graph for repo20 // * 8babce9 (origin/remove-files-b) Add a dummy file // * b67e43a Delete test.csv and link_hi From b13ac5fcbfd621f4eb167135a66005d2c2ecc378 Mon Sep 17 00:00:00 2001 From: mirko Date: Tue, 24 Dec 2024 18:53:09 +0100 Subject: [PATCH 2434/3435] Fix formatting --- tests/integration/compare_test.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/integration/compare_test.go b/tests/integration/compare_test.go index 083324c691..05f24dafbe 100644 --- a/tests/integration/compare_test.go +++ b/tests/integration/compare_test.go @@ -76,10 +76,10 @@ func TestComparePatchAndDiffMenuEntries(t *testing.T) { resp := session.MakeRequest(t, req, http.StatusOK) htmlDoc := NewHTMLParser(t, resp.Body) downloadOptions := htmlDoc.doc.Find("a.item[download]") - + var patchDownloadEntryPresent bool var diffDownloadEntryPresent bool - downloadOptions.Each(func (idx int, c *goquery.Selection) { + downloadOptions.Each(func(idx int, c *goquery.Selection) { value, exists := c.Attr("download") if exists && strings.HasSuffix(value, ".patch") { patchDownloadEntryPresent = true @@ -88,7 +88,6 @@ func TestComparePatchAndDiffMenuEntries(t *testing.T) { if exists && strings.HasSuffix(value, ".diff") { diffDownloadEntryPresent = true } - }) assert.True(t, patchDownloadEntryPresent, "Patch file download entry should be present") @@ -121,7 +120,7 @@ index 0000000..e69de29` assert.Equal(t, attendedResponse, resp.Body.String()) // htmlDoc := NewHTMLParser(t, resp.Body) // downloadOptions := htmlDoc.doc.Find("a.item[download]") - + // var patchDownloadEntryPresent bool // var diffDownloadEntryPresent bool // downloadOptions.Each(func (idx int, c *goquery.Selection) { @@ -139,7 +138,6 @@ index 0000000..e69de29` // assert.True(t, diffDownloadEntryPresent, "Diff file download entry should be present") } - // Git commit graph for repo20 // * 8babce9 (origin/remove-files-b) Add a dummy file // * b67e43a Delete test.csv and link_hi From f1bf48920349b9f40c6d9dd9d569b604a22c867b Mon Sep 17 00:00:00 2001 From: mirko Date: Tue, 24 Dec 2024 19:09:37 +0100 Subject: [PATCH 2435/3435] Fix formatting --- tests/integration/compare_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/integration/compare_test.go b/tests/integration/compare_test.go index 05f24dafbe..d06ea572c3 100644 --- a/tests/integration/compare_test.go +++ b/tests/integration/compare_test.go @@ -76,7 +76,6 @@ func TestComparePatchAndDiffMenuEntries(t *testing.T) { resp := session.MakeRequest(t, req, http.StatusOK) htmlDoc := NewHTMLParser(t, resp.Body) downloadOptions := htmlDoc.doc.Find("a.item[download]") - var patchDownloadEntryPresent bool var diffDownloadEntryPresent bool downloadOptions.Each(func(idx int, c *goquery.Selection) { From 11a433ed1c7b5981ca0dcf4fb18fcecfa810449d Mon Sep 17 00:00:00 2001 From: mirko Date: Tue, 24 Dec 2024 20:41:17 +0100 Subject: [PATCH 2436/3435] Fix tests --- tests/integration/compare_test.go | 81 +++++++++++++++++++++++-------- 1 file changed, 60 insertions(+), 21 deletions(-) diff --git a/tests/integration/compare_test.go b/tests/integration/compare_test.go index d06ea572c3..d134ebf40a 100644 --- a/tests/integration/compare_test.go +++ b/tests/integration/compare_test.go @@ -98,8 +98,64 @@ func TestComparePatchDownload(t *testing.T) { session := loginUser(t, "user2") req := NewRequest(t, "GET", "/user2/repo-release/compare/v1.0...v2.0.patch") - attendedResponse := ` - diff --git a/README.md b/README.md + attendedResponse := `From 4380f99290b2b3922733ff82c57afad915ace907 Mon Sep 17 00:00:00 2001 +From: user1 +Date: Mon, 17 Apr 2023 14:39:35 +0200 +Subject: [PATCH 1/3] feature v2 + +--- + feature | 0 + 1 file changed, 0 insertions(+), 0 deletions(-) + create mode 100644 feature + +diff --git a/feature b/feature +new file mode 100644 +index 0000000..e69de29 + +From 79f9d88f1b054d650f88da0bd658e21f7b0cf6ec Mon Sep 17 00:00:00 2001 +From: user1 +Date: Mon, 17 Apr 2023 14:38:53 +0200 +Subject: [PATCH 2/3] bugfix + +--- + bugfix | 0 + 1 file changed, 0 insertions(+), 0 deletions(-) + create mode 100644 bugfix + +diff --git a/bugfix b/bugfix +new file mode 100644 +index 0000000..e69de29 + +From 7197b56fdc75b453f47c9110938cb46a303579fd Mon Sep 17 00:00:00 2001 +From: user1 +Date: Mon, 17 Apr 2023 14:42:34 +0200 +Subject: [PATCH 3/3] readme: v2 + +--- + README.md | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/README.md b/README.md +index 6dfe48a..bc7068d 100644 +--- a/README.md ++++ b/README.md +@@ -1,3 +1,3 @@ + # Releases test repo + +-With a v1.0 ++With a v1.0 and a v2.0 +` + + resp := session.MakeRequest(t, req, http.StatusOK) + assert.Equal(t, attendedResponse, resp.Body.String()) +} + +func TestCompareDiffDownload(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + session := loginUser(t, "user2") + req := NewRequest(t, "GET", "/user2/repo-release/compare/v1.0...v2.0.diff") + attendedResponse := `diff --git a/README.md b/README.md index 6dfe48a..bc7068d 100644 --- a/README.md +++ b/README.md @@ -113,28 +169,11 @@ new file mode 100644 index 0000000..e69de29 diff --git a/feature b/feature new file mode 100644 -index 0000000..e69de29` +index 0000000..e69de29 +` resp := session.MakeRequest(t, req, http.StatusOK) assert.Equal(t, attendedResponse, resp.Body.String()) - // htmlDoc := NewHTMLParser(t, resp.Body) - // downloadOptions := htmlDoc.doc.Find("a.item[download]") - - // var patchDownloadEntryPresent bool - // var diffDownloadEntryPresent bool - // downloadOptions.Each(func (idx int, c *goquery.Selection) { - // value, exists := c.Attr("download") - // if exists && strings.HasSuffix(value, ".patch") { - // patchDownloadEntryPresent = true - // } - - // if exists && strings.HasSuffix(value, ".diff") { - // diffDownloadEntryPresent = true - // } - //}) - - // assert.True(t, patchDownloadEntryPresent, "Patch file download entry should be present") - // assert.True(t, diffDownloadEntryPresent, "Diff file download entry should be present") } // Git commit graph for repo20 From 10067db6b03cd75d4f771bacdbb70ad54c8ae08c Mon Sep 17 00:00:00 2001 From: mirko Date: Wed, 1 Jan 2025 08:07:33 +0100 Subject: [PATCH 2437/3435] Improve code --- routers/web/repo/compare.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index bd13cf4ed9..24785d867e 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -233,8 +233,11 @@ func ParseCompareInfo(ctx *context.Context) *common.CompareInfo { } else { infoPath, isDiff := strings.CutSuffix(infoPath, ".diff") ctx.Data["ComparingDiff"] = isDiff - infoPath, isPatch := strings.CutSuffix(infoPath, ".patch") - ctx.Data["ComparingPatch"] = isPatch + if !isDiff { + var isPatch bool + infoPath, isPatch = strings.CutSuffix(infoPath, ".patch") + ctx.Data["ComparingPatch"] = isPatch + } infos = strings.SplitN(infoPath, "...", 2) if len(infos) != 2 { if infos = strings.SplitN(infoPath, "..", 2); len(infos) == 2 { @@ -721,7 +724,7 @@ func CompareDiff(ctx *context.Context) { return } - if ctx.Data["ComparingDiff"].(bool) { + if ctx.Data["ComparingDiff"] != nil && ctx.Data["ComparingDiff"].(bool) { err := git.GetRepoRawDiffForFile(ci.HeadGitRepo, ci.BaseBranch, ci.HeadBranch, git.RawDiffNormal, "", ctx.Resp) if err != nil { ctx.ServerError("ComparingDiff", err) @@ -729,7 +732,7 @@ func CompareDiff(ctx *context.Context) { } } - if ctx.Data["ComparingPatch"].(bool) { + if ctx.Data["ComparingPatch"] != nil && ctx.Data["ComparingPatch"].(bool) { err := git.GetRepoRawDiffForFile(ci.HeadGitRepo, ci.BaseBranch, ci.HeadBranch, git.RawDiffPatch, "", ctx.Resp) if err != nil { ctx.ServerError("ComparingPatch", err) From 5f7a267e653b5aef6c3459d8e4d8c1d752d888d0 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Wed, 1 Jan 2025 08:03:04 +0000 Subject: [PATCH 2438/3435] Update module github.com/PuerkitoBio/goquery to v1.10.1 (forgejo) (#6437) Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- go.mod | 4 ++-- go.sum | 27 +++++++++++++++++++++++---- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index c6fb51a555..b74dab47bb 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121 github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 github.com/ProtonMail/go-crypto v1.0.0 - github.com/PuerkitoBio/goquery v1.10.0 + github.com/PuerkitoBio/goquery v1.10.1 github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.2 github.com/alecthomas/chroma/v2 v2.14.0 github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb @@ -137,7 +137,7 @@ require ( github.com/Microsoft/go-winio v0.6.2 // indirect github.com/RoaringBitmap/roaring v1.9.3 // indirect github.com/andybalholm/brotli v1.1.1 // indirect - github.com/andybalholm/cascadia v1.3.2 // indirect + github.com/andybalholm/cascadia v1.3.3 // indirect github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/beorn7/perks v1.0.1 // indirect diff --git a/go.sum b/go.sum index 7a100267c5..64b9cf888f 100644 --- a/go.sum +++ b/go.sum @@ -676,8 +676,8 @@ github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78= github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= -github.com/PuerkitoBio/goquery v1.10.0 h1:6fiXdLuUvYs2OJSvNRqlNPoBm6YABE226xrbavY5Wv4= -github.com/PuerkitoBio/goquery v1.10.0/go.mod h1:TjZZl68Q3eGHNBA8CWaxAN7rOU1EbDz3CWuolcO5Yu4= +github.com/PuerkitoBio/goquery v1.10.1 h1:Y8JGYUkXWTGRB6Ars3+j3kN0xg1YqqlwvdTV8WTFQcU= +github.com/PuerkitoBio/goquery v1.10.1/go.mod h1:IYiHrOMps66ag56LEH7QYDDupKXyo5A8qrjIx3ZtujY= github.com/RoaringBitmap/roaring v1.9.3 h1:t4EbC5qQwnisr5PrP9nt0IRhRTb9gMUgQF4t4S2OByM= github.com/RoaringBitmap/roaring v1.9.3/go.mod h1:6AXUsoIEzDTFFQCe1RbGA6uFONMhvejWj5rqITANK90= github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.2 h1:cSXom2MoKJ9KPPw29RoZtHvUETY4F4n/kXl8m9btnQ0= @@ -701,8 +701,8 @@ github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHG github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= -github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss= -github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU= +github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM= +github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= @@ -1507,6 +1507,8 @@ golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2Uz golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1568,6 +1570,9 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1631,6 +1636,9 @@ golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1680,6 +1688,9 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1771,8 +1782,11 @@ golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= @@ -1784,6 +1798,8 @@ golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1804,6 +1820,7 @@ golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1876,6 +1893,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From d5bd212d00c614af73cc8f1395c2b577c163e7e9 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Wed, 1 Jan 2025 08:51:37 +0000 Subject: [PATCH 2439/3435] Update dependency @vitest/eslint-plugin to v1.1.22 (forgejo) (#6436) Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index e081796a52..385880c301 100644 --- a/package-lock.json +++ b/package-lock.json @@ -69,7 +69,7 @@ "@typescript-eslint/parser": "8.18.2", "@vitejs/plugin-vue": "5.1.5", "@vitest/coverage-v8": "2.1.8", - "@vitest/eslint-plugin": "1.1.20", + "@vitest/eslint-plugin": "1.1.22", "@vue/test-utils": "2.4.6", "eslint": "9.17.0", "eslint-import-resolver-typescript": "3.7.0", @@ -4893,9 +4893,9 @@ } }, "node_modules/@vitest/eslint-plugin": { - "version": "1.1.20", - "resolved": "https://registry.npmjs.org/@vitest/eslint-plugin/-/eslint-plugin-1.1.20.tgz", - "integrity": "sha512-2eLsgUm+GVOpDfNyH2do//MiNO/WZkXrPi+EjDmXEdUt6Jwnziq4H221L8vJE0aJys+l1FRfSkm4QbaIyDCfBg==", + "version": "1.1.22", + "resolved": "https://registry.npmjs.org/@vitest/eslint-plugin/-/eslint-plugin-1.1.22.tgz", + "integrity": "sha512-ztvy2+thiCMmKnywvKGhH3AcKgEMGd4BsFK2QC9/EXqlyjXDp7Pg96PonbLx8bDvNCAjq4hfCw5YuZSAz1EDIg==", "dev": true, "license": "MIT", "peerDependencies": { diff --git a/package.json b/package.json index fe7c30471b..bb4b786cd8 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "@typescript-eslint/parser": "8.18.2", "@vitejs/plugin-vue": "5.1.5", "@vitest/coverage-v8": "2.1.8", - "@vitest/eslint-plugin": "1.1.20", + "@vitest/eslint-plugin": "1.1.22", "@vue/test-utils": "2.4.6", "eslint": "9.17.0", "eslint-import-resolver-typescript": "3.7.0", From faf1c836eba5a7ca5785acee7c50d6d57691cacc Mon Sep 17 00:00:00 2001 From: oliverpool Date: Wed, 1 Jan 2025 11:01:51 +0100 Subject: [PATCH 2440/3435] refactor sourcehut manifest building --- services/webhook/sourcehut/builds.go | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/services/webhook/sourcehut/builds.go b/services/webhook/sourcehut/builds.go index 5a34503a7d..68a9fee65b 100644 --- a/services/webhook/sourcehut/builds.go +++ b/services/webhook/sourcehut/builds.go @@ -8,6 +8,7 @@ import ( "context" "fmt" "html/template" + "io" "io/fs" "net/http" "strings" @@ -189,11 +190,11 @@ func (pc sourcehutConvertor) Package(_ *api.PackagePayload) (graphqlPayload[buil return graphqlPayload[buildsVariables]{}, shared.ErrPayloadTypeNotSupported } -// mustBuildManifest adjusts the manifest to submit to the builds service +// newPayload opens and adjusts the manifest to submit to the builds service // // in case of an error the Error field will be set, to be visible by the end-user under recent deliveries func (pc sourcehutConvertor) newPayload(repo *api.Repository, commitID, ref, note string, trusted bool) (graphqlPayload[buildsVariables], error) { - manifest, err := pc.buildManifest(repo, commitID, ref) + manifest, err := pc.constructManifest(repo, commitID, ref) if err != nil { if len(manifest) == 0 { return graphqlPayload[buildsVariables]{}, err @@ -238,9 +239,9 @@ func (pc sourcehutConvertor) newPayload(repo *api.Repository, commitID, ref, not }, nil } -// buildManifest adjusts the manifest to submit to the builds service +// constructManifest opens and adjusts the manifest to submit to the builds service // in case of an error the []byte might contain an error that can be displayed to the user -func (pc sourcehutConvertor) buildManifest(repo *api.Repository, commitID, gitRef string) ([]byte, error) { +func (pc sourcehutConvertor) constructManifest(repo *api.Repository, commitID, gitRef string) ([]byte, error) { gitRepo, err := gitrepo.OpenRepository(pc.ctx, repo) if err != nil { msg := "could not open repository" @@ -265,6 +266,10 @@ func (pc sourcehutConvertor) buildManifest(repo *api.Repository, commitID, gitRe } defer r.Close() + return adjustManifest(repo, commitID, gitRef, r, pc.meta.ManifestPath) +} + +func adjustManifest(repo *api.Repository, commitID, gitRef string, r io.Reader, path string) ([]byte, error) { // reference: https://man.sr.ht/builds.sr.ht/manifest.md var manifest struct { Sources []string `yaml:"sources"` @@ -273,7 +278,7 @@ func (pc sourcehutConvertor) buildManifest(repo *api.Repository, commitID, gitRe Rest map[string]yaml.Node `yaml:",inline"` } if err := yaml.NewDecoder(r).Decode(&manifest); err != nil { - msg := fmt.Sprintf("could not decode manifest %q", pc.meta.ManifestPath) + msg := fmt.Sprintf("could not decode manifest %q", path) return []byte(msg), fmt.Errorf(msg+": %w", err) } From b9ed4c3325b7e4728dc2f56a5d6d5e69ef0f53a7 Mon Sep 17 00:00:00 2001 From: 0ko <0ko@noreply.codeberg.org> Date: Wed, 1 Jan 2025 15:16:27 +0500 Subject: [PATCH 2441/3435] i18n(en): consistency improvements --- options/locale/locale_en-US.ini | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index ca247ff6a2..f9cb88eaf4 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1185,10 +1185,10 @@ template.issue_labels = Issue labels template.one_item = Must select at least one template item template.invalid = Must select a template repository -archive.title = This repo is archived. You can view files and clone it, but cannot push or open issues or pull requests. +archive.title = This repository is archived. You can view files and clone it, but cannot push or open issues or pull requests. archive.title_date = This repository has been archived on %s. You can view files and clone it, but cannot push or open issues or pull requests. -archive.issue.nocomment = This repo is archived. You cannot comment on issues. -archive.pull.nocomment = This repo is archived. You cannot comment on pull requests. +archive.issue.nocomment = This repository is archived. You cannot comment on issues. +archive.pull.nocomment = This repository is archived. You cannot comment on pull requests. form.reach_limit_of_creation_1 = The owner has already reached the limit of %d repository. form.reach_limit_of_creation_n = The owner has already reached the limit of %d repositories. @@ -3692,7 +3692,7 @@ cran.install = To install the package, run the following command: debian.registry = Setup this registry from the command line: debian.registry.info = Choose $distribution and $component from the list below. debian.install = To install the package, run the following command: -debian.repository = Repository Info +debian.repository = Repository info debian.repository.distributions = Distributions debian.repository.components = Components debian.repository.architectures = Architectures @@ -3723,7 +3723,7 @@ rpm.registry = Setup this registry from the command line: rpm.distros.redhat = on RedHat based distributions rpm.distros.suse = on SUSE based distributions rpm.install = To install the package, run the following command: -rpm.repository = Repository Info +rpm.repository = Repository info rpm.repository.architectures = Architectures rpm.repository.multiple_groups = This package is available in multiple groups. rubygems.install = To install the package using gem, run the following command: From 6e13ac4d5f734a1a555a821f57cd7516735279a1 Mon Sep 17 00:00:00 2001 From: oliverpool Date: Wed, 1 Jan 2025 11:31:49 +0100 Subject: [PATCH 2442/3435] add manifest tests --- services/webhook/sourcehut/builds_test.go | 81 +++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/services/webhook/sourcehut/builds_test.go b/services/webhook/sourcehut/builds_test.go index 1a37279c99..0f1559fd0f 100644 --- a/services/webhook/sourcehut/builds_test.go +++ b/services/webhook/sourcehut/builds_test.go @@ -5,6 +5,7 @@ package sourcehut import ( "context" + "strings" "testing" webhook_model "code.gitea.io/gitea/models/webhook" @@ -384,3 +385,83 @@ func TestSourcehutJSONPayload(t *testing.T) { require.NoError(t, err) assert.Equal(t, "json test", body.Variables.Note) } + +func TestSourcehutAdjustManifest(t *testing.T) { + defer test.MockVariableValue(&setting.AppURL, "https://example.forgejo.org/")() + t.Run("without sources", func(t *testing.T) { + repo := &api.Repository{ + CloneURL: "http://localhost:3000/testdata/repo.git", + } + + manifest, err := adjustManifest(repo, "58771003157b81abc6bf41df0c5db4147a3e3c83", "refs/heads/main", strings.NewReader(`image: alpine/edge +tasks: + - say-hello: | + echo hello + - say-world: echo world`), ".build.yml") + + require.NoError(t, err) + assert.Equal(t, `sources: + - http://localhost:3000/testdata/repo.git#58771003157b81abc6bf41df0c5db4147a3e3c83 +environment: + BUILD_SUBMITTER: forgejo + BUILD_SUBMITTER_URL: https://example.forgejo.org/ + GIT_REF: refs/heads/main +image: alpine/edge +tasks: + - say-hello: | + echo hello + - say-world: echo world +`, string(manifest)) + }) + + t.Run("with other sources", func(t *testing.T) { + repo := &api.Repository{ + CloneURL: "http://localhost:3000/testdata/repo.git", + } + + manifest, err := adjustManifest(repo, "58771003157b81abc6bf41df0c5db4147a3e3c83", "refs/heads/main", strings.NewReader(`image: alpine/edge +sources: +- http://other.example.conm/repo.git +tasks: + - hello: echo world`), ".build.yml") + + require.NoError(t, err) + assert.Equal(t, `sources: + - http://other.example.conm/repo.git + - http://localhost:3000/testdata/repo.git#58771003157b81abc6bf41df0c5db4147a3e3c83 +environment: + BUILD_SUBMITTER: forgejo + BUILD_SUBMITTER_URL: https://example.forgejo.org/ + GIT_REF: refs/heads/main +image: alpine/edge +tasks: + - hello: echo world +`, string(manifest)) + }) + + t.Run("with same source", func(t *testing.T) { + repo := &api.Repository{ + CloneURL: "http://localhost:3000/testdata/repo.git", + } + + manifest, err := adjustManifest(repo, "58771003157b81abc6bf41df0c5db4147a3e3c83", "refs/heads/main", strings.NewReader(`image: alpine/edge +sources: +- http://localhost:3000/testdata/repo.git +- http://other.example.conm/repo.git +tasks: + - hello: echo world`), ".build.yml") + + require.NoError(t, err) + assert.Equal(t, `sources: + - http://localhost:3000/testdata/repo.git#58771003157b81abc6bf41df0c5db4147a3e3c83 + - http://other.example.conm/repo.git +environment: + BUILD_SUBMITTER: forgejo + BUILD_SUBMITTER_URL: https://example.forgejo.org/ + GIT_REF: refs/heads/main +image: alpine/edge +tasks: + - hello: echo world +`, string(manifest)) + }) +} From c53d21965a6b9d235aa38ec624ab7055cb84db70 Mon Sep 17 00:00:00 2001 From: Shiny Nematoda Date: Wed, 1 Jan 2025 12:32:41 +0000 Subject: [PATCH 2443/3435] fix(code search): empty mode dropdown when keyword is empty --- routers/web/repo/search.go | 9 ++++++--- tests/integration/repo_search_test.go | 1 + 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/routers/web/repo/search.go b/routers/web/repo/search.go index f2264360ec..442034b287 100644 --- a/routers/web/repo/search.go +++ b/routers/web/repo/search.go @@ -67,6 +67,12 @@ func Search(ctx *context.Context) { ctx.Data["CodeSearchPath"] = path ctx.Data["CodeSearchMode"] = mode.String() ctx.Data["PageIsViewCode"] = true + ctx.Data["CodeIndexerDisabled"] = !setting.Indexer.RepoIndexerEnabled + if setting.Indexer.RepoIndexerEnabled { + ctx.Data["CodeSearchOptions"] = code_indexer.CodeSearchOptions + } else { + ctx.Data["CodeSearchOptions"] = git.GrepSearchOptions + } if keyword == "" { ctx.HTML(http.StatusOK, tplSearch) @@ -103,7 +109,6 @@ func Search(ctx *context.Context) { } else { ctx.Data["CodeIndexerUnavailable"] = !code_indexer.IsAvailable(ctx) } - ctx.Data["CodeSearchOptions"] = code_indexer.CodeSearchOptions } else { grepOpt := git.GrepOptions{ ContextLineNumber: 1, @@ -139,10 +144,8 @@ func Search(ctx *context.Context) { strings.Join(r.LineCodes, "\n")), }) } - ctx.Data["CodeSearchOptions"] = git.GrepSearchOptions } - ctx.Data["CodeIndexerDisabled"] = !setting.Indexer.RepoIndexerEnabled ctx.Data["Repo"] = ctx.Repo.Repository ctx.Data["SourcePath"] = ctx.Repo.Repository.Link() ctx.Data["SearchResults"] = searchResults diff --git a/tests/integration/repo_search_test.go b/tests/integration/repo_search_test.go index 259ba36d08..6189dc1150 100644 --- a/tests/integration/repo_search_test.go +++ b/tests/integration/repo_search_test.go @@ -78,6 +78,7 @@ func testSearchRepo(t *testing.T, indexer bool) { code_indexer.UpdateRepoIndexer(repo) } + testSearch(t, "/user2/glob/search?q=", []string{}, indexer) testSearch(t, "/user2/glob/search?q=loren&page=1", []string{"a.txt"}, indexer) testSearch(t, "/user2/glob/search?q=loren&page=1&mode=exact", []string{"a.txt"}, indexer) From 3bea06f906feebbadcd95e1c3b7acccdfc11ee40 Mon Sep 17 00:00:00 2001 From: oliverpool Date: Wed, 1 Jan 2025 11:40:41 +0100 Subject: [PATCH 2444/3435] sourcehut: use ssh sources URL when appropriate Co-authored-by: Jordan Anderson --- services/webhook/sourcehut/builds.go | 11 +++-- services/webhook/sourcehut/builds_test.go | 51 +++++++++++++++++++++++ 2 files changed, 59 insertions(+), 3 deletions(-) diff --git a/services/webhook/sourcehut/builds.go b/services/webhook/sourcehut/builds.go index 68a9fee65b..346ccd3c0b 100644 --- a/services/webhook/sourcehut/builds.go +++ b/services/webhook/sourcehut/builds.go @@ -289,16 +289,21 @@ func adjustManifest(repo *api.Repository, commitID, gitRef string, r io.Reader, manifest.Environment["BUILD_SUBMITTER_URL"] = setting.AppURL manifest.Environment["GIT_REF"] = gitRef - source := repo.CloneURL + "#" + commitID found := false for i, s := range manifest.Sources { - if s == repo.CloneURL { - manifest.Sources[i] = source + if s == repo.CloneURL || s == repo.SSHURL { + manifest.Sources[i] = s + "#" + commitID found = true break } } if !found { + source := repo.CloneURL + if repo.Private || setting.Repository.DisableHTTPGit { + // default to ssh for private repos or when git clone is disabled over http + source = repo.SSHURL + } + source += "#" + commitID manifest.Sources = append(manifest.Sources, source) } diff --git a/services/webhook/sourcehut/builds_test.go b/services/webhook/sourcehut/builds_test.go index 0f1559fd0f..5b16f08c09 100644 --- a/services/webhook/sourcehut/builds_test.go +++ b/services/webhook/sourcehut/builds_test.go @@ -460,6 +460,57 @@ environment: BUILD_SUBMITTER_URL: https://example.forgejo.org/ GIT_REF: refs/heads/main image: alpine/edge +tasks: + - hello: echo world +`, string(manifest)) + }) + + t.Run("with ssh source", func(t *testing.T) { + repo := &api.Repository{ + CloneURL: "http://localhost:3000/testdata/repo.git", + SSHURL: "git@localhost:testdata/repo.git", + } + + manifest, err := adjustManifest(repo, "58771003157b81abc6bf41df0c5db4147a3e3c83", "refs/heads/main", strings.NewReader(`image: alpine/edge +sources: +- git@localhost:testdata/repo.git +- http://other.example.conm/repo.git +tasks: + - hello: echo world`), ".build.yml") + + require.NoError(t, err) + assert.Equal(t, `sources: + - git@localhost:testdata/repo.git#58771003157b81abc6bf41df0c5db4147a3e3c83 + - http://other.example.conm/repo.git +environment: + BUILD_SUBMITTER: forgejo + BUILD_SUBMITTER_URL: https://example.forgejo.org/ + GIT_REF: refs/heads/main +image: alpine/edge +tasks: + - hello: echo world +`, string(manifest)) + }) + + t.Run("private without source", func(t *testing.T) { + repo := &api.Repository{ + CloneURL: "http://localhost:3000/testdata/repo.git", + SSHURL: "git@localhost:testdata/repo.git", + Private: true, + } + + manifest, err := adjustManifest(repo, "58771003157b81abc6bf41df0c5db4147a3e3c83", "refs/heads/main", strings.NewReader(`image: alpine/edge +tasks: + - hello: echo world`), ".build.yml") + + require.NoError(t, err) + assert.Equal(t, `sources: + - git@localhost:testdata/repo.git#58771003157b81abc6bf41df0c5db4147a3e3c83 +environment: + BUILD_SUBMITTER: forgejo + BUILD_SUBMITTER_URL: https://example.forgejo.org/ + GIT_REF: refs/heads/main +image: alpine/edge tasks: - hello: echo world `, string(manifest)) From e1cdaf1f287590453034265d67770c6be945bc74 Mon Sep 17 00:00:00 2001 From: oliverpool Date: Wed, 1 Jan 2025 19:50:39 +0100 Subject: [PATCH 2445/3435] add release notes --- release-notes/6445.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 release-notes/6445.md diff --git a/release-notes/6445.md b/release-notes/6445.md new file mode 100644 index 0000000000..d3755701b2 --- /dev/null +++ b/release-notes/6445.md @@ -0,0 +1 @@ +feat: webhook: sourcehut: submit SSH URL for private repository or when pre-filled From e99bf7f16d4503c4d2f40b11ce69ca062a208e93 Mon Sep 17 00:00:00 2001 From: JakobDev Date: Wed, 1 Jan 2025 20:43:20 +0100 Subject: [PATCH 2446/3435] Rewrite OpenGraph Header --- routers/web/home.go | 3 ++ routers/web/org/home.go | 6 +++ routers/web/repo/commit.go | 4 ++ routers/web/repo/issue.go | 3 ++ routers/web/repo/view.go | 4 ++ routers/web/repo/wiki.go | 3 ++ routers/web/user/profile.go | 6 +++ services/context/repo.go | 4 ++ templates/base/head_opengraph.tmpl | 75 +++++++----------------------- 9 files changed, 49 insertions(+), 59 deletions(-) diff --git a/routers/web/home.go b/routers/web/home.go index d4be0931e8..4ea961c055 100644 --- a/routers/web/home.go +++ b/routers/web/home.go @@ -61,6 +61,9 @@ func Home(ctx *context.Context) { ctx.Data["PageIsHome"] = true ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled + + ctx.Data["OpenGraphDescription"] = setting.UI.Meta.Description + ctx.HTML(http.StatusOK, tplHome) } diff --git a/routers/web/org/home.go b/routers/web/org/home.go index 9ebefa334c..1b58d8fde9 100644 --- a/routers/web/org/home.go +++ b/routers/web/org/home.go @@ -47,6 +47,12 @@ func Home(ctx *context.Context) { ctx.Data["PageIsUserProfile"] = true ctx.Data["Title"] = org.DisplayName() + ctx.Data["OpenGraphTitle"] = ctx.ContextUser.DisplayName() + ctx.Data["OpenGraphType"] = "profile" + ctx.Data["OpenGraphImageURL"] = ctx.ContextUser.AvatarLink(ctx) + ctx.Data["OpenGraphURL"] = ctx.ContextUser.HTMLURL() + ctx.Data["OpenGraphDescription"] = ctx.ContextUser.Description + var orderBy db.SearchOrderBy sortOrder := ctx.FormString("sort") if _, ok := repo_model.OrderByFlatMap[sortOrder]; !ok { diff --git a/routers/web/repo/commit.go b/routers/web/repo/commit.go index a06da71429..2feb898224 100644 --- a/routers/web/repo/commit.go +++ b/routers/web/repo/commit.go @@ -419,6 +419,10 @@ func Diff(ctx *context.Context) { } } + ctx.Data["OpenGraphTitle"] = commit.Summary() + " · " + base.ShortSha(commitID) + ctx.Data["OpenGraphURL"] = fmt.Sprintf("%s/commit/%s", ctx.Repo.Repository.HTMLURL(), commitID) + _, ctx.Data["OpenGraphDescription"], _ = strings.Cut(commit.Message(), "\n") + ctx.HTML(http.StatusOK, tplCommitPage) } diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 779cbd4b6a..b537afdab5 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -2070,6 +2070,9 @@ func ViewIssue(ctx *context.Context) { ctx.Data["RefEndName"] = git.RefName(issue.Ref).ShortName() ctx.Data["NewPinAllowed"] = pinAllowed ctx.Data["PinEnabled"] = setting.Repository.Issue.MaxPinned != 0 + ctx.Data["OpenGraphTitle"] = issue.Title + ctx.Data["OpenGraphURL"] = issue.HTMLURL() + ctx.Data["OpenGraphDescription"] = issue.Content ctx.Data["OpenGraphImageURL"] = issue.SummaryCardURL() ctx.Data["OpenGraphImageAltText"] = ctx.Tr("repo.issues.summary_card_alt", issue.Title, issue.Repo.FullName()) diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index fd8c1da058..9030b03a90 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -394,6 +394,10 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry) { ctx.Data["FileName"] = blob.Name() ctx.Data["RawFileLink"] = ctx.Repo.RepoLink + "/raw/" + ctx.Repo.BranchNameSubURL() + "/" + util.PathEscapeSegments(ctx.Repo.TreePath) + ctx.Data["OpenGraphTitle"] = ctx.Data["Title"] + ctx.Data["OpenGraphURL"] = fmt.Sprintf("%s%s", setting.AppURL, ctx.Data["Link"]) + ctx.Data["OpenGraphNoDescription"] = true + if entry.IsLink() { _, link, err := entry.FollowLinks() // Errors should be allowed, because this shouldn't diff --git a/routers/web/repo/wiki.go b/routers/web/repo/wiki.go index 3d55afe294..070d07cdf3 100644 --- a/routers/web/repo/wiki.go +++ b/routers/web/repo/wiki.go @@ -535,6 +535,9 @@ func Wiki(ctx *context.Context) { } ctx.Data["Author"] = lastCommit.Author + ctx.Data["OpenGraphTitle"] = ctx.Data["Title"] + ctx.Data["OpenGraphURL"] = fmt.Sprintf("%s%s", setting.AppURL, ctx.Data["Link"]) + ctx.HTML(http.StatusOK, tplWikiView) } diff --git a/routers/web/user/profile.go b/routers/web/user/profile.go index 9cb392d878..de1c6850aa 100644 --- a/routers/web/user/profile.go +++ b/routers/web/user/profile.go @@ -63,6 +63,12 @@ func userProfile(ctx *context.Context) { ctx.Data["Title"] = ctx.ContextUser.DisplayName() ctx.Data["PageIsUserProfile"] = true + ctx.Data["OpenGraphTitle"] = ctx.ContextUser.DisplayName() + ctx.Data["OpenGraphType"] = "profile" + ctx.Data["OpenGraphImageURL"] = ctx.ContextUser.AvatarLink(ctx) + ctx.Data["OpenGraphURL"] = ctx.ContextUser.HTMLURL() + ctx.Data["OpenGraphDescription"] = ctx.ContextUser.Description + // prepare heatmap data if setting.Service.EnableUserHeatmap { data, err := activities_model.GetUserHeatmapDataByUser(ctx, ctx.ContextUser, ctx.Doer) diff --git a/services/context/repo.go b/services/context/repo.go index 462d843bfc..d294c00455 100644 --- a/services/context/repo.go +++ b/services/context/repo.go @@ -634,6 +634,10 @@ func RepoAssignment(ctx *Context) context.CancelFunc { } cardWidth, cardHeight := card.DefaultSize() + ctx.Data["OpenGraphTitle"] = repo.Name + ctx.Data["OpenGraphURL"] = repo.HTMLURL() + ctx.Data["OpenGraphType"] = "object" + ctx.Data["OpenGraphDescription"] = repo.Description ctx.Data["OpenGraphImageURL"] = repo.SummaryCardURL() ctx.Data["OpenGraphImageWidth"] = cardWidth ctx.Data["OpenGraphImageHeight"] = cardHeight diff --git a/templates/base/head_opengraph.tmpl b/templates/base/head_opengraph.tmpl index 692f1797b6..7f6eae3f49 100644 --- a/templates/base/head_opengraph.tmpl +++ b/templates/base/head_opengraph.tmpl @@ -1,12 +1,24 @@ -{{- /* og:description - a one to two sentence description of your object, maybe it only needs at most 300 bytes */ -}} +{{- /* See https://ogp.me for specification */ -}} {{if .OpenGraphTitle}} +{{else if .Title}} + +{{else}} + {{end}} -{{if .OpenGraphDescription}} - +{{- /* og:description - a one to two sentence description of your object, maybe it only needs at most 300 bytes */ -}} +{{if and .OpenGraphDescription (not .OpenGraphNoDescription)}} + {{end}} {{if .OpenGraphURL}} +{{else}} + +{{end}} +{{if .OpenGraphType}} + +{{else}} + {{end}} {{if .OpenGraphImageURL}} @@ -19,62 +31,7 @@ {{if .OpenGraphImageAltText}} {{end}} -{{end}} -{{if .PageIsUserProfile}} - - - - - {{if .ContextUser.Description}} - - {{end}} -{{else if .Repository}} - {{if .Issue}} - - - {{if .Issue.Content}} - - {{end}} - {{else if or .PageIsDiff .IsViewFile}} - - - {{if and .PageIsDiff .Commit}} - {{- $commitMessageParts := StringUtils.Cut .Commit.Message "\n" -}} - {{- $commitMessageBody := index $commitMessageParts 1 -}} - {{- if $commitMessageBody -}} - - {{- end -}} - {{end}} - {{else if .Pages}} - - - {{if .Repository.Description}} - - {{end}} - {{else}} - {{if not .OpenGraphTitle}} - - {{end}} - {{if not .OpenGraphURL}} - - {{end}} - {{if and (.Repository.Description) (not .OpenGraphDescription)}} - - {{end}} - {{end}} - - {{if and (not .Issue) (not .OpenGraphImageURL)}} - {{if (.Repository.AvatarLink ctx)}} - - {{else}} - - {{end}} - {{end}} {{else}} - - - - - + {{end}} From a33fe60bbef28de5a809261bba53ca474748d7e8 Mon Sep 17 00:00:00 2001 From: Codeberg Translate Date: Thu, 2 Jan 2025 07:47:18 +0000 Subject: [PATCH 2447/3435] i18n: update of translations from Codeberg Translate (#6378) Co-authored-by: tacaly Co-authored-by: Fjuro Co-authored-by: artnay Co-authored-by: Gusted Co-authored-by: 0ko <0ko@users.noreply.translate.codeberg.org> Co-authored-by: Edgarsons Co-authored-by: Benedikt Straub Co-authored-by: Atalanttore Co-authored-by: Wuzzy Co-authored-by: Juno Takano Co-authored-by: Dirk Co-authored-by: thodorisl Co-authored-by: jasonb Co-authored-by: SomeTr Co-authored-by: Outbreak2096 Co-authored-by: xtex Co-authored-by: Salif Mehmed Co-authored-by: s-light Co-authored-by: earl-warren Co-authored-by: Kita Ikuyo Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6378 Reviewed-by: Earl Warren Co-authored-by: Codeberg Translate Co-committed-by: Codeberg Translate --- options/locale/locale_bg.ini | 131 ++++++- options/locale/locale_cs-CZ.ini | 26 +- options/locale/locale_da.ini | 574 ++++++++++++++++++++++++++++- options/locale/locale_de-DE.ini | 27 +- options/locale/locale_el-GR.ini | 31 +- options/locale/locale_fi-FI.ini | 6 + options/locale/locale_fil.ini | 29 +- options/locale/locale_fr-FR.ini | 1 + options/locale/locale_it-IT.ini | 2 +- options/locale/locale_lv-LV.ini | 487 ++++++++++++++----------- options/locale/locale_nds.ini | 22 +- options/locale/locale_nl-NL.ini | 34 +- options/locale/locale_pt-BR.ini | 10 +- options/locale/locale_ru-RU.ini | 14 +- options/locale/locale_sv-SE.ini | 628 ++++++++++++++++++-------------- options/locale/locale_uk-UA.ini | 18 +- options/locale/locale_zh-CN.ini | 145 ++++---- 17 files changed, 1569 insertions(+), 616 deletions(-) diff --git a/options/locale/locale_bg.ini b/options/locale/locale_bg.ini index 924ce710e0..f25062876a 100644 --- a/options/locale/locale_bg.ini +++ b/options/locale/locale_bg.ini @@ -111,6 +111,7 @@ new_migrate.link = Нова миграция new_org.link = Нова организация copy_generic = Копиране в клипборда copy_error = Неуспешно копиране +copy_path = Копиране на пътя [settings] ui = Тема @@ -159,7 +160,7 @@ comment_type_group_milestone = Етап manage_emails = Управление на адресите на ел. поща permission_read = Четене update_password = Обновяване на паролата -biography_placeholder = Разкажете ни малко за себе си! (Можете да използвате Markdown) +biography_placeholder = Разкажете на другите малко за себе си! (Можете да използвате Маркдаун) orgs = Организации continue = Продължаване blocked_users = Блокирани потребители @@ -224,7 +225,7 @@ key_signature_gpg_placeholder = Започва с „-----BEGIN PGP SIGNATURE--- key_signature_ssh_placeholder = Започва с „-----BEGIN SSH SIGNATURE-----“ saved_successfully = Настройките бяха запазени успешно. no_activity = Няма скорошна дейност -theme_desc = Това ще бъде вашата тема по подразбиране в целия сайт. +theme_desc = Тази тема ще се използва за уеб интерфейса, когато сте влезли. keep_activity_private = Скриване на дейността от профилната страница lookup_avatar_by_mail = Търсене на профилна снимка по адреса на ел. поща password_incorrect = Текущата парола е неправилна. @@ -235,7 +236,7 @@ twofa_disabled = Двуфакторното удостоверяване е из orgs_none = Не сте участник в никакви организации. repos_none = Не притежавате никакви хранилища. blocked_users_none = Няма блокирани потребители. -profile_desc = Контролирайте как вашият профил се показва на другите потребители. Вашият основен адрес на ел. поща ще се използва за известия, възстановяване на паролата и уеб базирани Git операции. +profile_desc = Вашият профил permission_write = Четене и писане twofa_disable = Изключване на двуфакторното удостоверяване twofa_enroll = Включване на двуфакторно удостоверяване @@ -252,6 +253,9 @@ gpg_token_code = echo "%s" | gpg -a --default-key %s --detach-sig language.title = Език по подразбиране language.localization_project = Помогнете ни да преведем Forgejo на вашия език! Научете повече. language.description = Този език ще бъде запазен във вашия акаунт и ще се използва като език по подразбиране, след като влезете. +pronouns_custom = Персонализирани +visibility.limited_tooltip = Видимо само за влезли потребители +pronouns_custom_label = Персонализирани местоимения [packages] container.labels.value = Стойност @@ -280,6 +284,9 @@ dependencies = Зависимости published_by_in = Публикуван %[1]s от %[3]s в %[5]s published_by = Публикуван %[1]s от %[3]s generic.download = Изтеглете пакета от командния ред: +container.details.type = Тип образ +alpine.repository = За хранилището +container.images.title = Образи [tool] hours = %d часа @@ -397,7 +404,7 @@ issues.keyword_search_unavailable = В момента търсенето по к repo_desc_helper = Въведете кратко описание (опционално) mirror_address = Клониране от URL owner_helper = Някои организации може да не се показват в падащото меню поради ограничение за максимален брой хранилища. -new_repo_helper = Хранилището съдържа всички файлове на проекта, включително хронологията на ревизиите. Вече хоствате хранилище другаде? Мигрирайте хранилище. +new_repo_helper = Хранилището съдържа всички файлове на проекта, включително хронологията на ревизиите. Вече хоствате хранилище другаде? Мигрирайте хранилище. repo_name_helper = Добрите имена на хранилища използват кратки, запомнящи се и уникални ключови думи. migrated_from = Мигрирано от %[2]s visibility_description = Само притежателят или участниците в организацията, ако имат права, ще могат да го видят. @@ -424,14 +431,14 @@ settings.add_webhook = Добавяне на уеб-кука template.webhooks = Уеб-куки issues.label_templates.info = Все още няма етикети. Създайте етикет с „Нов етикет“ или използвайте предварително зададен набор от етикети: labels = Етикети -license_helper_desc = Лицензът определя какво могат и какво не могат да правят другите с вашия код. Не сте сигурни кой е подходящ за вашия проект? Вижте Избиране на лиценз. +license_helper_desc = Лицензът определя какво могат и какво не могат да правят другите с вашия код. Не сте сигурни кой е подходящ за вашия проект? Вижте Избиране на лиценз. issues.choose.blank = По подразбиране settings.hooks = Уеб-куки issue_labels = Етикети issue_labels_helper = Изберете набор от етикети readme_helper_desc = Това е мястото, където можете да напишете пълно описание на вашия проект. repo_gitignore_helper = Изберете .gitignore шаблони -auto_init = Да се инициализира хранилище (Добавя .gitignore, License и README) +auto_init = Да се инициализира хранилище template.issue_labels = Етикети за задачите migrate_items_labels = Етикети issues.label_templates.title = Зареждане на предв. зададен набор от етикети @@ -815,7 +822,7 @@ diff.browse_source = Разглеждане на изходния код file_view_rendered = Преглед на визуализация issues.lock_with_reason = заключи като %s и ограничи обсъждането до сътрудници %s milestones.new_subheader = Етапите ви помагат да управлявате задачите и да проследявате напредъка им. -release.edit = редактиране +release.edit = Редактиране activity.published_release_label = Издание activity.navbar.contributors = Допринесли pulls.recently_pushed_new_branches = Изтласкахте в клона %[1]s %[2]s @@ -939,7 +946,7 @@ pulls.approve_count_1 = %d одобрение pulls.can_auto_merge_desc = Тази заявка за сливане може да бъде слята автоматично. pulls.num_conflicting_files_1 = %d конфликтен файл activity.git_stats_commit_n = %d подавания -settings.event_issues = Модификация +settings.event_issues = Изменение branch.delete_head = Изтриване branch.delete = Изтриване на клона „%s“ branch.delete_html = Изтриване на клона @@ -1113,7 +1120,7 @@ pulls.reject_count_1 = %d поискана промяна issues.review.show_resolved = Показване на решено issues.review.hide_resolved = Скриване на решено issues.review.resolve_conversation = Решаване на обсъждането -diff.comment.markdown_info = Поддържа се стилизиране с markdown. +diff.comment.markdown_info = Поддържа се стилизиране с Маркдаун. diff.file_suppressed = Разликите не са показани, защото са твърде много pulls.reject_count_n = %d поискани промени settings.pulls.default_allow_edits_from_maintainers = Позволяване на редакции от поддържащите по подразбиране @@ -1195,6 +1202,89 @@ issues.all_title = Общо issues.new.assign_to_me = Възлагане на мен ext_wiki = Външно уики ext_issues = Външни задачи +readme_helper = Изберете шаблон за файл README +settings.event_pull_request_review_desc = Заявка за сливане е одобрена, отхвърлена или са добавени рецензионни коментари. +settings.event_pull_request_review = Рецензии +issues.filter_sort.relevance = Съответствие +settings.confirm_wiki_branch_rename = Преименуване на клона на уикито +settings.webhook.request = Заявка +settings.webhook.response = Отговор +settings.event_create = Създаване +settings.event_push_only = Събития при изтласкване +settings.event_delete = Изтриване +settings.event_header_repository = Събития за хранилището +settings.event_fork_desc = Хранилище е разклонено. +settings.event_fork = Разклоняване +settings.event_wiki_desc = Уики страница е създадена, преименувана, редактирана или изтрита. +settings.event_issue_milestone = Етапи +settings.event_pull_request_milestone_desc = Етап е добавен, премахнат или изменен. +settings.event_pull_request_label_desc = Етикети на заявка за сливане са добавени или премахнати. +settings.event_pull_request_merge = Сливане на заявка за сливане +settings.archive.tagsettings_unavailable = Настройките за маркери не са налични в архивирани хранилища. +settings.event_desc = Задействане при: +settings.event_create_desc = Клон или маркер е създаден. +generate_from = Генериране от +settings.event_push_desc = Git изтласкване към хранилище. +settings.event_package = Пакет +settings.event_pull_request_label = Етикети +settings.event_pull_request_assign_desc = Заявка за сливане е възложена или отвъзложена. +settings.event_choose = Персонализирани събития… +settings.event_header_issue = Събития при задачи +fork_no_valid_owners = Това хранилище не може да бъде разклонено, защото няма валидни притежатели. +settings.unarchive.text = Разархивирането на хранилище ще възстанови способността му да получава подавания и изтласквания, както и нови задачи и заявки за сливане. +settings.archive.branchsettings_unavailable = Настройките за клонове не са налични в архивирани хранилища. +settings.event_send_everything = Всички събития +settings.event_pull_request_approvals = Одобрения на заявка за сливане +release.invalid_external_url = Невалиден външен URL адрес: "%s" +settings.event_delete_desc = Клон или маркер е изтрит. +settings.discord_icon_url = URL адрес на иконка +settings.discord_icon_url.exceeds_max_length = URL адресът на иконката трябва да е по-малък или равен на 2048 знака +settings.event_push = Изтласкване +settings.event_repository_desc = Хранилище е създадено или изтрито. +settings.slack_icon_url = URL адрес на иконка +settings.event_issue_comment = Коментари +settings.event_pull_request_desc = Заявка за сливане е отворена, затворена, отворена наново или редактирана. +settings.event_issue_comment_desc = Коментар на задача е създаден, редактиран или изтрит. +settings.event_release_desc = Издание е публикувано, обновено или изтрито в хранилище. +settings.event_pull_request_review_request = Искания за рецензия +settings.event_pull_request_enforcement = Принудително изпълнение +diff.git-notes.remove-header = Премахване на бележката +diff.git-notes.add = Добавяне на бележка +settings.event_pull_request_assign = Възлагане +new_advanced_expand = Щракнете за разгъване +new_advanced = Разширени настройки +new_from_template = Използване на шаблон +new_from_template_description = Можете да изберете съществуващо шаблонно хранилище в тази инстанция и да приложите неговите настройки. +settings.event_pull_request_comment = Коментари +repo_gitignore_helper_desc = Изберете кои файлове да не се проследяват от списък с шаблони за обичайните езици. Типичните артефакти, генерирани от инструментите за изграждане, са включени в .gitignore по подразбиране. +object_format_helper = Формат на обектите на хранилището. Не може да се променя по-късно. SHA1 е най-съвместим. +issues.num_reviews_one = %d рецензия +settings.event_pull_request = Изменение +settings.event_issue_label = Етикети +settings.event_issue_assign = Възлагане +settings.event_header_pull_request = Събития при заявка за сливане +settings.event_issue_milestone_desc = Етап е добавен, премахнат или изменен. +settings.event_issue_label_desc = Етикети на задача са добавени или премахнати. +settings.event_issues_desc = Задача е отворена, затворена, отворена наново или редактирана. +settings.webhook.headers = Заглавки +settings.webhook.body = Тяло +settings.event_pull_request_sync = Синхронизирано +settings.event_pull_request_sync_desc = Клонът е обновен автоматично с целевия клон. +settings.event_package_desc = Пакет е създаден или изтрит в хранилище. +template_description = Шаблонните хранилища позволяват на потребителите да генерират нови хранилища със същата структура на директориите, файлове и опционални настройки. +auto_init_description = Поставете началото на Git историята с README и по избор добавете файлове License и .gitignore. +pulls.sign_in_require = Влезте, за да създадете нова заявка за сливане. +issues.num_reviews_few = %d рецензии +diff.git-notes.remove-body = Тази бележка ще бъде премахната. +issues.review.add_remove_review_requests = поиска рецензии от %[1]s и премахна заявки за рецензия за %[2]s %[3]s +form.name_pattern_not_allowed = Шаблонът "%s" не е разрешен в име на хранилище. +settings.wiki_rename_branch_main_notices_2 = Това ще преименува перманентно вътрешния клон на уикито на хранилището %s. Съществуващите изтегляния ще трябва да бъдат обновени. +settings.event_pull_request_milestone = Етапи +settings.event_pull_request_comment_desc = Заявка за сливане е създадена, редактирана или изтрита. +settings.event_issue_assign_desc = Задача е възложена или отвъзложена. +settings.event_pull_request_review_request_desc = Рецензия на заявка за сливане е поискана или е премахната. +generate_repo = Генериране на хранилище +default_branch_helper = Стандартният клон е основния клон за заявки за сливане и подавания на код. [modal] confirm = Потвърждаване @@ -1218,6 +1308,12 @@ buttons.italic.tooltip = Добавяне на курсив текст buttons.link.tooltip = Добавяне на връзка buttons.disable_monospace_font = Изключване на равноширокия шрифт buttons.ref.tooltip = Препратка към задача или заявка за сливане +table_modal.label.columns = Колони +table_modal.label.rows = Редове +table_modal.placeholder.content = Съдържание +table_modal.placeholder.header = Заглавка +buttons.new_table.tooltip = Добавяне на таблица +table_modal.header = Добавяне на таблица [org] teams.write_access = Писане @@ -1249,7 +1345,7 @@ settings.visibility.public = Публична settings.visibility.limited_shortname = Ограничена settings.visibility.private_shortname = Частна settings.permission = Разрешения -settings.visibility.limited = Ограничена (видима само за удостоверени потребители) +settings.visibility.limited = Ограничена (видима само за влезли потребители) settings.visibility.private = Частна (видима само за участниците в организацията) org_name_helper = Имената на организациите е добре да са кратки и запомнящи се. org_full_name_holder = Пълно име на организацията @@ -1330,6 +1426,9 @@ err_empty_admin_email = Администраторският адрес на е password_algorithm = Алгоритъм за хеш. на паролите default_keep_email_private = Скриване на адресите на ел. поща по подразбиране invalid_password_algorithm = Невалиден алгоритъм за хеш. на паролите +err_admin_name_is_reserved = Потребителското име на администратора е невалидно, потребителското име е резервирано +err_admin_name_pattern_not_allowed = Потребителското име на администратора е невалидно, потребителското име съответства с резервиран шаблон +err_admin_name_is_invalid = Потребителското име на администратора е невалидно [filter] string.asc = А - Я @@ -1361,6 +1460,14 @@ activate_account.text_1 = Здравейте, %[1]s, благодарим activate_email.text = Моля, щракнете върху следната връзка, за да потвърдите своя адрес на ел. поща в рамките на %s: activate_email = Потвърдете своя адрес на ел. поща activate_account.text_2 = Моля, щракнете върху следната връзка, за да активирате своя акаунт в рамките на %s: +issue_assigned.issue = @%[1]s ви възложи задача %[2]s в хранилище %[3]s. +issue.action.push_n = @%[1]s изтласка %[3]d подавания към %[2]s +issue.action.push_1 = @%[1]s изтласка %[3]d подаване към %[2]s +repo.transfer.subject_to_you = %s иска да прехвърли хранилище "%s" към вас +issue.action.merge = @%[1]s сля #%[2]d в %[3]s. +issue_assigned.pull = @%[1]s ви възложи заявката за сливане %[2]s в хранилище %[3]s. +issue.action.ready_for_review = @%[1]s отбеляза тази заявка за сливане като готова за рецензиране. +repo.transfer.subject_to = %s иска да прехвърли хранилище "%s" към %s [user] joined_on = Присъединени на %s @@ -1392,6 +1499,9 @@ followers.title.one = Последовател following.title.one = Следван following.title.few = Следвани public_activity.visibility_hint.self_public = Вашата дейност е видима за всички, с изключение на взаимодействията в частни пространства. Конфигуриране. +form.name_pattern_not_allowed = Шаблонът "%s" не е разрешен в потребителско име. +form.name_reserved = Потребителското име "%s" е резервирано. +public_activity.visibility_hint.self_private_profile = Вашата дейност е видима само за вас и администраторите на инстанцията, тъй като вашият профил е частен. Конфигуриране. [home] filter = Други филтри @@ -1661,6 +1771,7 @@ variables.creation = Добавяне на променлива variables.deletion.failed = Неуспешно премахване на променлива. runners.task_list.repository = Хранилище runners.description = Описание +runs.no_workflows.help_no_write_access = За да научите повече за Forgejo Actions, вижте документацията. [heatmap] less = По-малко diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini index 375c4a710f..7d8fdee165 100644 --- a/options/locale/locale_cs-CZ.ini +++ b/options/locale/locale_cs-CZ.ini @@ -747,7 +747,7 @@ webauthn=Dvoufázové ověření (bezpečnostní klíče) public_profile=Veřejný profil biography_placeholder=Řekněte ostatním něco o sobě! (Je podporován Markdown) location_placeholder=Sdílejte svou přibližnou polohu s ostatními -profile_desc=Nastavte, jak bude váš profil zobrazen ostatním uživatelům. Vaše hlavní e-mailová adresa bude použita pro oznámení, obnovení hesla a operace Git. +profile_desc=O vás password_username_disabled=Externí uživatelé nemohou měnit svoje uživatelské jméno. Kontaktujte prosím svého administrátora pro více detailů. full_name=Celé jméno website=Web @@ -838,7 +838,7 @@ add_email_success=Nová e-mailová adresa byla přidána. email_preference_set_success=Nastavení e-mailu bylo úspěšně nastaveno. add_openid_success=Nová OpenID adresa byla přidána. keep_email_private=Skrýt e-mailovou adresu -keep_email_private_popup=Tímto skryjete svou e-mailovou adresu ze svého profilu. Nebude již výchozí adresou pro commity provedené skrze webové rozhraní, jako nahrávání a úpravy souborů, a nebude použita pro slučovací commity. Místo toho můžete použít speciální adresu %s pro spojení commitů s vaším účtem. Změna této funkce nebude mít vliv na stávající commity. +keep_email_private_popup=Vaše e-mailová adresa nebude zobrazena na vašem profilu a nebude výchozí adresou pro commity provedené skrze webové rozhraní, jako nahrávání, úpravy a sloučení. Namísto toho lze použít speciální adresu %s pro propojení commitů s vaším účtem. Tato možnost neovlivní existující commity. openid_desc=OpenID vám umožní delegovat ověřování na externího poskytovatele. manage_ssh_keys=Správa klíčů SSH @@ -1054,6 +1054,8 @@ language.title = Výchozí jazyk keep_activity_private.description = Vaše veřejná aktivita bude viditelná pouze vám a správcům instance. language.description = Tento jazyk bude uložen do vašeho účtu a po přihlášení bude použit jako výchozí. language.localization_project = Pomozte nám s překladem Forgejo do vašeho jazyka! Více informací. +user_block_yourself = Nemůžete zablokovat sami sebe. +pronouns_custom_label = Vlastní zájmena [repo] new_repo_helper=Repozitář obsahuje všechny soubory projektu, včetně historie revizí. Už jej hostujete jinde? Migrovat repozitář. @@ -1102,7 +1104,7 @@ object_format_helper = Objektový formát repozitáře. Později jej nelze změn readme=README readme_helper=Vyberte šablonu souboru README readme_helper_desc=Do tohoto místa můžete zadat celý popis vašeho projektu. -auto_init=Inicializovat repozitář (přidá soubory .gitignore, License a README) +auto_init=Inicializovat repozitář trust_model_helper=Vyberte model důvěry pro ověření podpisu. Možnosti jsou: trust_model_helper_collaborator=Spolupracovník: Důvěřovat podpisům spolupracovníků trust_model_helper_committer=Přispěvatel: Důvěřovat podpisům, které se shodují s přispěvateli @@ -2395,7 +2397,7 @@ settings.deploy_key_deletion=Odstranit klíč pro nasazení settings.deploy_key_deletion_desc=Odstranění klíče pro nasazení zruší jeho přístup k repozitáři. Pokračovat? settings.deploy_key_deletion_success=Klíč pro nasazení byl odstraněn. settings.branches=Větve -settings.protected_branch=Ochrana větve +settings.protected_branch=Ochrana větví settings.protected_branch.save_rule=Uložit pravidlo settings.protected_branch.delete_rule=Odstranit pravidlo settings.protected_branch_can_push=Povolit nahrání? @@ -2461,7 +2463,7 @@ settings.default_branch_desc=Vybrat výchozí větev repozitáře pro požadavky settings.merge_style_desc=Sloučit styly settings.default_merge_style_desc=Výchozí styl sloučení settings.choose_branch=Vyberte větev… -settings.no_protected_branch=Nejsou tu žádné chráněné větve. +settings.no_protected_branch=Nemáte žádné chráněné větve. settings.edit_protected_branch=Upravit settings.protected_branch_required_rule_name=Požadovaný název pravidla settings.protected_branch_duplicate_rule_name=Již existuje pravidlo pro tuto sadu větví @@ -2850,6 +2852,20 @@ issues.num_reviews_few = %d kontrol issues.summary_card_alt = Souhrn problému s názvem „%s“ v repozitáři %s editor.add_tmpl.filename = nazevsouboru settings.default_update_style_desc = Výchozí způsob aktualizací používaný pro aktualizace žádostí o sloučení, které jsou pozadu oproti základní větvi. +pulls.sign_in_require = Přihlaste se pro vytvoření nové žádosti o sloučení. +new_from_template = Použít šablonu +new_from_template_description = Můžete si vybrat existující šablonu repozitáře na této instanci a použít její nastavení. +new_advanced = Pokročilá nastavení +new_advanced_expand = Klikněte pro rozbalení +auto_init_description = Vytvořit historii Gitu se souborem README a volitelně přidat soubory License a .gitignore. +summary_card_alt = Karta se souhrnem repozitáře %s +issues.reaction.alt_many = Uživatel %[1]s a %[2]d dalších zareagovali s %[3]s. +release.summary_card_alt = Karta souhrnu vydání s názvem „%s“ v repozitáři %s +issues.reaction.add = Přidat reakci +issues.reaction.alt_few = Uživatel %[1]s zareagoval pomocí %[2]s. +issues.context.menu = Nabídka komentáře +issues.reaction.alt_remove = Odstranit %[1]s reakci z komentáře. +issues.reaction.alt_add = Přidat %[1]s reakci na komentář. [graphs] component_loading_info = Tohle může chvíli trvat… diff --git a/options/locale/locale_da.ini b/options/locale/locale_da.ini index 25a3010e05..9dbfa83f91 100644 --- a/options/locale/locale_da.ini +++ b/options/locale/locale_da.ini @@ -774,7 +774,7 @@ hidden_comment_types_description = Kommentartyper, der er markeret her, vil ikke update_profile = Opdater profil change_username_prompt = Note: Ændring af dit brugernavn ændrer også din konto-URL. biography_placeholder = Fortæl andre lidt om dig selv! (Markdown understøttes) -profile_desc = Styr, hvordan din profil vises til andre brugere. Din primære e-mailadresse vil blive brugt til meddelelser, gendannelse af adgangskode og webbaserede Git-operationer. +profile_desc = Om dig saved_successfully = Dine indstillinger blev gemt. keep_activity_private.description = Din offentlige aktivitet vil kun være synlig for dig og instans-administratorerne. email_desc = Din primære e-mailadresse vil blive brugt til meddelelser, gendannelse af adgangskode og, forudsat at den ikke er skjult, webbaserede Git-operationer. @@ -871,7 +871,7 @@ key_content_ssh_placeholder = Begynder med "ssh-ed25519", "ssh-rsa", "ecdsa-sha2 gpg_key_matched_identities_long = De indlejrede identiteter i denne nøgle matcher de følgende aktiverede e-mailadresser for denne bruger. Commits, der matcher disse e-mailadresser, kan bekræftes med denne nøgle. gpg_key_deletion_success = GPG-nøglen er blevet fjernet. email_preference_set_success = E-mail-præference er blevet indstillet. -keep_email_private_popup = Dette vil skjule din e-mailadresse fra din profil. Det vil ikke længere være standard for commits foretaget via webgrænsefladen, såsom filupload og redigeringer, og vil ikke blive brugt til merge commits. I stedet kan en speciel adresse %s bruges til at knytte commits til din konto. Bemærk, at ændring af denne mulighed ikke vil påvirke eksisterende commits. +keep_email_private_popup = Din e-mailadresse vil ikke blive vist på din profil og vil ikke være standard for commits foretaget via webgrænsefladen, som f.eks. filupload, redigeringer og merge commits. I stedet kan en speciel adresse %s bruges til at linke commits til din konto. Denne mulighed vil ikke påvirke eksisterende commits. gpg_desc = Disse offentlige GPG-nøgler er knyttet til din konto og bruges til at bekræfte dine commits. Opbevar dine private nøgler sikkert, da de giver dig mulighed for at underskrive commits med din identitet. gpg_token_help = Du kan generere en signatur ved at bruge: ssh_key_verified_long = Nøglen er blevet bekræftet med et token og kan bruges til at bekræfte commits, der matcher enhver aktiveret e-mail-adresse for denne bruger. @@ -904,4 +904,572 @@ oauth2_regenerate_secret = Genskab hemmeligheden oauth2_regenerate_secret_hint = Har du mistet din hemmelighed? oauth2_client_secret_hint = Hemmeligheden vil ikke blive vist igen, når du forlader eller opdaterer denne side. Sørg for, at du har gemt den. oauth2_application_edit = Redigere -oauth2_confidential_client = Fortrolig klient. Vælg for apps, der holder hemmeligheden fortrolig, såsom webapps. Vælg ikke for indbyggede apps, herunder desktop- og mobilapps. \ No newline at end of file +oauth2_confidential_client = Fortrolig klient. Vælg for apps, der holder hemmeligheden fortrolig, såsom webapps. Vælg ikke for indbyggede apps, herunder desktop- og mobilapps. +oauth2_application_create_description = OAuth2-applikationer giver din tredjepartsapplikation adgang til brugerkonti på denne instans. +oauth2_application_remove_description = Fjernelse af en OAuth2-applikation forhindrer den i at få adgang til autoriserede brugerkonti på denne instans. Vil du fortsætte? +authorized_oauth2_applications = Autoriserede OAuth2-applikationer +revoke_key = Tilbagekald +revoke_oauth2_grant = Tilbagekald adgang +revoke_oauth2_grant_description = Tilbagekaldelse af adgang for denne tredjepartsapplikation forhindrer denne applikation i at få adgang til dine data. Er du sikker? +revoke_oauth2_grant_success = Adgangen blev tilbagekaldt. +twofa_desc = For at beskytte din konto mod adgangskodetyveri kan du bruge en smartphone eller en anden enhed til at modtage tidsbaserede engangsadgangskoder ("TOTP"). +twofa_is_enrolled = Din konto er i øjeblikket tilmeldt til tofaktorgodkendelse. +twofa_not_enrolled = Din konto er i øjeblikket ikke tilmeldt to-faktor-godkendelse. +twofa_disable = Deaktiver to-faktor-godkendelse +twofa_scratch_token_regenerate = Gendan engangsgendannelsesnøgle +twofa_scratch_token_regenerated = Din gendannelsesnøgle til engangsbrug er nu %s. Opbevar det et sikkert sted, da det ikke vil blive vist igen. +twofa_enroll = Tilmeld dig to-faktor-godkendelse +twofa_recovery_tip = Hvis du mister din enhed, vil du være i stand til at bruge en gendannelsesnøgle til engangsbrug for at få adgang til din konto igen. +oauth2_application_locked = Forgejo forhåndsregistrerer nogle OAuth2-applikationer ved opstart, hvis det er aktiveret i config. For at forhindre uventet adfærd kan disse hverken redigeres eller fjernes. Se venligst OAuth2-dokumentationen for at få flere oplysninger. +authorized_oauth2_applications_description = Du har givet adgang til din personlige Forgejo-konto til disse tredjepartsapplikationer. Tilbagekald venligst adgangen for applikationer, der ikke længere er i brug. +manage_account_links = Tilknyttede konti +manage_account_links_desc = Disse eksterne konti er knyttet til din Forgejo-konto. +link_account = Link konto +remove_account_link = Slet linket konto +remove_account_link_desc = Fjernelse af en tilknyttet konto vil tilbagekalde dens adgang til din Forgejo-konto. Vil du fortsætte? +remove_account_link_success = Den tilknyttede konto er blevet fjernet. +twofa_disable_note = Du kan deaktivere to-faktor-godkendelse, hvis det er nødvendigt. +twofa_disable_desc = Deaktivering af to-faktor-godkendelse vil gøre din konto mindre sikker. Vil du fortsætte? +regenerate_scratch_token_desc = Hvis du har forlagt din gendannelsesnøgle eller allerede har brugt den til at logge på, kan du nulstille den her. +twofa_disabled = To-faktor-godkendelse er blevet deaktiveret. +scan_this_image = Scan dette billede med din godkendelsesapplikation: +then_enter_passcode = Og indtast adgangskoden vist i applikationen: +passcode_invalid = Passkoden er forkert. Prøv igen. +twofa_enrolled = Din konto er blevet tilmeldt. Opbevar din gendannelsesnøgle til engangsbrug (%s) et sikkert sted, da den ikke vil blive vist igen. +twofa_failed_get_secret = Det lykkedes ikke at få hemmelig-koden. +or_enter_secret = Eller indtast hemmelig-koden: %s +webauthn_desc = Sikkerhedsnøgler er hardwareenheder, der indeholder kryptografiske nøgler. De kan bruges til to-faktor-godkendelse. Sikkerhedsnøgler skal understøtte WebAuthn Authenticator-standarden. +webauthn_register_key = Tilføj sikkerhedsnøgle +webauthn_nickname = Kaldenavn +webauthn_delete_key = Slet sikkerhedsnøglen +webauthn_delete_key_desc = Hvis du sletter en sikkerhedsnøgle, kan du ikke længere logge ind med den. Vil du fortsætte? +webauthn_key_loss_warning = Hvis du mister dine sikkerhedsnøgler, mister du adgangen til din konto. +webauthn_alternative_tip = Du vil måske gerne konfigurere en ekstra godkendelsesmetode. +hooks.desc = Tilføj webhooks, som vil blive udløst for alle depoter, som du ejer. +orgs_none = Du er ikke medlem af nogen organisationer. +repos_none = Du ejer ikke nogen depoter. +delete_account = Slet din konto +delete_prompt = Denne handling vil permanent slette din brugerkonto. Det KAN IKKE fortrydes. +confirm_delete_account = Bekræft sletning +email_notifications.enable = Aktiver e-mail-notifikationer +email_notifications.onmention = Kun e-mail ved omtale +email_notifications.disable = Deaktiver e-mail-notifikationer +email_notifications.submit = Indstil e-mail-præference +email_notifications.andyourown = Og dine egne notifikationer +visibility = Brugersynlighed +visibility.public = Offentlig +visibility.public_tooltip = Synlig for alle +visibility.limited_tooltip = Kun synlig for brugere, der er logget ind +visibility.private = Privat +blocked_since = Blokeret siden %s +user_unblock_success = Blokeringen af brugeren er blevet fjernet. +user_block_success = Brugeren er blevet blokeret. +blocked_users_none = Der er ingen blokerede brugere. +visibility.limited = Begrænset +delete_account_desc = Er du sikker på, at du vil slette denne brugerkonto permanent? +visibility.private_tooltip = Kun synlig for medlemmer af organisationer, du har tilsluttet dig +delete_with_all_comments = Din konto er yngre end %s. For at undgå spøgelseskommentarer slettes alle problemer/PR-kommentarer med kontoen. +delete_account_title = Slet brugerkonto +user_block_yourself = Du kan ikke blokere dig selv. +pronouns_custom_label = Brugerdefinerede stedord + +[repo] +rss.must_be_on_branch = Du skal være på en gren for at have et RSS-feed. +admin.manage_flags = Administrer flag +admin.enabled_flags = Flag aktiveret for depotet: +admin.update_flags = Opdater flag +admin.flags_replaced = Depot flag udskiftet +owner = Ejer +owner_helper = Nogle organisationer vises muligvis ikke i dropdown-menuen på grund af en maksimal grænse for optælling af depoter. +repo_name = Depot navn +repo_name_helper = Gode depotnavne bruger korte, mindeværdige og unikke nøgleord. +repo_size = Depotstørrelse +size_format = %[1]s: %[2]s, %[3]s: %[4]s +template = Skabelon +template_select = Vælg en skabelon +template_helper = Gør depot til en skabelon +visibility = Synlighed +visibility_helper = Gør depotet privat +visibility_helper_forced = Din webstedsadministrator tvinger nye depoter til at være private. +clone_helper = Har du brug for hjælp til kloning? Besøg Hjælp. +fork_repo = Fork depot +fork_from = Fork fra +already_forked = Du har allerede forked %s +fork_to_different_account = Fork til en anden konto +fork_branch = Gren, der skal klones til fork +all_branches = Alle grene +fork_no_valid_owners = Dette depot kan ikke blive forked, fordi der ikke er nogen gyldige ejere. +use_template = Brug denne skabelon +open_with_editor = Åben med %s +download_zip = Download ZIP +download_tar = Download TAR.GZ +download_bundle = Download BUNDLE +generate_repo = Generer depot +generate_from = Generer fra +repo_desc = Beskrivelse +repo_lang = Sprog +repo_gitignore_helper = Vælg .gitignore skabeloner +issue_labels = Etiketter +issue_labels_helper = Vælg et etiketsæt +license = Licens +object_format = Objektformat +object_format_helper = Objektformatet for depotet. Kan ikke ændres senere. SHA1 er den mest kompatible. +readme = LÆSMIG +readme_helper = Vælg en LÆSMIG-filskabelon +readme_helper_desc = Dette er stedet, hvor du kan skrive en komplet beskrivelse af dit projekt. +auto_init = Initialiser depot +create_repo = Opret depot +default_branch = Standard gren +default_branch_label = standard +default_branch_helper = Standardgrenen er basegrenen for pull-anmodninger og kode-commits. +mirror_prune = Prune +mirror_prune_desc = Slet forældede referencer til fjernsporing +mirror_interval = Spejlinterval (gyldige tidsenheder er "h", "m", "s"). 0 for at deaktivere periodisk synkronisering. (Minimumsinterval: %s) +mirror_interval_invalid = Spejlintervallet er ikke gyldigt. +mirror_use_ssh.text = Brug SSH-godkendelse +mirror_use_ssh.not_available = SSH-godkendelse er ikke tilgængelig. +mirror_denied_combination = Kan ikke bruge offentlig nøgle- og adgangskodebaseret godkendelse i kombination. +mirror_sync = synkroniseret +mirror_sync_on_commit = Synkroniser når commits er pushed +mirror_address = Klon fra URL +mirror_address_desc = Angiv eventuelle nødvendige legitimationsoplysninger i sektionen Autorisation. +mirror_address_url_invalid = Den angivne URL er ugyldig. Du skal escape alle komponenter i URL'en korrekt. +mirror_lfs = Stort fillager (LFS) +mirror_lfs_desc = Aktiver spejling af LFS-data. +mirror_lfs_endpoint = LFS-endepunkt +mirror_last_synced = Sidst synkroniseret +mirror_password_placeholder = (Uændret) +mirror_password_blank_placeholder = (Usat) +mirror_password_help = Skift brugernavn for at slette en gemt adgangskode. +watchers = Observatører +stargazers = Stjernekiggere +stars_remove_warning = Dette vil fjerne alle stjerner fra dette depot. +forks = Forks +stars = Stjerner +reactions_more = og %d mere +unit_disabled = Webstedsadministratoren har deaktiveret denne depotsektion. +adopt_preexisting_label = Adopter filer +adopt_preexisting = Adopter allerede eksisterende filer +adopt_preexisting_content = Opret depot fra %s +adopt_preexisting_success = Adopterede filer og oprettet depot fra %s +delete_preexisting_label = Slet +delete_preexisting = Slet allerede eksisterende filer +delete_preexisting_content = Slet filer i %s +delete_preexisting_success = Slettede ikke-adopterede filer i %s +blame_prior = Se skylden før denne ændring +blame.ignore_revs.failed = Kunne ikke ignorere revisioner i .git-blame-ignore-revs. +author_search_tooltip = Viser maksimalt 30 brugere +tree_path_not_found_commit = Sti %[1]s eksisterer ikke i commit %[2]s +tree_path_not_found_branch = Sti %[1]s findes ikke i gren %[2]s +tree_path_not_found_tag = Sti %[1]s findes ikke i tag %[2]s +transfer.accept = Accepter overførsel +transfer.accept_desc = Overfør til "%s" +transfer.reject = Afvis overførsel +transfer.no_permission_to_accept = Du har ikke tilladelse til at acceptere denne overførsel. +transfer.no_permission_to_reject = Du har ikke tilladelse til at afvise denne overførsel. +desc.private = Privat +desc.public = Offentlig +desc.template = Skabelon +desc.archived = Arkiveret +desc.sha256 = SHA256 +template.items = Skabelonelementer +template.git_content = Git-indhold (standardgren) +template.git_hooks = Git hooks +template.webhooks = Webhooks +template.topics = Emner +template.avatar = Avatar +template.issue_labels = Problem etiketter +template.one_item = Skal vælge mindst ét skabelonelement +template.invalid = Skal vælge et skabelondepot +admin.failed_to_replace_flags = Det lykkedes ikke at erstatte depot flag +repo_desc_helper = Indtast en kort beskrivelse (valgfrit) +mirror_use_ssh.helper = Forgejo vil spejle depotet via Git over SSH og oprette et nøglepar til dig, når du vælger denne mulighed. Du skal sikre dig, at den genererede offentlige nøgle er autoriseret til at pushe til destinationsdepotet. Du kan ikke bruge adgangskodebaseret godkendelse, når du vælger dette. +adopt_search = Indtast brugernavn for at søge efter ikke-adopterede depoter... (lad stå tomt for at finde alle) +fork_visibility_helper = Synligheden af et forked depot kan ikke ændres. +license_helper = Vælg en licensfil +language_other = Andre +desc.internal = Internt +visibility_description = Kun ejeren eller organisationens medlemmer, hvis de har rettigheder, vil kunne se det. +new_repo_helper = Et depot indeholder alle projektfiler, inklusive revisionshistorik. Er du allerede vært for en et andet sted? Migrer depot. +visibility_fork_helper = (Hvis du ændrer dette, vil det påvirke synligheden af alle forks.) +mirror_lfs_endpoint_desc = Synkronisering vil forsøge at bruge klon-url'en til at bestemme LFS-serveren. Du kan også angive et brugerdefineret endepunkt, hvis depots LFS-data er gemt et andet sted. +template_description = Skabelondepoter lader brugere generere nye depoter med samme mappestruktur, filer og valgfri indstillinger. +repo_gitignore_helper_desc = Vælg, hvilke filer der ikke skal spores, fra en liste over skabeloner til almindelige sprog. Typiske artefakter genereret af hvert sprogs byggeværktøjer er inkluderet på .gitignore som standard. +license_helper_desc = En licens styrer, hvad andre kan og ikke kan gøre med din kode. Er du i tvivl om, hvilken der passer til dit projekt? Se Vælg en licens. +template.git_hooks_tooltip = Du er i øjeblikket ikke i stand til at ændre eller fjerne Git hooks, når først de er tilføjet. Vælg kun dette, hvis du har tillid til skabelondepotet. +mirror_public_key = Offentlig SSH-nøgle +blame.ignore_revs = Ignorerer revisioner i .git-blame-ignore-revs. Klik her for at omgå og se den normale skyldvisning. +mirror_address_protocol_invalid = Den angivne URL er ugyldig. Kun http(s):// eller git:// placeringer kan bruges til spejling. +transfer.reject_desc = Annuller overførsel til "%s" +archive.title = Dette depot er arkiveret. Du kan se filer og klone dem, men du kan ikke pushe eller åbne problemer eller pull-anmodninger. +archive.title_date = Dette depot er blevet arkiveret på %s. Du kan se filer og klone dem, men du kan ikke pushe eller åbne problemer eller pull-anmodninger. +archive.issue.nocomment = Denne depot er arkiveret. Du kan ikke kommentere på problemer. +form.reach_limit_of_creation_1 = Ejeren har allerede nået grænsen på %d depot. +form.reach_limit_of_creation_n = Ejeren har allerede nået grænsen på %d depoterne. +form.name_reserved = Depotnavnet "%s" er reserveret. +form.string_too_long = Den givne streng er længere end %d tegn. +need_auth = Autorisation +migrate_options = Migrationsmuligheder +migrate_options_mirror_helper = Dette depot vil være et spejl +migrate_options_lfs = Migrer LFS-filer +migrate_options_lfs_endpoint.label = LFS endepunkt +migrate_options_lfs_endpoint.description.local = En lokal serversti understøttes også. +migrate_items = Migration Elementer +migrate_items_wiki = Wiki +migrate_items_milestones = Milepæle +migrate_items_labels = Etiketter +migrate_items_issues = Problemmer +migrate_items_pullrequests = Pull-anmodninger +migrate_items_merge_requests = Flet anmodninger +migrate_items_releases = Udgivelser +migrate_repo = Migrer depot +migrate.clone_address_desc = HTTP(S) eller Git "klone" URL'en for et eksisterende lager +migrate.clone_local_path = eller en lokal serversti +migrate.permission_denied = Du har ikke tilladelse til at importere lokale depoter. +migrate.permission_denied_blocked = Du kan ikke importere fra ikke-tilladte værter. Bed venligst administratoren om at kontrollere ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS indstillingerne. +migrate.invalid_local_path = Den lokale sti er ugyldig. Den eksisterer ikke eller er ikke en mappe. +migrate.invalid_lfs_endpoint = LFS-endepunktet er ikke gyldigt. +migrate.failed = Migrering mislykkedes: %v +migrate.migrate_items_options = Adgangstoken er påkrævet for at migrere yderligere elementer +migrated_from_fake = Migreret fra %[1]s +migrate.migrate = Migrer fra %s +migrate.migrating = Migrerer fra %s ... +migrate.migrating_failed = Migrering fra %s mislykkedes. +migrate.migrating_failed.error = Kunne ikke migrere: %s +migrate.migrating_failed_no_addr = Migration mislykkedes. +migrate.git.description = Migrer kun et depot fra enhver Git-tjeneste. +migrate.gitlab.description = Migrer data fra gitlab.com eller andre GitLab-instanser. +migrate.forgejo.description = Migrer data fra codeberg.org eller andre Forgejo-instanser. +migrate.gogs.description = Migrer data fra notabug.org eller andre Gogs-instanser. +migrate.codebase.description = Migrer data fra codebasehq.com. +migrate.migrating_git = Migrering af Git-data +migrate.migrating_topics = Migrering af emner +migrate.migrating_milestones = Migrerende milepæle +migrate.migrating_labels = Migrering af etiketter +migrate.migrating_releases = Migrering af udgivelser +migrate.migrating_issues = Migrering af problemer +migrate.migrating_pulls = Migrering af pull-anmodninger +migrate.cancel_migrating_title = Annuller migrering +mirror_from = spejl af +forked_from = forked fra +generated_from = genereret fra +fork_from_self = Du kan ikke forke et depot, du ejer. +migrate.onedev.description = Migrer data fra code.onedev.io eller andre OneDev-instanser. +archive.pull.nocomment = Denne depot er arkiveret. Du kan ikke kommentere på pull-anmodninger. +migrated_from = Migreret fra %[2]s +migrate.gitea.description = Migrer data fra gitea.com eller andre Gitea-instanser. +migrate.gitbucket.description = Migrer data fra GitBucket-instanser. +migrate.github_token_desc = Du kan sætte et eller flere tokens med kommasepareret her for at gøre migreringen hurtigere på grund af GitHub API-hastighedsgrænsen. ADVARSEL: Misbrug af denne funktion kan overtræde tjenesteudbyderens politik og føre til kontoblokering. +migrate_options_lfs_endpoint.description = Migration vil forsøge at bruge din fjern-Git til at bestemme LFS-serveren. Du kan også angive et brugerdefineret slutpunkt, hvis lagerets LFS-data er gemt et andet sted. +form.name_pattern_not_allowed = Mønsteret "%s" er ikke tilladt i et depotnavn. +migrate_options_lfs_endpoint.placeholder = Hvis det efterlades tomt, vil endepunktet blive afledt fra klonens URL +migrate.clone_address = Migrer / Klon fra URL +migrate.github.description = Migrer data fra github.com eller GitHub Enterprise server. +migrate.cancel_migrating_confirm = Vil du annullere denne migrering? +more_operations = Flere operationer +new_from_template = Brug en skabelon +new_from_template_description = Du kan vælge en eksisterende depotskabelon på denne instans og anvende dens indstillinger. +new_advanced = Avancerede indstillinger +new_advanced_expand = Klik for at udvide +auto_init_description = Start Git-historikken med en LÆSMIG og tilføj eventuelt licens- og .gitignore-filer. +unwatch = Fjern overvågning +star = Stjerne +unstar = Fjern stjerne +fork = Fork +download_archive = Download depot +no_desc = Ingen beskrivelse +code = Kode +audio_not_supported_in_browser = Din browser understøtter ikke HTML5 "audio"-tagget. +clone_this_repo = Klon dette depot +create_new_repo_command = Oprettelse af et nyt depot på kommandolinjen +push_exist_repo = Push et eksisterende depot fra kommandolinjen +empty_message = Dette depot indeholder ikke noget indhold. +code.desc = Få adgang til kildekode, filer, commits og grene. +branch = Gren +tree = Træ +clear_ref = `Ryd nuværende reference` +find_tag = Find tag +branches = Grene +tag = Tag +tags = Tags +issues = Problemer +pulls = Pull-anmodninger +project = Projekter +file_permalink = Permalink +file_too_large = Filen er for stor til at blive vist. +ambiguous_runes_header = `Denne fil indeholder tvetydige Unicode-tegn` +ambiguous_runes_description = `Denne fil indeholder Unicode-tegn, der kan forveksles med andre tegn. Hvis du tror, at dette er med vilje, kan du roligt ignorere denne advarsel. Brug Escape-knappen til at afsløre dem.` +invisible_runes_line = `Denne linje har usynlige Unicode-tegn` +ambiguous_runes_line = `Denne linje har tvetydige Unicode-tegn` +ambiguous_character = `%[1]c [U+%04[1]X] kan forveksles med %[2]c [U+%04[2]X]` +escape_control_characters = Escape +unescape_control_characters = Unescape +file_copy_permalink = Kopiér permalink +view_git_blame = Se git skyld +video_not_supported_in_browser = Din browser understøtter ikke HTML5 "video"-tagget. +stored_lfs = Lagret med Git LFS +symbolic_link = Symbolsk link +executable_file = Eksekverbar fil +vendored = Vendored +generated = Genereret +commit_graph = Commit graf +commit_graph.select = Vælg grene +commit_graph.hide_pr_refs = Skjul pull-anmodninger +commit_graph.monochrome = Mono +commit_graph.color = Farve +commit.contained_in = Denne commit er indeholdt i: +commit.contained_in_default_branch = Denne commit er en del af standardgrenen +commit.load_referencing_branches_and_tags = Indlæs grene og tags, der refererer til denne commit +blame = Skyld +download_file = Download fil +normal_view = Normal visning +line = linje +lines = linjer +from_comment = (kommentar) +no_eol.text = Ingen EOL +no_eol.tooltip = Denne fil indeholder ikke et afsluttende linjesluttegn. +editor.add_file = Tilføj fil +editor.new_file = Ny fil +editor.upload_file = Upload fil +editor.edit_file = Rediger fil +editor.preview_changes = Forhåndsvisning af ændringer +editor.cannot_edit_lfs_files = LFS-filer kan ikke redigeres i webgrænsefladen. +editor.cannot_edit_non_text_files = Binære filer kan ikke redigeres i webgrænsefladen. +editor.edit_this_file = Rediger fil +editor.this_file_locked = Filen er låst +editor.must_be_on_a_branch = Du skal være på en gren for at foretage eller foreslå ændringer til denne fil. +editor.fork_before_edit = Du skal fork dette depot for at foretage eller foreslå ændringer til denne fil. +editor.delete_this_file = Slet fil +editor.must_have_write_access = Du skal have skriveadgang for at foretage eller foreslå ændringer til denne fil. +filter_branch_and_tag = Filtrer gren eller tag +quick_guide = Hurtig guide +broken_message = Git-dataene, der ligger til grund for dette depot, kan ikke læses. Kontakt administratoren af denne instans eller slet dette depot. +invisible_runes_header = `Denne fil indeholder usynlige Unicode-tegn` +cite_this_repo = Citér dette depot +invisible_runes_description = `Denne fil indeholder usynlige Unicode-tegn, der ikke kan skelnes for mennesker, men som kan behandles anderledes af en computer. Hvis du tror, at dette er med vilje, kan du roligt ignorere denne advarsel. Brug Escape-knappen til at afsløre dem.` +fork_guest_user = Log ind for at forke dette depot. +watch_guest_user = Log ind for at overvåge dette depot. +watch = Overvåg +star_guest_user = Log ind for at stjernemarkere dette depot. +subscribe.issue.guest.tooltip = Log ind for at abonnere på dette problem. +subscribe.pull.guest.tooltip = Log ind for at abonnere på denne pull-anmodning. +release = Udgivelse +releases = Udgivelser +packages = Pakker +actions = Handlinger +labels = Etiketter +milestones = Milepæle +n_commit_few = %s commits +n_branch_one = %s gren +n_branch_few = %s grene +org_labels_desc_manage = Styr +commits = Commits +commit = Commit +org_labels_desc = Etiketter på organisationsniveau, der kan bruges med alle depoter under denne organisation +n_commit_one = %s commit +n_release_few = %s udgivelser +released_this = udgivet dette +file.title = %s ved %s +file_raw = Rå +file_follow = Følg symbollink +editor.or = eller +editor.fail_to_update_file_summary = Fejlmeddelelse: +editor.push_rejected_summary = Fuldstændig afvisningsmeddelelse: +editor.add_subdir = Tilføj en mappe… +editor.unable_to_upload_files = Kunne ikke uploade filer til "%s" med fejl: %v +n_tag_one = %s tag +n_tag_few = %s tags +n_release_one = %s udgivelse +file_history = Historie +file_view_source = Se kilde +file_view_rendered = Vis gengivet +file_view_raw = Vis rå +editor.file_delete_success = Filen "%s" er blevet slettet. +editor.name_your_file = Navngiv din fil… +editor.filename_help = Tilføj en mappe ved at skrive dens navn efterfulgt af en skråstreg ("/"). Fjern en mappe ved at skrive backspace i begyndelsen af inputfeltet. +editor.cancel_lower = Annuller +editor.commit_signed_changes = Commit underskrevne ændringer +editor.file_is_a_symlink = `"%s" er et symbolsk link. Symbolske links kan ikke redigeres i webeditoren` +editor.filename_is_a_directory = Filnavnet "%s" er allerede brugt som et mappenavn i dette depot. +editor.upload_file_is_locked = Filen "%s" er låst af %s. +editor.upload_files_to_dir = Upload filer til "%s" +editor.cannot_commit_to_protected_branch = Kan ikke commit til den beskyttede gren "%s". +editor.no_commit_to_branch = Kan ikke commit direkte til grenen fordi: +editor.user_no_push_to_branch = Brugeren kan ikke push til gren +editor.require_signed_commit = Gren kræver en underskrevet commit +editor.cherry_pick = Kirsebær-pluk %s på: +editor.revert = Vend %s tilbage til: +commits.desc = Gennemse kildekodens ændringshistorik. +commits.commits = Commits +commits.no_commits = Ingen commits til fælles. "%s" og "%s" har helt forskellige historier. +commits.nothing_to_compare = Disse grene er ens. +commits.search.tooltip = Du kan præfikse nøgleord med "forfatter:", "committer:", "efter:" eller "før:", f.eks. "vend forfatter:Alice før:2019-01-13". +commits.search_branch = Denne gren +commits.search_all = Alle grene +commits.author = Forfatter +commits.gpg_key_id = GPG nøgle-id +commits.ssh_key_fingerprint = SSH nøgle fingeraftryk +commits.view_path = Se på dette tidspunkt i historien +commit.operations = Operationer +commit.revert = Revert +commit.revert-header = Revert: %s +commit.revert-content = Vælg gren for at revert til: +editor.directory_is_a_file = Katalognavnet "%s" er allerede brugt som et filnavn i dette depot. +editor.branch_already_exists = Gren "%s" findes allerede i dette depot. +editor.filename_cannot_be_empty = Filnavnet må ikke være tomt. +editor.commit_changes = Commit ændringer +editor.signoff_desc = Tilføj en Signed-off-by trailer af committeren i slutningen af commit log-meddelelsen. +editor.commit_directly_to_this_branch = Commit direkte til %[1]s-grenen. +editor.add_tmpl = Tilføj "<%s>" +editor.add_tmpl.filename = filnavn +editor.add = Tilføj %s +editor.update = Opdater %s +editor.delete = Slet %s +editor.patch = Påfør patch +editor.patching = Patching: +editor.fail_to_apply_patch = Kan ikke anvende patch "%s" +editor.new_patch = Nyt patch +editor.commit_message_desc = Tilføj en valgfri udvidet beskrivelse… +editor.create_new_branch = Opret en ny gren til denne commit og start en pull-anmodning. +editor.create_new_branch_np = Opret en ny gren til denne commit. +editor.propose_file_change = Foreslå filændring +editor.new_branch_name = Navngiv den nye gren for denne commit +editor.new_branch_name_desc = Ny gren navn… +editor.cancel = Annuller +commits.signed_by_untrusted_user_unmatched = Signeret af ikke-pålidelig bruger, der ikke matcher committer +commits.signed_by = Signeret af +commits.signed_by_untrusted_user = Signeret af en ikke-pålidelig bruger +commit.cherry-pick = Kirsebær-pluk +commit.cherry-pick-header = Kirsebærpluk: %s +commit.cherry-pick-content = Vælg en gren at kirsebær-pluk på: +commitstatus.failure = Fiasko +commitstatus.error = Fejl +projects.create_success = Projektet "%s" er blevet oprettet. +projects.title = Titel +projects.new = Nyt projekt +projects.new_subheader = Koordiner, spor og opdater dit arbejde ét sted, så projekter forbliver gennemsigtige og planmæssigt. +editor.filename_is_invalid = Filnavnet er ugyldigt: "%s". +projects.deletion_desc = Sletning af et projekt fjerner det fra alle relaterede problemer. Vil du fortsætte? +projects.deletion_success = Projektet er blevet slettet. +projects.modify = Rediger projekt +projects.type.none = Intet +projects.type.basic_kanban = Grundlæggende kanban +projects.type.bug_triage = Fejltriage +projects.template.desc = Skabelon +projects.template.desc_helper = Vælg en projektskabelon for at komme i gang +projects.column.edit = Rediger kolonne +projects.column.set_default_desc = Indstil denne kolonne som standard for ukategoriserede problemer og pulls +projects.column.delete = Slet kolonne +projects.column.deletion_desc = Sletning af en projektkolonne flytter alle relaterede problemer til standardkolonnen. Vil du fortsætte? +projects.column.color = Farve +projects.open = Åben +projects.close = Luk +projects.column.assigned_to = Tildelt til +projects.card_type.desc = Forhåndsvisninger af kort +projects.card_type.images_and_text = Billeder og tekst +projects.card_type.text_only = Kun tekst +issues.desc = Organiser fejlrapporter, opgaver og milepæle. +editor.invalid_commit_mail = Ugyldig mail til oprettelse af en commit. +editor.branch_does_not_exist = Gren "%s" findes ikke i dette depot. +editor.file_editing_no_longer_exists = Filen, der redigeres, "%s", findes ikke længere i dette depot. +projects.column.edit_title = Navn +projects.column.new_title = Navn +projects.column.new_submit = Opret kolonne +projects.column.new = Ny kolonne +projects.column.set_default = Indstil standard +editor.file_deleting_no_longer_exists = Filen, der slettes, "%s", eksisterer ikke længere i dette depot. +editor.no_changes_to_show = Der er ingen ændringer at vise. +editor.fail_to_update_file = Kunne ikke opdatere/oprette filen "%s". +editor.push_rejected_no_message = Ændringen blev afvist af serveren uden en besked. Tjek venligst Git hooks. +editor.push_rejected = Ændringen blev afvist af serveren. Tjek venligst Git hooks. +projects.create = Opret projekt +projects.deletion = Slet projekt +projects.edit = Rediger projekt +projects.edit_subheader = Projekter organiserer problemer og sporer fremskridt. +projects.edit_success = Projekt "%s" er blevet opdateret. +issues.new.no_label = Ingen etiketter +issues.new.clear_labels = Tydelige etiketter +issues.new.projects = Projekter +issues.new.clear_projects = Ryd projekter +issues.new.no_projects = Intet projekt +issues.new.open_projects = Åben projekter +issues.new.closed_projects = Lukkede projekter +issues.new.no_items = Ingen elementer +issues.new.milestone = Milepæl +issues.new.no_milestone = Ingen milepæl +issues.filter_assignees = Filter tildelt +issues.filter_milestones = Filter Milepæl +issues.filter_projects = Filter projekt +issues.filter_labels = Filter etiket +issues.filter_reviewers = Filter anmelder +issues.new = Nyt problem +issues.new.title_empty = Titel må ikke være tom +issues.new.labels = Etiketter +issues.new.clear_milestone = Ryd milepæl +issues.new.open_milestone = Åben milepæle +issues.new.closed_milestone = Lukkede milepæle +issues.new.assignees = Tilvalgte +issues.new.clear_assignees = Ryd tildelte +issues.filter_sort.recentupdate = For nylig opdateret +issues.filter_sort.leastupdate = Sidst opdateret +issues.filter_sort.mostcomment = De fleste kommenterede +issues.filter_sort.leastcomment = Mindst kommenteret +issues.filter_sort.nearduedate = Nærmeste forfaldsdato +issues.filter_sort.farduedate = Længste forfaldsdato +issues.filter_sort.moststars = Flest stjerner +editor.file_changed_while_editing = Filens indhold er ændret, siden du begyndte at redigere. Klik her for at se dem eller Bekræft ændringer igen for at overskrive dem. +editor.file_already_exists = En fil med navnet "%s" findes allerede i dette depot. +editor.commit_id_not_matching = Filen blev ændret, mens du redigerede den. Forpligt dig til en ny gren og merge derefter. +editor.push_out_of_date = Pushet ser ud til at være forældet. +editor.commit_empty_file_header = Commit en tom fil +editor.commit_empty_file_text = Den fil, du er ved at commitere, er tom. Vil du fortsætte? +commits.message = Besked +commits.browse_further = Gennemse videre +commits.renamed_from = Omdøbt fra %s +commits.date = Dato +commits.older = Ældre +commits.newer = Nyere +issues.new.no_assignees = Ingen tildelte +issues.new.assign_to_me = Tildel mig +issues.action_assignee_no_select = Ingen tildelt +issues.action_check = Marker/fjern markeringen +issues.action_check_all = Marker/fjern markeringen af alle elementer +issues.opened_by = åbnet %[1]s af %[3]s +pulls.merged_by = af %[3]s blev merged %[1]s +pulls.merged_by_fake = af %[2]s blev merged %[1]s +issues.closed_by = af %[3]s blev lukket %[1]s +projects.desc = Håndtere problemer og pulls i projektboards. +issues.filter_sort.feweststars = Færre stjerner +issues.action_assignee = Tildelte +commitstatus.success = Succes +ext_issues = Eksterne problemer +projects = Projekter +projects.description = Beskrivelse (valgfrit) +projects.description_placeholder = Beskrivelse +issues.new.no_reviewers = Ingen anmeldere +issues.choose.get_started = Kom godt i gang +issues.choose.open_external_link = Åben +issues.choose.blank = Standard +issues.choose.blank_about = Opret et problem fra standardskabelon. +issues.choose.ignore_invalid_templates = Ugyldige skabeloner er blevet ignoreret +issues.filter_sort.mostforks = Fleste forks +issues.filter_sort.fewestforks = Mindste forks +issues.action_open = Åben +issues.action_close = Lukket +issues.action_label = Etiket +issues.action_milestone_no_select = Ingen milepæl +commitstatus.pending = Afventer +issues.edit.already_changed = Kunne ikke gemme ændringer af problemet. Det ser ud til, at indholdet allerede er blevet ændret af en anden bruger. Opdater siden, og prøv at redigere igen for at undgå at overskrive deres ændringer +issues.action_milestone = Milepæl +issues.comment_pull_merged_at = merged commit %[1]s into %[2]s %[3]s +issues.comment_manually_pull_merged_at = manuelt merged commit %[1]s into %[2]s %[3]s +issues.context.edit = Redigere +issues.context.delete = Slet +issues.no_content = Ingen beskrivelse angivet. +issues.close = Luk problem + +[notification] +watching = Overvåger + +[action] +watched_repo = begyndte at overvåge %[2]s \ No newline at end of file diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index 2b28ab13af..6a479b7629 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -749,7 +749,7 @@ webauthn=Hardware-Sicherheitsschlüssel public_profile=Öffentliches Profil biography_placeholder=Erzähle anderen ein wenig über dich selbst! (Markdown wird unterstützt) location_placeholder=Teile deinen ungefähren Standort mit anderen -profile_desc=Leg fest, wie dein Profil anderen Benutzern angezeigt wird. Deine primäre E-Mail-Adresse wird für Benachrichtigungen, Passwort-Wiederherstellung und webbasierte Git-Operationen verwendet. +profile_desc=Über dich password_username_disabled=Benutzer, die nicht von Forgejo verwaltet werden können ihren Benutzernamen nicht ändern. Bitte kontaktiere deinen Administrator für mehr Details. full_name=Vollständiger Name website=Webseite @@ -840,7 +840,7 @@ add_email_success=Die neue E-Mail-Addresse wurde hinzugefügt. email_preference_set_success=E-Mail-Einstellungen wurden erfolgreich aktualisiert. add_openid_success=Die neue OpenID-Adresse wurde hinzugefügt. keep_email_private=E-Mail-Adresse verbergen -keep_email_private_popup=Dies wird deine E-Mail-Adresse in deinem Profil ausblenden. Sie wird nicht mehr der Standardwert für die Commits, die vom Web-Interface gemacht wurden, sein, z.B. Dateiuploads und -bearbeitungen, und sie wird nicht für Merge-Commits benutzt werden. Stattdessen kann eine besondere Adresse %s benutzt werden, um Commits mit deinem Konto zu assoziieren. Beachte, dass diese Option für existierende Commits keine Wirkung hat. +keep_email_private_popup=Deine Mailadresse wird nicht in deinem Profil angezeigt und wird nicht der Standard für Commits über das Webinterface sein, wie zum Beispiel Dateiuploads, Bearbeitungen, und Merge-Commits. Stattdessen kann eine besondere Adresse %s benutzt werden, um Commits mit deinem Account zu verbinden. Diese Option wirkt sich nicht auf bestehende Commits aus. openid_desc=Mit OpenID kannst du dich über einen Drittanbieter authentifizieren. manage_ssh_keys=SSH-Schlüssel verwalten @@ -1056,6 +1056,8 @@ language.title = Standardsprache keep_activity_private.description = Deine öffentliche Aktivität wird nur für dich selbst und die Instanzadminstratoren sichtbar sein. language.localization_project = Hilf uns, Forgejo in deine Sprache zu übersetzen! Mehr erfahren. language.description = Diese Sprache wird in deinem Konto gespeichert und standardmäßig nach dem Anmelden benutzt. +user_block_yourself = Du kannst dich nicht selbst blockieren. +pronouns_custom_label = Individuelle Pronomen [repo] owner=Besitzer @@ -1101,7 +1103,7 @@ license_helper_desc=Eine Lizenz regelt, was andere mit deinem Code tun (oder nic readme=README readme_helper=Wähle eine README-Vorlage readme_helper_desc=Hier kannst du eine komplette Beschreibung für dein Projekt schreiben. -auto_init=Repository initialisieren (Fügt .gitignore, License und README-Dateien hinzu) +auto_init=Repository initialisieren trust_model_helper=Wähle das Vertrauensmodell für die Signaturvalidierung aus. Mögliche Modelle sind: trust_model_helper_collaborator=Mitarbeiter: Vertraue Signaturen von Mitarbeitern am Projekt trust_model_helper_committer=Committer: Vertraue Signaturen, die mit ihren Committern übereinstimmen @@ -1179,8 +1181,8 @@ template.invalid=Es muss ein Vorlagen-Repository ausgewählt werden archive.title=Dieses Repository ist archiviert. Du kannst Dateien ansehen und es klonen, kannst aber nicht pushen oder Issues/Pull-Requests öffnen. archive.title_date=Dieses Repository wurde am %s archiviert. Du kannst Dateien ansehen und es klonen, aber nicht pushen oder Issues/Pull-Requests öffnen. -archive.issue.nocomment=Dieses Repo ist archiviert. Du kannst Issues nicht kommentieren. -archive.pull.nocomment=Dieses Repo ist archiviert. Du kannst Pull-Requests nicht kommentieren. +archive.issue.nocomment=Dieses Repository ist archiviert. Du kannst Issues nicht kommentieren. +archive.pull.nocomment=Dieses Repository ist archiviert. Du kannst Pull-Requests nicht kommentieren. form.reach_limit_of_creation_1=Du hast bereits dein Limit von %d Repository erreicht. form.reach_limit_of_creation_n=Du hast bereits dein Limit von %d Repositorys erreicht. @@ -2851,6 +2853,21 @@ issues.num_reviews_one = %d Review issues.summary_card_alt = Zusammenfassung eines Issues mit dem Titel „%s“ im Repository %s issues.num_reviews_few = %d Reviews editor.add_tmpl.filename = Dateiname +settings.default_update_style_desc = Standard-Aktualisierungsart um Pull-Requests zu aktualisieren, die hinter dem Base-Branch sind. +new_advanced = Erweiterte Einstellungen +new_advanced_expand = Zum Ausklappen klicken +pulls.sign_in_require = Anmelden, um einen neuen Pull-Request zu erstellen. +new_from_template = Eine Vorlage benutzen +new_from_template_description = Du kannst eine existierende Repository-Vorlage auf dieser Instanz benutzen und ihre Einstellungen anwenden. +auto_init_description = Die Git-Historie mit einer README-Datei und optional einer Lizenz- und .gitignore-Datei starten. +issues.context.menu = Kommentar Menü +issues.reaction.add = Reaktion hinzufügen +issues.reaction.alt_many = %[1]s und %[2]d mehr reagierten %[3]s. +issues.reaction.alt_few = %[1]s reagierten %[2]s. +issues.reaction.alt_add = Füge %[1]s Reaktion zum Kommentar hinzu. +issues.reaction.alt_remove = Entferne %[1]s Reaktion von diesem Kommentar. +summary_card_alt = Zusammenfassungskarte des Repositorys %s +release.summary_card_alt = Übersichtskarte eines Releases mit dem Titel „%s“ im Repository %s [graphs] component_loading_failed = Konnte %s nicht laden diff --git a/options/locale/locale_el-GR.ini b/options/locale/locale_el-GR.ini index a32d616814..cb99563034 100644 --- a/options/locale/locale_el-GR.ini +++ b/options/locale/locale_el-GR.ini @@ -166,6 +166,7 @@ new_org.link = Νέος οργανισμός new_migrate.title = Νέα μεταφορά new_repo.title = Νέο repository new_org.title = Νέος οργανισμός +copy_path = Αντιγραφή τοποθεσίας [aria] navbar=Μπάρα πλοήγησης @@ -197,8 +198,14 @@ buttons.ref.tooltip=Μνημόνευση ενός θέματος ή pull request buttons.switch_to_legacy.tooltip=Χρήση του κλασσικού κειμενογράφου buttons.enable_monospace_font=Ενεργοποίηση σταθερής γραμματοσειράς buttons.disable_monospace_font=Απενεργοποίηση σταθερής γραμματοσειράς -buttons.unindent.tooltip = Αναίρεση στοιχείων κατά ένα επίπεδο -buttons.indent.tooltip = Στοιχεία φωλιών κατά ένα επίπεδο +buttons.unindent.tooltip = Μείωση εσοχής στοιχείων κατά ένα επίπεδο +buttons.indent.tooltip = Αύξηση εσοχής στοιχείων κατά ένα επίπεδο +table_modal.header = Προσθήκη πίνακα +table_modal.placeholder.header = Επικεφαλίδα +table_modal.placeholder.content = Περιεχόμενο +table_modal.label.rows = Σειρές +table_modal.label.columns = Στήλες +buttons.new_table.tooltip = Προσθήκη πίνακα [filter] string.asc=A - Z @@ -249,9 +256,9 @@ err_empty_db_path=Η διαδρομή της βάσης δεδομένων SQLit no_admin_and_disable_registration=Δεν μπορείτε να απενεργοποιήσετε την ιδιο-εγγραφή χρήστη χωρίς να έχετε δημιουργήσει διαχειριστικό λογαριασμό. err_empty_admin_password=Ο κωδικός πρόσβασης του διαχειριστή δεν μπορεί να είναι κενός. err_empty_admin_email=Το email του διαχειριστή δεν μπορεί να είναι κενό. -err_admin_name_is_reserved=Το Όνομα χρήστη του Διαχειριστή δεν είναι έγκυρο, είναι δεσμευμένο +err_admin_name_is_reserved=Το όνομα χρήστη του Διαχειριστή δεν είναι έγκυρο, αυτό το όνομα είναι δεσμευμένο err_admin_name_pattern_not_allowed=Το Όνομα χρήστη του Διαχειριστή δεν είναι έγκυρο, ταιριάζει σε μια δεσμευμένη μορφή -err_admin_name_is_invalid=Το Όνομα Χρήστη του Διαχειριστή δεν είναι έγκυρο +err_admin_name_is_invalid=Το όνομα χρήστη του Διαχειριστή δεν είναι έγκυρο general_title=Γενικές ρυθμίσεις app_name=Τίτλος διακομιστή @@ -476,6 +483,8 @@ hint_register = Χρειάζεστε έναν λογαριασμό; Αλλαγή ορατότητας. public_activity.visibility_hint.self_private = Η δραστηριότητά σας είναι ορατή μόνο σε εσάς και στους διαχειριστές. Αλλαγή ορατότητας. +public_activity.visibility_hint.self_private_profile = Η δραστηριότητά σας είναι ορατή μόνο σε εσάς και τους διαχειριστές του διακομιστή καθώς το προφίλ σας είναι ιδιωτικό Αλλαγή ορατότητας. [settings] profile=Προφίλ @@ -736,9 +746,9 @@ uid=UID webauthn=Πιστοποίηση δύο παραγόντων (Κλειδιά Ασφαλείας) public_profile=Δημόσιο προφίλ -biography_placeholder=Πείτε μας λίγο για τον εαυτό σας! (Μπορείτε να γράψετε με Markdown) +biography_placeholder=Πείτε λίγα πράγματα για τον εαυτό σας! (Μπορείτε να γράψετε με Markdown) location_placeholder=Μοιραστείτε την κατά προσέγγιση τοποθεσία σας με άλλους -profile_desc=Ελέγξτε πώς εμφανίζεται το προφίλ σας σε άλλους χρήστες. Η κύρια διεύθυνση email σας θα χρησιμοποιηθεί για ειδοποιήσεις, ανάκτηση κωδικού πρόσβασης και λειτουργίες Git που βασίζονται στο web. +profile_desc=Σχετικά με εσάς password_username_disabled=Οι μη τοπικοί χρήστες δεν επιτρέπεται να αλλάξουν το όνομα χρήστη τους. Επικοινωνήστε με το διαχειριστή σας για περισσότερες λεπτομέρειες. full_name=Πλήρες όνομα website=Ιστοσελίδα @@ -803,7 +813,7 @@ manage_emails=Διαχείριση διευθύνσεων email manage_themes=Προεπιλεγμένο θέμα manage_openid=Διευθύνσεις OpenID email_desc=Η κύρια διεύθυνση ηλεκτρονικού ταχυδρομείου σας θα χρησιμοποιηθεί για ειδοποιήσεις, ανάκτηση του κωδικού πρόσβασης και, εφόσον δεν είναι κρυμμένη, λειτουργίες Git στον ιστότοπο. -theme_desc=Αυτό θα είναι το προεπιλεγμένο θέμα διεπαφής σας σε όλη την ιστοσελίδα. +theme_desc=Αυτό θα είναι το προεπιλεγμένο θέμα διεπαφής σας όταν είστε συνδεδεμένοι. primary=Κύριο activated=Ενεργό requires_activation=Απαιτείται ενεργοποίηση @@ -1038,13 +1048,14 @@ pronouns = Αντωνυμίες pronouns_custom = κάτι άλλο pronouns_unspecified = Απροσδιόριστες hints = Συμβουλές -additional_repo_units_hint_description = Εμφάνιση κουμπιού «Προσθήκη μονάδων...» σε αποθετήρια που δεν έχουν ενεργοποιημένες όλες τις διαθέσιμες μονάδες. +additional_repo_units_hint_description = Εμφάνιση υπόδειξης «Προσθήκη μονάδων...» σε αποθετήρια που δεν έχουν ενεργοποιημένες όλες τις διαθέσιμες μονάδες. update_hints = Ενημέρωση συμβουλών update_hints_success = Οι συμβουλές ενημερώθηκαν. language.title = Προεπιλεγμένη γλώσσα keep_activity_private.description = Η δημόσια δραστηριότητά σας θα είναι ορατή μόνο σε εσάς και στους διαχειριστές. language.localization_project = Βοηθήστε μας να μεταφράσουμε το Forgejo στην γλώσσα σας! Περισσότερες πληροφορίες. language.description = Από εδώ και στο εξής, αυτή η γλώσσα θα χρησιμοποιείται από προεπιλογή για τον λογαριασμό σας. +pronouns_custom_label = Ειδικές αντωνυμίες [repo] new_repo_helper=Ένα repository περιέχει όλα τα αρχεία έργου, συμπεριλαμβανομένου του ιστορικού εκδόσεων. Έχετε ήδη ένα που φιλοξενείται κάπου αλλού; Μεταφορά αποθετηρίου. @@ -3929,6 +3940,8 @@ exact = Ακριβής milestone_kind = Αναζήτηση ορόσημων... union = Ένωση union_tooltip = Να συμπεριληφθούν αποτελέσματα που περιέχουν οποιαδήποτε από τις λέξεις που έχουν εισαχθεί και διαχωριστεί με κενό +regexp = Κανονική Έκφραση +regexp_tooltip = Ερμηνεία του όρου αναζήτησης ως κανονική έκφραση [munits.data] mib = MiB diff --git a/options/locale/locale_fi-FI.ini b/options/locale/locale_fi-FI.ini index 12f88a5847..43cd2b3aa9 100644 --- a/options/locale/locale_fi-FI.ini +++ b/options/locale/locale_fi-FI.ini @@ -517,6 +517,10 @@ account_security_caution.text_1 = Jos se olit sinä, voit jättää tämän vies issue.action.approve = @%[1]s hyväksyi tämän vetopyynnön. issue.action.review = @%[1]s kommentoi tätä vetopyyntöä. issue.action.ready_for_review = @%[1]s merkitsi tämän vetopyynnön valmiiksi katselmointia varten. +totp_disabled.text_1 = Tilisi aikapohjainen kertakäyttösalasana (TOTP) poistettiin käytöstä. +issue.action.close = @%[1]s sulki ongelman #%[2]d. +issue.action.reopen = @%[1]s avasi uudelleen ongelman #%[2]d. +admin.new_user.text = Napsauta tästä hallitaksesi tätä käyttäjää ylläpitonäkymästä. @@ -2011,6 +2015,8 @@ issues.time_spent_total = Käytetty kokonaisaika settings.webhook.test_delivery_desc = Testaa tätä webkoukkua tekaistulla tapahtumalla. pulls.switch_comparison_type = Vaihda vertailutyyppiä settings.hooks_desc = Webkoukut tekevät automaattisesti HTTP POST -pyyntöjä palvelimelle, kun jotkin Forgejo-tapahtumat käynnistyvät. Lue lisää webkoukkujen oppaasta. +issues.num_participants_one = %d osallistuja +issues.reference_link = Viittaus: %s diff --git a/options/locale/locale_fil.ini b/options/locale/locale_fil.ini index 9a001ee81a..9d0ec9f308 100644 --- a/options/locale/locale_fil.ini +++ b/options/locale/locale_fil.ini @@ -748,7 +748,7 @@ social = Mga social account biography_placeholder = Sabihin sa iba ng kaunti tungkol sa iyong sarili! (Sinusuportahan ang Markdown) change_username_prompt = Tandaan: Ang pagpalit ng username ay papalitan din ang URL ng iyong account. organization = Mga Organisasyon -profile_desc = Kontrolin kung paano ipinapakita ang iyong profile sa ibang mga gumagamit. Ang iyong pangunahing email address ay gagamitin para sa mga abiso, pagbawi ng password at mga Git operation na batay sa web. +profile_desc = Tungkol sa iyo hidden_comment_types_description = Ang mga uri ng komento na naka-check dito ay hindi ipapakita sa loob ng mga pahina ng isyu. Halimbawa ang pag-check ng "Label" ay tatanggalin lahat ng mga "Idinagdag/tinanggal ni ang
From a23cf58d47d3c5424216f743cade92a01e3120ae Mon Sep 17 00:00:00 2001 From: 0ko <0ko@noreply.codeberg.org> Date: Sun, 5 Jan 2025 18:46:47 +0000 Subject: [PATCH 2469/3435] ui: switch redesign (#6459) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6459 Reviewed-by: Gusted --- templates/repo/issue/navbar.tmpl | 4 ++-- templates/repo/issue/openclose.tmpl | 2 +- web_src/css/index.css | 1 + web_src/css/modules/switch.css | 24 ++++++++++++++++++++++++ 4 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 web_src/css/modules/switch.css diff --git a/templates/repo/issue/navbar.tmpl b/templates/repo/issue/navbar.tmpl index 30e42c77cc..8310d3a33f 100644 --- a/templates/repo/issue/navbar.tmpl +++ b/templates/repo/issue/navbar.tmpl @@ -1,4 +1,4 @@ - +
diff --git a/templates/repo/issue/openclose.tmpl b/templates/repo/issue/openclose.tmpl index 4c2d69355b..efe4fa6756 100644 --- a/templates/repo/issue/openclose.tmpl +++ b/templates/repo/issue/openclose.tmpl @@ -1,4 +1,4 @@ -