From 1d3240887c519a04c13bcd7e852c6d6ad1cb00b5 Mon Sep 17 00:00:00 2001 From: Mai-Lapyst Date: Fri, 15 Mar 2024 13:44:42 +0100 Subject: [PATCH 001/426] 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 002/426] 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 003/426] 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 004/426] 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 005/426] 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 006/426] 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 007/426] 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 008/426] 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 009/426] 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 010/426] 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 011/426] 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 012/426] 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 013/426] 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 014/426] 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 015/426] "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 016/426] "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 017/426] 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 018/426] 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 019/426] [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 020/426] 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 021/426] 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 022/426] 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 023/426] [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 6647e4d53f04f8d5585090c7f0cf266713542b4b Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Thu, 28 Mar 2024 17:29:32 +0100 Subject: [PATCH 024/426] [I18N] make merge-locales & build/merge-forgejo-locales.go are noop Instead of failing or do things that could be damaging, this script prints a deprecation notice. It fixes the unnecessary breaking change introduced in `[I18n] tooling and process`. https://github.com/NixOS/nixpkgs/blob/master/pkgs/applications/version-management/forgejo/default.nix#L80 $ make merge-locales NOT NEEDED: THIS IS A NOOP AS OF Forgejo 7.0 BUT KEPT FOR BACKWARD COMPATIBILITY $ go run build/merge-forgejo-locales.go NOT NEEDED: THIS IS A NOOP AS OF Forgejo 7.0 BUT KEPT FOR BACKWARD COMPATIBILITY Also remove the build/crowdin-to-weblate.sh script that was never needed. --- Makefile | 4 ++ build/crowdin-to-weblate.sh | 27 --------- build/merge-forgejo-locales.go | 100 +-------------------------------- 3 files changed, 7 insertions(+), 124 deletions(-) delete mode 100755 build/crowdin-to-weblate.sh diff --git a/Makefile b/Makefile index b1c4864c7a..70a7f906eb 100644 --- a/Makefile +++ b/Makefile @@ -814,6 +814,10 @@ generate-go: $(TAGS_PREREQ) @echo "Running go generate..." @CC= GOOS= GOARCH= $(GO) generate -tags '$(TAGS)' $(GO_PACKAGES) +.PHONY: merge-locales +merge-locales: + @echo "NOT NEEDED: THIS IS A NOOP AS OF Forgejo 7.0 BUT KEPT FOR BACKWARD COMPATIBILITY" + .PHONY: security-check security-check: go run $(GOVULNCHECK_PACKAGE) ./... diff --git a/build/crowdin-to-weblate.sh b/build/crowdin-to-weblate.sh deleted file mode 100755 index 877b5d9e07..0000000000 --- a/build/crowdin-to-weblate.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash -# Copyright 2024 The Forgejo Authors -# SPDX-License-Identifier: MIT - -D=/tmp/crowdin-to-weblate -mkdir -p $D - -function checkout() { - if test -d $D/gitea ; then - git -C $D/gitea reset --hard - return - fi - - git clone --depth 1 https://github.com/go-gitea/gitea $D/gitea -} - -function replace() { - go run build/merge-forgejo-locales.go $D/gitea/options/locale - cp -a $D/gitea/options/locale/* options/locale -} - -function run() { - checkout - replace -} - -"$@" diff --git a/build/merge-forgejo-locales.go b/build/merge-forgejo-locales.go index eebe12578b..05c8d9b5e9 100644 --- a/build/merge-forgejo-locales.go +++ b/build/merge-forgejo-locales.go @@ -1,4 +1,4 @@ -// Copyright 2022 The Forgejo Authors c/o Codeberg e.V.. All rights reserved. +// Copyright 2024 The Forgejo Authors c/o Codeberg e.V.. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. @@ -7,103 +7,9 @@ package main import ( - "bufio" - "log" - "os" - "regexp" - "strings" + "fmt" ) -const ( - trimPrefix = "gitea_" - sourceFolder = "options/locales/" -) - -// returns list of locales, still containing the file extension! -func generate_locale_list() []string { - localeFiles, _ := os.ReadDir(sourceFolder) - locales := []string{} - for _, localeFile := range localeFiles { - if !localeFile.IsDir() && strings.HasPrefix(localeFile.Name(), trimPrefix) { - locales = append(locales, strings.TrimPrefix(localeFile.Name(), trimPrefix)) - } - } - return locales -} - -// replace all occurrences of Gitea with Forgejo -func renameGiteaForgejo(filename string) []byte { - file, err := os.Open(filename) - if err != nil { - panic(err) - } - - replacements := []string{ - "Gitea", "Forgejo", - "https://docs.gitea.com/installation/install-from-binary", "https://forgejo.org/download/#installation-from-binary", - "https://github.com/go-gitea/gitea/tree/master/docker", "https://forgejo.org/download/#container-image", - "https://docs.gitea.com/installation/install-from-package", "https://forgejo.org/download", - "https://code.gitea.io/gitea", "https://forgejo.org/download", - "code.gitea.io/gitea", "Forgejo", - `GitHub`, `Codeberg`, - "https://github.com/go-gitea/gitea", "https://codeberg.org/forgejo/forgejo", - "https://blog.gitea.io", "https://forgejo.org/news", - "https://docs.gitea.com/usage/protected-tags", "https://forgejo.org/docs/latest/user/protection/#protected-tags", - "https://docs.gitea.com/usage/webhooks", "https://forgejo.org/docs/latest/user/webhooks/", - } - replacer := strings.NewReplacer(replacements...) - replaced := make(map[string]bool, len(replacements)/2) - count_replaced := func(original string) { - for i := 0; i < len(replacements); i += 2 { - if strings.Contains(original, replacements[i]) { - replaced[replacements[i]] = true - } - } - } - - out := make([]byte, 0, 1024) - scanner := bufio.NewScanner(file) - scanner.Split(bufio.ScanLines) - for scanner.Scan() { - line := scanner.Text() - - if strings.HasPrefix(line, "license_desc=") { - line = strings.Replace(line, "GitHub", "Forgejo", 1) - } - - if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") { - out = append(out, []byte(line+"\n")...) - } else if strings.HasPrefix(line, "settings.web_hook_name_gitea") { - out = append(out, []byte(line+"\n")...) - out = append(out, []byte("settings.web_hook_name_forgejo = Forgejo\n")...) - } else if strings.HasPrefix(line, "migrate.gitea.description") { - re := regexp.MustCompile(`(.*Gitea)`) - out = append(out, []byte(re.ReplaceAllString(line, "${1}/Forgejo")+"\n")...) - } else { - count_replaced(line) - out = append(out, []byte(replacer.Replace(line)+"\n")...) - } - } - file.Close() - if strings.HasSuffix(filename, "gitea_en-US.ini") { - for i := 0; i < len(replacements); i += 2 { - if replaced[replacements[i]] == false { - log.Fatalf("%s was never used to replace something in %s, it is obsolete and must be updated", replacements[i], filename) - } - } - } - return out -} - func main() { - d := os.Args[1] - files, err := os.ReadDir(d) - if err != nil { - log.Fatal(err) - } - - for _, f := range files { - p := d + "/" + f.Name() - os.WriteFile(p, renameGiteaForgejo(p), 0o644) - } + fmt.Println("NOT NEEDED: THIS IS A NOOP AS OF Forgejo 7.0 BUT KEPT FOR BACKWARD COMPATIBILITY") } From 1ebf4abddc05bb74d3187d01eda2384bba4e70f5 Mon Sep 17 00:00:00 2001 From: Gusted Date: Thu, 28 Mar 2024 22:27:53 +0100 Subject: [PATCH 025/426] [BUG] Use correct format - `%w` is to wrap errors, but can only be used by `fmt.Errorf`. Instead use `%v` to display the error. - Regression of #2763 Before: [E] failed to run attr-check. Error: %!w(*exec.ExitError=&{0xc006568e28 []}) Stderr: fatal: this operation must be run in a work tree After: [E] failed to run attr-check. Error: exit status 128 Stderr: fatal: this operation must be run in a work tree --- modules/git/repo_attribute.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/git/repo_attribute.go b/modules/git/repo_attribute.go index 7651a4434c..0008fcfe11 100644 --- a/modules/git/repo_attribute.go +++ b/modules/git/repo_attribute.go @@ -268,7 +268,7 @@ func (repo *Repository) GitAttributeChecker(treeish string, attributes ...string if err != nil && // If there is an error we need to return but: cmd.parentContext.Err() != err && // 1. Ignore the context error if the context is cancelled or exceeds the deadline (RunWithContext could return c.ctx.Err() which is Canceled or DeadlineExceeded) err.Error() != "signal: killed" { // 2. We should not pass up errors due to the program being killed - log.Error("failed to run attr-check. Error: %w\nStderr: %s", err, stdErr.String()) + log.Error("failed to run attr-check. Error: %v\nStderr: %s", err, stdErr.String()) } }() From 79b70893601c33a33d8d44eb0421797dfd846a47 Mon Sep 17 00:00:00 2001 From: Gusted Date: Thu, 28 Mar 2024 21:41:52 +0100 Subject: [PATCH 026/426] [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 2c8bcc163e1ed047af7c43edee9ca25f250df30e Mon Sep 17 00:00:00 2001 From: Gusted Date: Fri, 29 Mar 2024 00:20:21 +0100 Subject: [PATCH 027/426] [REFACTOR] PKT protocol - Use `Fprintf` to convert to hex and do padding. Simplifies the code. - Use `Read()` and `io.ReadFull` instead of `ReadByte()`. Should improve performance and allows for cleaner code. - s/pktLineTypeUnknow/pktLineTypeUnknown. - Disallow empty Pkt line per the specification. - Disallow too large Pkt line per the specification. - Add unit tests. --- cmd/hook.go | 54 ++++++++++++++---------------- cmd/hook_test.go | 85 ++++++++++++++++++++++++++++++++++++------------ cmd/serv.go | 6 +++- 3 files changed, 93 insertions(+), 52 deletions(-) diff --git a/cmd/hook.go b/cmd/hook.go index 966e4a57ca..3be480618f 100644 --- a/cmd/hook.go +++ b/cmd/hook.go @@ -583,7 +583,7 @@ Forgejo or set your environment appropriately.`, "") for { // note: pktLineTypeUnknow means pktLineTypeFlush and pktLineTypeData all allowed - rs, err = readPktLine(ctx, reader, pktLineTypeUnknow) + rs, err = readPktLine(ctx, reader, pktLineTypeUnknown) if err != nil { return err } @@ -604,7 +604,7 @@ Forgejo or set your environment appropriately.`, "") if hasPushOptions { for { - rs, err = readPktLine(ctx, reader, pktLineTypeUnknow) + rs, err = readPktLine(ctx, reader, pktLineTypeUnknown) if err != nil { return err } @@ -699,8 +699,8 @@ Forgejo or set your environment appropriately.`, "") type pktLineType int64 const ( - // UnKnow type - pktLineTypeUnknow pktLineType = 0 + // Unknown type + pktLineTypeUnknown pktLineType = 0 // flush-pkt "0000" pktLineTypeFlush pktLineType = iota // data line @@ -714,22 +714,16 @@ type gitPktLine struct { Data []byte } +// Reads an Pkt-Line from `in`. If requestType is not unknown, it will a func readPktLine(ctx context.Context, in *bufio.Reader, requestType pktLineType) (*gitPktLine, error) { - var ( - err error - r *gitPktLine - ) - - // read prefix + // Read length prefix lengthBytes := make([]byte, 4) - for i := 0; i < 4; i++ { - lengthBytes[i], err = in.ReadByte() - if err != nil { - return nil, fail(ctx, "Protocol: stdin error", "Pkt-Line: read stdin failed : %v", err) - } + if n, err := in.Read(lengthBytes); n != 4 || err != nil { + return nil, fail(ctx, "Protocol: stdin error", "Pkt-Line: read stdin failed : %v", err) } - r = new(gitPktLine) + var err error + r := &gitPktLine{} r.Length, err = strconv.ParseUint(string(lengthBytes), 16, 32) if err != nil { return nil, fail(ctx, "Protocol: format parse error", "Pkt-Line format is wrong :%v", err) @@ -748,11 +742,8 @@ func readPktLine(ctx context.Context, in *bufio.Reader, requestType pktLineType) } r.Data = make([]byte, r.Length-4) - for i := range r.Data { - r.Data[i], err = in.ReadByte() - if err != nil { - return nil, fail(ctx, "Protocol: data error", "Pkt-Line: read stdin failed : %v", err) - } + if n, err := io.ReadFull(in, r.Data); uint64(n) != r.Length-4 || err != nil { + return nil, fail(ctx, "Protocol: stdin error", "Pkt-Line: read stdin failed : %v", err) } r.Type = pktLineTypeData @@ -768,20 +759,23 @@ func writeFlushPktLine(ctx context.Context, out io.Writer) error { return nil } +// Write an Pkt-Line based on `data` to `out` according to the specifcation. +// https://git-scm.com/docs/protocol-common func writeDataPktLine(ctx context.Context, out io.Writer, data []byte) error { - hexchar := []byte("0123456789abcdef") - hex := func(n uint64) byte { - return hexchar[(n)&15] + // Implementations SHOULD NOT send an empty pkt-line ("0004"). + if len(data) == 0 { + return fail(ctx, "Protocol: write error", "Not allowed to write empty Pkt-Line") } length := uint64(len(data) + 4) - tmp := make([]byte, 4) - tmp[0] = hex(length >> 12) - tmp[1] = hex(length >> 8) - tmp[2] = hex(length >> 4) - tmp[3] = hex(length) - lr, err := out.Write(tmp) + // The maximum length of a pkt-line’s data component is 65516 bytes. + // Implementations MUST NOT send pkt-line whose length exceeds 65520 (65516 bytes of payload + 4 bytes of length data). + if length > 65520 { + return fail(ctx, "Protocol: write error", "Pkt-Line exceeds maximum of 65520 bytes") + } + + lr, err := fmt.Fprintf(out, "%04x", length) if err != nil || lr != 4 { return fail(ctx, "Protocol: write error", "Pkt-Line response failed: %v", err) } diff --git a/cmd/hook_test.go b/cmd/hook_test.go index 91f24ff2b4..d4e16dc411 100644 --- a/cmd/hook_test.go +++ b/cmd/hook_test.go @@ -14,29 +14,72 @@ import ( ) func TestPktLine(t *testing.T) { - // test read ctx := context.Background() - s := strings.NewReader("0000") - r := bufio.NewReader(s) - result, err := readPktLine(ctx, r, pktLineTypeFlush) - assert.NoError(t, err) - assert.Equal(t, pktLineTypeFlush, result.Type) - s = strings.NewReader("0006a\n") - r = bufio.NewReader(s) - result, err = readPktLine(ctx, r, pktLineTypeData) - assert.NoError(t, err) - assert.Equal(t, pktLineTypeData, result.Type) - assert.Equal(t, []byte("a\n"), result.Data) + t.Run("Read", func(t *testing.T) { + s := strings.NewReader("0000") + r := bufio.NewReader(s) + result, err := readPktLine(ctx, r, pktLineTypeFlush) + assert.NoError(t, err) + assert.Equal(t, pktLineTypeFlush, result.Type) - // test write - w := bytes.NewBuffer([]byte{}) - err = writeFlushPktLine(ctx, w) - assert.NoError(t, err) - assert.Equal(t, []byte("0000"), w.Bytes()) + s = strings.NewReader("0006a\n") + r = bufio.NewReader(s) + result, err = readPktLine(ctx, r, pktLineTypeData) + assert.NoError(t, err) + assert.Equal(t, pktLineTypeData, result.Type) + assert.Equal(t, []byte("a\n"), result.Data) - w.Reset() - err = writeDataPktLine(ctx, w, []byte("a\nb")) - assert.NoError(t, err) - assert.Equal(t, []byte("0007a\nb"), w.Bytes()) + s = strings.NewReader("0004") + r = bufio.NewReader(s) + result, err = readPktLine(ctx, r, pktLineTypeData) + assert.Error(t, err) + assert.Nil(t, result) + + data := strings.Repeat("x", 65516) + r = bufio.NewReader(strings.NewReader("fff0" + data)) + result, err = readPktLine(ctx, r, pktLineTypeData) + assert.NoError(t, err) + assert.Equal(t, pktLineTypeData, result.Type) + assert.Equal(t, []byte(data), result.Data) + + r = bufio.NewReader(strings.NewReader("fff1a")) + result, err = readPktLine(ctx, r, pktLineTypeData) + assert.Error(t, err) + assert.Nil(t, result) + }) + + t.Run("Write", func(t *testing.T) { + w := bytes.NewBuffer([]byte{}) + err := writeFlushPktLine(ctx, w) + assert.NoError(t, err) + assert.Equal(t, []byte("0000"), w.Bytes()) + + w.Reset() + err = writeDataPktLine(ctx, w, []byte("a\nb")) + assert.NoError(t, err) + assert.Equal(t, []byte("0007a\nb"), w.Bytes()) + + w.Reset() + data := bytes.Repeat([]byte{0x05}, 288) + err = writeDataPktLine(ctx, w, data) + assert.NoError(t, err) + assert.Equal(t, append([]byte("0124"), data...), w.Bytes()) + + w.Reset() + err = writeDataPktLine(ctx, w, nil) + assert.Error(t, err) + assert.Empty(t, w.Bytes()) + + w.Reset() + data = bytes.Repeat([]byte{0x64}, 65516) + err = writeDataPktLine(ctx, w, data) + assert.NoError(t, err) + assert.Equal(t, append([]byte("fff0"), data...), w.Bytes()) + + w.Reset() + err = writeDataPktLine(ctx, w, bytes.Repeat([]byte{0x64}, 65516+1)) + assert.Error(t, err) + assert.Empty(t, w.Bytes()) + }) } diff --git a/cmd/serv.go b/cmd/serv.go index 9d26515254..d5c54f91b8 100644 --- a/cmd/serv.go +++ b/cmd/serv.go @@ -14,6 +14,7 @@ import ( "regexp" "strconv" "strings" + "testing" "time" "unicode" @@ -106,7 +107,10 @@ func fail(ctx context.Context, userMessage, logMsgFmt string, args ...any) error logMsg = userMessage + ". " + logMsg } } - _ = private.SSHLog(ctx, true, logMsg) + // Don't send an log if this is done in a test and no InternalToken is set. + if !testing.Testing() || setting.InternalToken != "" { + _ = private.SSHLog(ctx, true, logMsg) + } } return cli.Exit("", 1) } From 3ba127c61849c2bec1efed0149c0d7ea1aaaf3c3 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Mon, 25 Mar 2024 15:51:23 +0800 Subject: [PATCH 028/426] [Port] gitea#29999: Fix Add/Remove WIP on pull request title failure Fix #29997 --- Conflict resolution: - Use Forgejo's user blocking feature (services/issue/issue.go) - Trivial (tests/integration/pull_review_test.go) Ref: https://codeberg.org/forgejo/forgejo/pulls/2872 (cherry picked from commit 475b6e839caa88994318f905f0965c3b418f876a) --- services/issue/issue.go | 35 +++++++++++++++------------ services/issue/pull.go | 16 +++++++----- tests/integration/pull_review_test.go | 16 +++++++++++- 3 files changed, 44 insertions(+), 23 deletions(-) diff --git a/services/issue/issue.go b/services/issue/issue.go index 995015da25..5e726176d0 100644 --- a/services/issue/issue.go +++ b/services/issue/issue.go @@ -17,6 +17,7 @@ import ( system_model "code.gitea.io/gitea/models/system" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/timeutil" notify_service "code.gitea.io/gitea/services/notify" @@ -64,25 +65,27 @@ func ChangeTitle(ctx context.Context, issue *issues_model.Issue, doer *user_mode return nil } - var reviewNotifers []*ReviewRequestNotifier - - if err := db.WithTx(ctx, func(ctx context.Context) error { - if err := issues_model.ChangeIssueTitle(ctx, issue, doer, oldTitle); err != nil { - return err - } - - if issue.IsPull && issues_model.HasWorkInProgressPrefix(oldTitle) && !issues_model.HasWorkInProgressPrefix(title) { - var err error - reviewNotifers, err = PullRequestCodeOwnersReview(ctx, issue, issue.PullRequest) - if err != nil { - return err - } - } - return nil - }); err != nil { + if err := issue.LoadRepo(ctx); err != nil { return err } + if user_model.IsBlockedMultiple(ctx, []int64{issue.PosterID, issue.Repo.OwnerID}, doer.ID) { + return user_model.ErrBlockedByUser + } + + if err := issues_model.ChangeIssueTitle(ctx, issue, doer, oldTitle); err != nil { + return err + } + + var reviewNotifers []*ReviewRequestNotifier + if issue.IsPull && issues_model.HasWorkInProgressPrefix(oldTitle) && !issues_model.HasWorkInProgressPrefix(title) { + var err error + reviewNotifers, err = PullRequestCodeOwnersReview(ctx, issue, issue.PullRequest) + if err != nil { + log.Error("PullRequestCodeOwnersReview: %v", err) + } + } + notify_service.IssueChangeTitle(ctx, doer, issue, oldTitle) ReviewRequestNotify(ctx, issue, issue.Poster, reviewNotifers) diff --git a/services/issue/pull.go b/services/issue/pull.go index 8e85c11e9b..b7b63a7024 100644 --- a/services/issue/pull.go +++ b/services/issue/pull.go @@ -40,7 +40,7 @@ type ReviewRequestNotifier struct { ReviewTeam *org_model.Team } -func PullRequestCodeOwnersReview(ctx context.Context, pull *issues_model.Issue, pr *issues_model.PullRequest) ([]*ReviewRequestNotifier, error) { +func PullRequestCodeOwnersReview(ctx context.Context, issue *issues_model.Issue, pr *issues_model.PullRequest) ([]*ReviewRequestNotifier, error) { files := []string{"CODEOWNERS", "docs/CODEOWNERS", ".gitea/CODEOWNERS"} if pr.IsWorkInProgress(ctx) { @@ -90,7 +90,7 @@ func PullRequestCodeOwnersReview(ctx context.Context, pull *issues_model.Issue, // https://github.com/go-gitea/gitea/issues/29763, we need to get the files changed // between the merge base and the head commit but not the base branch and the head commit - changedFiles, err := repo.GetFilesChangedBetween(mergeBase, pr.HeadCommitID) + changedFiles, err := repo.GetFilesChangedBetween(mergeBase, pr.GetGitRefName()) if err != nil { return nil, err } @@ -112,9 +112,13 @@ func PullRequestCodeOwnersReview(ctx context.Context, pull *issues_model.Issue, notifiers := make([]*ReviewRequestNotifier, 0, len(uniqUsers)+len(uniqTeams)) + if err := issue.LoadPoster(ctx); err != nil { + return nil, err + } + for _, u := range uniqUsers { - if u.ID != pull.Poster.ID { - comment, err := issues_model.AddReviewRequest(ctx, pull, u, pull.Poster) + if u.ID != issue.Poster.ID { + comment, err := issues_model.AddReviewRequest(ctx, issue, u, issue.Poster) if err != nil { log.Warn("Failed add assignee user: %s to PR review: %s#%d, error: %s", u.Name, pr.BaseRepo.Name, pr.ID, err) return nil, err @@ -122,12 +126,12 @@ func PullRequestCodeOwnersReview(ctx context.Context, pull *issues_model.Issue, notifiers = append(notifiers, &ReviewRequestNotifier{ Comment: comment, IsAdd: true, - Reviwer: pull.Poster, + Reviwer: u, }) } } for _, t := range uniqTeams { - comment, err := issues_model.AddTeamReviewRequest(ctx, pull, t, pull.Poster) + comment, err := issues_model.AddTeamReviewRequest(ctx, issue, t, issue.Poster) if err != nil { log.Warn("Failed add assignee team: %s to PR review: %s#%d, error: %s", t.Name, pr.BaseRepo.Name, pr.ID, err) return nil, err diff --git a/tests/integration/pull_review_test.go b/tests/integration/pull_review_test.go index 1400ad1d73..82e709382d 100644 --- a/tests/integration/pull_review_test.go +++ b/tests/integration/pull_review_test.go @@ -16,6 +16,7 @@ import ( "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" + issue_service "code.gitea.io/gitea/services/issue" repo_service "code.gitea.io/gitea/services/repository" files_service "code.gitea.io/gitea/services/repository/files" "code.gitea.io/gitea/tests" @@ -303,8 +304,21 @@ func TestPullView_CodeOwner(t *testing.T) { session := loginUser(t, "user2") testPullCreate(t, session, "user2", "test_codeowner", false, repo.DefaultBranch, "codeowner-basebranch", "Test Pull Request") - pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadBranch: "codeowner-basebranch"}) + pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadRepoID: repo.ID, HeadBranch: "codeowner-basebranch"}) unittest.AssertExistsIf(t, true, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 5}) + assert.NoError(t, pr.LoadIssue(db.DefaultContext)) + + err := issue_service.ChangeTitle(db.DefaultContext, pr.Issue, user2, "[WIP] Test Pull Request") + assert.NoError(t, err) + prUpdated1 := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: pr.ID}) + assert.NoError(t, prUpdated1.LoadIssue(db.DefaultContext)) + assert.EqualValues(t, "[WIP] Test Pull Request", prUpdated1.Issue.Title) + + err = issue_service.ChangeTitle(db.DefaultContext, prUpdated1.Issue, user2, "Test Pull Request2") + assert.NoError(t, err) + prUpdated2 := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: pr.ID}) + assert.NoError(t, prUpdated2.LoadIssue(db.DefaultContext)) + assert.EqualValues(t, "Test Pull Request2", prUpdated2.Issue.Title) }) // change the default branch CODEOWNERS file to change README.md's codeowner From 88800059397596e75786f128e6b72dd8a5ef281e Mon Sep 17 00:00:00 2001 From: Gusted Date: Fri, 29 Mar 2024 12:55:37 +0100 Subject: [PATCH 029/426] [THEME] Increase contrast of code block - Make the background color of code blocks a bit darker, so they are more distinctive when used in containers that use `--color-box-body` as background color (for example, comments). - Ref: https://codeberg.org/Codeberg/Community/issues/1523 --- web_src/css/themes/theme-forgejo-dark.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web_src/css/themes/theme-forgejo-dark.css b/web_src/css/themes/theme-forgejo-dark.css index f27db7ac3b..819a366f27 100644 --- a/web_src/css/themes/theme-forgejo-dark.css +++ b/web_src/css/themes/theme-forgejo-dark.css @@ -205,7 +205,7 @@ --color-menu: var(--steel-700); --color-card: var(--steel-700); --color-markup-table-row: #ffffff06; - --color-markup-code-block: var(--steel-800); + --color-markup-code-block: var(--steel-850); --color-button: var(--steel-600); --color-code-bg: var(--steel-750); --color-code-sidebar-bg: var(--steel-600); From 92aaa14117f5fa83838590f25e3bbe0299e02bfb Mon Sep 17 00:00:00 2001 From: Gusted Date: Fri, 29 Mar 2024 12:55:01 +0100 Subject: [PATCH 030/426] [BUG] Allow to exclude files in dump - Move the skip path check outside the `file.IsDir()` condition, so it can be used to skip files. - Add unit tests. - Resolves https://codeberg.org/forgejo/forgejo/issues/1397 - Ref: https://codeberg.org/forgejo/forgejo/pulls/1438 --- cmd/dump.go | 18 +++++--- cmd/dump_test.go | 117 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+), 7 deletions(-) create mode 100644 cmd/dump_test.go diff --git a/cmd/dump.go b/cmd/dump.go index 6fbd049191..3ea92aa112 100644 --- a/cmd/dump.go +++ b/cmd/dump.go @@ -454,14 +454,18 @@ func addRecursiveExclude(w archiver.Writer, insidePath, absPath string, excludeA for _, file := range files { currentAbsPath := filepath.Join(absPath, file.Name()) currentInsidePath := path.Join(insidePath, file.Name()) + + if util.SliceContainsString(excludeAbsPath, currentAbsPath) { + log.Debug("Skipping %q because matched an excluded path.", currentAbsPath) + continue + } + if file.IsDir() { - if !util.SliceContainsString(excludeAbsPath, currentAbsPath) { - if err := addFile(w, currentInsidePath, currentAbsPath, false); err != nil { - return err - } - if err = addRecursiveExclude(w, currentInsidePath, currentAbsPath, excludeAbsPath, verbose); err != nil { - return err - } + if err := addFile(w, currentInsidePath, currentAbsPath, false); err != nil { + return err + } + if err = addRecursiveExclude(w, currentInsidePath, currentAbsPath, excludeAbsPath, verbose); err != nil { + return err } } else { // only copy regular files and symlink regular files, skip non-regular files like socket/pipe/... diff --git a/cmd/dump_test.go b/cmd/dump_test.go new file mode 100644 index 0000000000..7b83c70f09 --- /dev/null +++ b/cmd/dump_test.go @@ -0,0 +1,117 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "io" + "os" + "testing" + + "github.com/mholt/archiver/v3" + "github.com/stretchr/testify/assert" +) + +type mockArchiver struct { + addedFiles []string +} + +func (mockArchiver) Create(out io.Writer) error { + return nil +} + +func (m *mockArchiver) Write(f archiver.File) error { + m.addedFiles = append(m.addedFiles, f.Name()) + return nil +} + +func (mockArchiver) Close() error { + return nil +} + +func TestAddRecursiveExclude(t *testing.T) { + t.Run("Empty", func(t *testing.T) { + dir := t.TempDir() + archiver := &mockArchiver{} + + err := addRecursiveExclude(archiver, "", dir, []string{}, false) + assert.NoError(t, err) + assert.Empty(t, archiver.addedFiles) + }) + + t.Run("Single file", func(t *testing.T) { + dir := t.TempDir() + err := os.WriteFile(dir+"/example", nil, 0o666) + assert.NoError(t, err) + + t.Run("No exclude", func(t *testing.T) { + archiver := &mockArchiver{} + + err = addRecursiveExclude(archiver, "", dir, nil, false) + assert.NoError(t, err) + assert.Len(t, archiver.addedFiles, 1) + assert.EqualValues(t, "example", archiver.addedFiles[0]) + }) + + t.Run("With exclude", func(t *testing.T) { + archiver := &mockArchiver{} + + err = addRecursiveExclude(archiver, "", dir, []string{dir + "/example"}, false) + assert.NoError(t, err) + assert.Empty(t, archiver.addedFiles) + }) + }) + + t.Run("File inside directory", func(t *testing.T) { + dir := t.TempDir() + err := os.MkdirAll(dir+"/deep/nested/folder", 0o750) + assert.NoError(t, err) + err = os.WriteFile(dir+"/deep/nested/folder/example", nil, 0o666) + assert.NoError(t, err) + err = os.WriteFile(dir+"/deep/nested/folder/another-file", nil, 0o666) + assert.NoError(t, err) + + t.Run("No exclude", func(t *testing.T) { + archiver := &mockArchiver{} + + err = addRecursiveExclude(archiver, "", dir, nil, false) + assert.NoError(t, err) + assert.Len(t, archiver.addedFiles, 5) + assert.EqualValues(t, "deep", archiver.addedFiles[0]) + assert.EqualValues(t, "deep/nested", archiver.addedFiles[1]) + assert.EqualValues(t, "deep/nested/folder", archiver.addedFiles[2]) + assert.EqualValues(t, "deep/nested/folder/example", archiver.addedFiles[3]) + assert.EqualValues(t, "deep/nested/folder/another-file", archiver.addedFiles[4]) + }) + + t.Run("Exclude first directory", func(t *testing.T) { + archiver := &mockArchiver{} + + err = addRecursiveExclude(archiver, "", dir, []string{dir + "/deep"}, false) + assert.NoError(t, err) + assert.Empty(t, archiver.addedFiles) + }) + + t.Run("Exclude nested directory", func(t *testing.T) { + archiver := &mockArchiver{} + + err = addRecursiveExclude(archiver, "", dir, []string{dir + "/deep/nested/folder"}, false) + assert.NoError(t, err) + assert.Len(t, archiver.addedFiles, 2) + assert.EqualValues(t, "deep", archiver.addedFiles[0]) + assert.EqualValues(t, "deep/nested", archiver.addedFiles[1]) + }) + + t.Run("Exclude file", func(t *testing.T) { + archiver := &mockArchiver{} + + err = addRecursiveExclude(archiver, "", dir, []string{dir + "/deep/nested/folder/example"}, false) + assert.NoError(t, err) + assert.Len(t, archiver.addedFiles, 4) + assert.EqualValues(t, "deep", archiver.addedFiles[0]) + assert.EqualValues(t, "deep/nested", archiver.addedFiles[1]) + assert.EqualValues(t, "deep/nested/folder", archiver.addedFiles[2]) + assert.EqualValues(t, "deep/nested/folder/another-file", archiver.addedFiles[3]) + }) + }) +} 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 031/426] 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 190383dbf82d9fa3e1c5a89078604bc04b29ae75 Mon Sep 17 00:00:00 2001 From: Gusted Date: Fri, 29 Mar 2024 14:53:56 +0100 Subject: [PATCH 032/426] [BUG] Don't delete inactive emails explicitly - `user_model.DeleteInactiveEmailAddresses` related code was added in Gogs as part to delete inactive users, however since then the related code to delete users has changed and this code now already delete email addresses of the user, it's therefore not needed anymore to `DeleteInactiveEmailAddresses`. - The call to `DeleteInactiveEmailAddresses` can actually cause issues. As the associated user might not have been deleted, because it was not older than the specified `olderThan` argument. Therefore causing a database inconsistency and lead to internal server errors if the user tries to activate their account. - Adds unit test to verify correct behavior (fails without this patch). --- models/user/email_address.go | 8 -------- services/user/user.go | 2 +- services/user/user_test.go | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/models/user/email_address.go b/models/user/email_address.go index 45bcc54aa3..85824fcdcb 100644 --- a/models/user/email_address.go +++ b/models/user/email_address.go @@ -278,14 +278,6 @@ func IsEmailUsed(ctx context.Context, email string) (bool, error) { return db.GetEngine(ctx).Where("lower_email=?", strings.ToLower(email)).Get(&EmailAddress{}) } -// DeleteInactiveEmailAddresses deletes inactive email addresses -func DeleteInactiveEmailAddresses(ctx context.Context) error { - _, err := db.GetEngine(ctx). - Where("is_activated = ?", false). - Delete(new(EmailAddress)) - return err -} - // ActivateEmail activates the email address to given user. func ActivateEmail(ctx context.Context, email *EmailAddress) error { ctx, committer, err := db.TxContext(ctx) diff --git a/services/user/user.go b/services/user/user.go index f2648db409..9dc4f6fe62 100644 --- a/services/user/user.go +++ b/services/user/user.go @@ -304,5 +304,5 @@ func DeleteInactiveUsers(ctx context.Context, olderThan time.Duration) error { } } - return user_model.DeleteInactiveEmailAddresses(ctx) + return nil } diff --git a/services/user/user_test.go b/services/user/user_test.go index 2ebcded925..9013208ed0 100644 --- a/services/user/user_test.go +++ b/services/user/user_test.go @@ -7,6 +7,7 @@ import ( "fmt" "strings" "testing" + "time" "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/auth" @@ -16,6 +17,7 @@ import ( "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/timeutil" "github.com/stretchr/testify/assert" ) @@ -184,3 +186,33 @@ func TestCreateUser_Issue5882(t *testing.T) { assert.NoError(t, DeleteUser(db.DefaultContext, v.user, false)) } } + +func TestDeleteInactiveUsers(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + // Add an inactive user older than a minute, with an associated email_address record. + oldUser := &user_model.User{Name: "OldInactive", LowerName: "oldinactive", Email: "old@example.com", CreatedUnix: timeutil.TimeStampNow().Add(-120)} + _, err := db.GetEngine(db.DefaultContext).NoAutoTime().Insert(oldUser) + assert.NoError(t, err) + oldEmail := &user_model.EmailAddress{UID: oldUser.ID, IsPrimary: true, Email: "old@example.com", LowerEmail: "old@example.com"} + err = db.Insert(db.DefaultContext, oldEmail) + assert.NoError(t, err) + + // Add an inactive user that's not older than a minute, with an associated email_address record. + newUser := &user_model.User{Name: "NewInactive", LowerName: "newinactive", Email: "new@example.com"} + err = db.Insert(db.DefaultContext, newUser) + assert.NoError(t, err) + newEmail := &user_model.EmailAddress{UID: newUser.ID, IsPrimary: true, Email: "new@example.com", LowerEmail: "new@example.com"} + err = db.Insert(db.DefaultContext, newEmail) + assert.NoError(t, err) + + err = DeleteInactiveUsers(db.DefaultContext, time.Minute) + assert.NoError(t, err) + + // User older than a minute should be deleted along with their email address. + unittest.AssertExistsIf(t, false, oldUser) + unittest.AssertExistsIf(t, false, oldEmail) + + // User not older than a minute shouldn't be deleted and their emaill address should still exist. + unittest.AssertExistsIf(t, true, newUser) + unittest.AssertExistsIf(t, true, newEmail) +} From 722e0dbac13d6dd3df66a9d1f2dbca096c57f7e7 Mon Sep 17 00:00:00 2001 From: Gusted Date: Fri, 29 Mar 2024 15:38:31 +0100 Subject: [PATCH 033/426] [THEME] Revert darker tone on labels - In #1782 I made these darker to make some elements look good, but this variable is used quite broadly which causes some issues. So reverting the darker tone to the more light tone (which is identical with what's the behavior before #1782 was merged). - Resolves https://codeberg.org/forgejo/forgejo/issues/2879 --- web_src/css/themes/theme-forgejo-dark.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web_src/css/themes/theme-forgejo-dark.css b/web_src/css/themes/theme-forgejo-dark.css index 819a366f27..0533344a8c 100644 --- a/web_src/css/themes/theme-forgejo-dark.css +++ b/web_src/css/themes/theme-forgejo-dark.css @@ -226,9 +226,9 @@ --color-nav-bg: var(--steel-900); --color-nav-hover-bg: var(--steel-600); --color-label-text: #fff; - --color-label-bg: #393939; - --color-label-hover-bg: #5f5f5f; - --color-label-active-bg: #4c4c4c; + --color-label-bg: #cacaca5b; + --color-label-hover-bg: #cacacaa0; + --color-label-active-bg: #cacacaff; --color-accent: var(--color-primary-light-1); --color-small-accent: var(--color-primary-light-5); --color-active-line: var(--color-primary-alpha-20); From f579bde69df2d4d7520c819f9b9eb198029e0670 Mon Sep 17 00:00:00 2001 From: Gusted Date: Fri, 29 Mar 2024 23:48:47 +0100 Subject: [PATCH 034/426] [CHORE] Cleanup dependency - Remove `gitea.com/lunny/dingtalk_webhook` as dependency, we only use two structs which are small enough to be recreated in Forgejo and don't need to rely on the dependency. - Existing tests (thanks @oliverpool) prove that this has no effect. --- assets/go-licenses.json | 5 ----- go.mod | 1 - go.sum | 2 -- services/webhook/dingtalk.go | 22 +++++++++++++++++----- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/assets/go-licenses.json b/assets/go-licenses.json index ad3ab6c4c1..d1062d747b 100644 --- a/assets/go-licenses.json +++ b/assets/go-licenses.json @@ -64,11 +64,6 @@ "path": "gitea.com/go-chi/session/LICENSE", "licenseText": "Apache License\nVersion 2.0, January 2004\nhttp://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n\"License\" shall mean the terms and conditions for use, reproduction, and\ndistribution as defined by Sections 1 through 9 of this document.\n\n\"Licensor\" shall mean the copyright owner or entity authorized by the copyright\nowner that is granting the License.\n\n\"Legal Entity\" shall mean the union of the acting entity and all other entities\nthat control, are controlled by, or are under common control with that entity.\nFor the purposes of this definition, \"control\" means (i) the power, direct or\nindirect, to cause the direction or management of such entity, whether by\ncontract or otherwise, or (ii) ownership of fifty percent (50%) or more of the\noutstanding shares, or (iii) beneficial ownership of such entity.\n\n\"You\" (or \"Your\") shall mean an individual or Legal Entity exercising\npermissions granted by this License.\n\n\"Source\" form shall mean the preferred form for making modifications, including\nbut not limited to software source code, documentation source, and configuration\nfiles.\n\n\"Object\" form shall mean any form resulting from mechanical transformation or\ntranslation of a Source form, including but not limited to compiled object code,\ngenerated documentation, and conversions to other media types.\n\n\"Work\" shall mean the work of authorship, whether in Source or Object form, made\navailable under the License, as indicated by a copyright notice that is included\nin or attached to the work (an example is provided in the Appendix below).\n\n\"Derivative Works\" shall mean any work, whether in Source or Object form, that\nis based on (or derived from) the Work and for which the editorial revisions,\nannotations, elaborations, or other modifications represent, as a whole, an\noriginal work of authorship. For the purposes of this License, Derivative Works\nshall not include works that remain separable from, or merely link (or bind by\nname) to the interfaces of, the Work and Derivative Works thereof.\n\n\"Contribution\" shall mean any work of authorship, including the original version\nof the Work and any modifications or additions to that Work or Derivative Works\nthereof, that is intentionally submitted to Licensor for inclusion in the Work\nby the copyright owner or by an individual or Legal Entity authorized to submit\non behalf of the copyright owner. For the purposes of this definition,\n\"submitted\" means any form of electronic, verbal, or written communication sent\nto the Licensor or its representatives, including but not limited to\ncommunication on electronic mailing lists, source code control systems, and\nissue tracking systems that are managed by, or on behalf of, the Licensor for\nthe purpose of discussing and improving the Work, but excluding communication\nthat is conspicuously marked or otherwise designated in writing by the copyright\nowner as \"Not a Contribution.\"\n\n\"Contributor\" shall mean Licensor and any individual or Legal Entity on behalf\nof whom a Contribution has been received by Licensor and subsequently\nincorporated within the Work.\n\n2. Grant of Copyright License.\n\nSubject to the terms and conditions of this License, each Contributor hereby\ngrants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,\nirrevocable copyright license to reproduce, prepare Derivative Works of,\npublicly display, publicly perform, sublicense, and distribute the Work and such\nDerivative Works in Source or Object form.\n\n3. Grant of Patent License.\n\nSubject to the terms and conditions of this License, each Contributor hereby\ngrants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,\nirrevocable (except as stated in this section) patent license to make, have\nmade, use, offer to sell, sell, import, and otherwise transfer the Work, where\nsuch license applies only to those patent claims licensable by such Contributor\nthat are necessarily infringed by their Contribution(s) alone or by combination\nof their Contribution(s) with the Work to which such Contribution(s) was\nsubmitted. If You institute patent litigation against any entity (including a\ncross-claim or counterclaim in a lawsuit) alleging that the Work or a\nContribution incorporated within the Work constitutes direct or contributory\npatent infringement, then any patent licenses granted to You under this License\nfor that Work shall terminate as of the date such litigation is filed.\n\n4. Redistribution.\n\nYou may reproduce and distribute copies of the Work or Derivative Works thereof\nin any medium, with or without modifications, and in Source or Object form,\nprovided that You meet the following conditions:\n\nYou must give any other recipients of the Work or Derivative Works a copy of\nthis License; and\nYou must cause any modified files to carry prominent notices stating that You\nchanged the files; and\nYou must retain, in the Source form of any Derivative Works that You distribute,\nall copyright, patent, trademark, and attribution notices from the Source form\nof the Work, excluding those notices that do not pertain to any part of the\nDerivative Works; and\nIf the Work includes a \"NOTICE\" text file as part of its distribution, then any\nDerivative Works that You distribute must include a readable copy of the\nattribution notices contained within such NOTICE file, excluding those notices\nthat do not pertain to any part of the Derivative Works, in at least one of the\nfollowing places: within a NOTICE text file distributed as part of the\nDerivative Works; within the Source form or documentation, if provided along\nwith the Derivative Works; or, within a display generated by the Derivative\nWorks, if and wherever such third-party notices normally appear. The contents of\nthe NOTICE file are for informational purposes only and do not modify the\nLicense. You may add Your own attribution notices within Derivative Works that\nYou distribute, alongside or as an addendum to the NOTICE text from the Work,\nprovided that such additional attribution notices cannot be construed as\nmodifying the License.\nYou may add Your own copyright statement to Your modifications and may provide\nadditional or different license terms and conditions for use, reproduction, or\ndistribution of Your modifications, or for any such Derivative Works as a whole,\nprovided Your use, reproduction, and distribution of the Work otherwise complies\nwith the conditions stated in this License.\n\n5. Submission of Contributions.\n\nUnless You explicitly state otherwise, any Contribution intentionally submitted\nfor inclusion in the Work by You to the Licensor shall be under the terms and\nconditions of this License, without any additional terms or conditions.\nNotwithstanding the above, nothing herein shall supersede or modify the terms of\nany separate license agreement you may have executed with Licensor regarding\nsuch Contributions.\n\n6. Trademarks.\n\nThis License does not grant permission to use the trade names, trademarks,\nservice marks, or product names of the Licensor, except as required for\nreasonable and customary use in describing the origin of the Work and\nreproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty.\n\nUnless required by applicable law or agreed to in writing, Licensor provides the\nWork (and each Contributor provides its Contributions) on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,\nincluding, without limitation, any warranties or conditions of TITLE,\nNON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are\nsolely responsible for determining the appropriateness of using or\nredistributing the Work and assume any risks associated with Your exercise of\npermissions under this License.\n\n8. Limitation of Liability.\n\nIn no event and under no legal theory, whether in tort (including negligence),\ncontract, or otherwise, unless required by applicable law (such as deliberate\nand grossly negligent acts) or agreed to in writing, shall any Contributor be\nliable to You for damages, including any direct, indirect, special, incidental,\nor consequential damages of any character arising as a result of this License or\nout of the use or inability to use the Work (including but not limited to\ndamages for loss of goodwill, work stoppage, computer failure or malfunction, or\nany and all other commercial damages or losses), even if such Contributor has\nbeen advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability.\n\nWhile redistributing the Work or Derivative Works thereof, You may choose to\noffer, and charge a fee for, acceptance of support, warranty, indemnity, or\nother liability obligations and/or rights consistent with this License. However,\nin accepting such obligations, You may act only on Your own behalf and on Your\nsole responsibility, not on behalf of any other Contributor, and only if You\nagree to indemnify, defend, and hold each Contributor harmless for any liability\nincurred by, or claims asserted against, such Contributor by reason of your\naccepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work\n\nTo apply the Apache License to your work, attach the following boilerplate\nnotice, with the fields enclosed by brackets \"[]\" replaced with your own\nidentifying information. (Don't include the brackets!) The text should be\nenclosed in the appropriate comment syntax for the file format. We also\nrecommend that a file or class name and description of purpose be included on\nthe same \"printed page\" as the copyright notice for easier identification within\nthird-party archives.\n\n Copyright [yyyy] [name of copyright owner]\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License." }, - { - "name": "gitea.com/lunny/dingtalk_webhook", - "path": "gitea.com/lunny/dingtalk_webhook/LICENSE", - "licenseText": "Copyright (c) 2016 The Gitea Authors\nCopyright (c) 2015 The Gogs Authors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n" - }, { "name": "gitea.com/lunny/levelqueue", "path": "gitea.com/lunny/levelqueue/LICENSE", diff --git a/go.mod b/go.mod index dbefea608c..5c39436eca 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,6 @@ require ( gitea.com/go-chi/cache v0.2.0 gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098 gitea.com/go-chi/session v0.0.0-20240316035857-16768d98ec96 - gitea.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96 gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4 github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121 github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 diff --git a/go.sum b/go.sum index ae1f89b43f..5ab9cfc5f9 100644 --- a/go.sum +++ b/go.sum @@ -62,8 +62,6 @@ gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098 h1:p2ki+WK0cIeNQuqjR gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098/go.mod h1:LjzIOHlRemuUyO7WR12fmm18VZIlCAaOt9L3yKw40pk= gitea.com/go-chi/session v0.0.0-20240316035857-16768d98ec96 h1:IFDiMBObsP6CZIRaDLd54SR6zPYAffPXiXck5Xslu0Q= gitea.com/go-chi/session v0.0.0-20240316035857-16768d98ec96/go.mod h1:0iEpFKnwO5dG0aF98O4eq6FMsAiXkNBaDIlUOlq4BtM= -gitea.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96 h1:+wWBi6Qfruqu7xJgjOIrKVQGiLUZdpKYCZewJ4clqhw= -gitea.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96/go.mod h1:VyMQP6ue6MKHM8UsOXfNfuMKD0oSAWZdXVcpHIN2yaY= gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4 h1:IFT+hup2xejHqdhS7keYWioqfmxdnfblFDTGoOwcZ+o= gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4/go.mod h1:HBqmLbz56JWpfEGG0prskAV97ATNRoj5LDmPicD22hU= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s= diff --git a/services/webhook/dingtalk.go b/services/webhook/dingtalk.go index 562e3e86eb..f4bc715e08 100644 --- a/services/webhook/dingtalk.go +++ b/services/webhook/dingtalk.go @@ -16,8 +16,6 @@ import ( "code.gitea.io/gitea/modules/util" webhook_module "code.gitea.io/gitea/modules/webhook" "code.gitea.io/gitea/services/forms" - - dingtalk "gitea.com/lunny/dingtalk_webhook" ) type dingtalkHandler struct{} @@ -42,8 +40,22 @@ func (dingtalkHandler) FormFields(bind func(any)) FormFields { } type ( - // DingtalkPayload represents - DingtalkPayload dingtalk.Payload + // DingtalkPayload represents an dingtalk payload. + DingtalkPayload struct { + MsgType string `json:"msgtype"` + Text struct { + Content string `json:"content"` + } `json:"text"` + ActionCard DingtalkActionCard `json:"actionCard"` + } + + DingtalkActionCard struct { + Text string `json:"text"` + Title string `json:"title"` + HideAvatar string `json:"hideAvatar"` + SingleTitle string `json:"singleTitle"` + SingleURL string `json:"singleURL"` + } ) // Create implements PayloadConvertor Create method @@ -195,7 +207,7 @@ func (dc dingtalkConvertor) Package(p *api.PackagePayload) (DingtalkPayload, err func createDingtalkPayload(title, text, singleTitle, singleURL string) DingtalkPayload { return DingtalkPayload{ MsgType: "actionCard", - ActionCard: dingtalk.ActionCard{ + ActionCard: DingtalkActionCard{ Text: strings.TrimSpace(text), Title: strings.TrimSpace(title), HideAvatar: "0", From 6c5534737731aa5c7663989aac8f7cce00e428e8 Mon Sep 17 00:00:00 2001 From: Gusted Date: Sat, 30 Mar 2024 01:56:25 +0100 Subject: [PATCH 035/426] [BRANDING] Move docs.gitea.com to forgejo.org/docs - Replace links from docs.gitea.com with forgejo.org/docs for those where the relevant links are available on the Forgejo documentation. - Resolves #2892 --- options/locale/locale_en-US.ini | 2 +- templates/install.tmpl | 2 +- templates/repo/actions/no_workflows.tmpl | 4 ++-- templates/repo/migrate/gitea.tmpl | 2 +- templates/user/settings/applications.tmpl | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 17a8180ec9..c83f386f2a 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -3148,7 +3148,7 @@ auths.tip.google_plus = Obtain OAuth2 client credentials from the Google API con auths.tip.openid_connect = Use the OpenID Connect Discovery URL (/.well-known/openid-configuration) to specify the endpoints auths.tip.twitter = Go to https://dev.twitter.com/apps, create an application and ensure that the “Allow this application to be used to Sign in with Twitter” option is enabled auths.tip.discord = Register a new application on https://discordapp.com/developers/applications/me -auths.tip.gitea = Register a new OAuth2 application. Guide can be found at https://docs.gitea.com/development/oauth2-provider +auths.tip.gitea = Register a new OAuth2 application. Guide can be found at https://forgejo.org/docs/latest/user/oauth2-provider auths.tip.yandex = Create a new application at https://oauth.yandex.com/client/new. Select following permissions from the "Yandex.Passport API" section: "Access to email address", "Access to user avatar" and "Access to username, first name and surname, gender" auths.tip.mastodon = Input a custom instance URL for the mastodon instance you want to authenticate with (or use the default one) auths.edit = Edit authentication source diff --git a/templates/install.tmpl b/templates/install.tmpl index b3aea39ee5..faec8cb587 100644 --- a/templates/install.tmpl +++ b/templates/install.tmpl @@ -8,7 +8,7 @@
{{template "base/alert" .}} -

{{ctx.Locale.Tr "install.docker_helper" "https://docs.gitea.com/installation/install-with-docker"}}

+

{{ctx.Locale.Tr "install.docker_helper" "https://forgejo.org/docs/latest/admin/installation-docker/"}}

diff --git a/templates/repo/actions/no_workflows.tmpl b/templates/repo/actions/no_workflows.tmpl index 009313581e..88d6e513ef 100644 --- a/templates/repo/actions/no_workflows.tmpl +++ b/templates/repo/actions/no_workflows.tmpl @@ -2,7 +2,7 @@ {{svg "octicon-no-entry" 48}}

{{ctx.Locale.Tr "actions.runs.no_workflows"}}

{{if and .CanWriteCode .CanWriteActions}} -

{{ctx.Locale.Tr "actions.runs.no_workflows.quick_start" "https://docs.gitea.com/usage/actions/quickstart/"}}

+

{{ctx.Locale.Tr "actions.runs.no_workflows.quick_start" "https://forgejo.org/docs/latest/admin/actions/"}}

{{end}} -

{{ctx.Locale.Tr "actions.runs.no_workflows.documentation" "https://docs.gitea.com/usage/actions/overview/"}}

+

{{ctx.Locale.Tr "actions.runs.no_workflows.documentation" "https://forgejo.org/docs/latest/admin/actions/"}}

diff --git a/templates/repo/migrate/gitea.tmpl b/templates/repo/migrate/gitea.tmpl index 143f220449..f2a3ae4348 100644 --- a/templates/repo/migrate/gitea.tmpl +++ b/templates/repo/migrate/gitea.tmpl @@ -21,7 +21,7 @@
{{template "repo/migrate/options" .}} diff --git a/templates/user/settings/applications.tmpl b/templates/user/settings/applications.tmpl index e43cf2ebbe..e6b70fb6d3 100644 --- a/templates/user/settings/applications.tmpl +++ b/templates/user/settings/applications.tmpl @@ -75,7 +75,7 @@ {{ctx.Locale.Tr "settings.select_permissions"}}

- {{ctx.Locale.Tr "settings.access_token_desc" (`href="/api/swagger" target="_blank"`|SafeHTML) (`href="https://docs.gitea.com/development/oauth2-provider#scopes" target="_blank"`|SafeHTML)}} + {{ctx.Locale.Tr "settings.access_token_desc" (`href="/api/swagger" target="_blank"`|SafeHTML) (`href="https://forgejo.org/docs/latest/user/token-scope/" target="_blank"`|SafeHTML)}}

Date: Sat, 30 Mar 2024 03:58:50 +0100 Subject: [PATCH 036/426] [FEAT] Remove add organization on dashboard switcher - Similair to #2593 that removed subtle add button in a place where it's strictly not needed. - Remove the "Add organization" item in the 'context dashboard switcher' dropdown. --- templates/user/dashboard/navbar.tmpl | 5 ----- 1 file changed, 5 deletions(-) diff --git a/templates/user/dashboard/navbar.tmpl b/templates/user/dashboard/navbar.tmpl index bdd26ec464..b2ee198b0a 100644 --- a/templates/user/dashboard/navbar.tmpl +++ b/templates/user/dashboard/navbar.tmpl @@ -35,11 +35,6 @@ {{end}}
- {{if .SignedUser.CanCreateOrganization}} - - {{svg "octicon-plus"}}   {{ctx.Locale.Tr "new_org"}} - - {{end}}
From a517e4aeb1146aea392d6e7f5bdfe5ed8513a61d Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sat, 30 Mar 2024 06:05:07 +0000 Subject: [PATCH 037/426] 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 0cb9ea64a659c7664ee25d1b44aacd00b99c6a2f Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sat, 30 Mar 2024 06:05:12 +0000 Subject: [PATCH 038/426] Update module github.com/opencontainers/image-spec to v1.1.0 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 5c39436eca..cc3528800d 100644 --- a/go.mod +++ b/go.mod @@ -80,7 +80,7 @@ require ( github.com/niklasfasching/go-org v1.7.0 github.com/olivere/elastic/v7 v7.0.32 github.com/opencontainers/go-digest v1.0.0 - github.com/opencontainers/image-spec v1.1.0-rc6 + github.com/opencontainers/image-spec v1.1.0 github.com/pquerna/otp v1.4.0 github.com/prometheus/client_golang v1.18.0 github.com/quasoft/websspi v1.1.2 diff --git a/go.sum b/go.sum index 5ab9cfc5f9..b3fae2a8d8 100644 --- a/go.sum +++ b/go.sum @@ -668,8 +668,8 @@ github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.1.0-rc6 h1:XDqvyKsJEbRtATzkgItUqBA7QHk58yxX1Ov9HERHNqU= -github.com/opencontainers/image-spec v1.1.0-rc6/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= +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/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= From c82bef515e81997b2691ebd7ab5449adda060e8d Mon Sep 17 00:00:00 2001 From: silverwind Date: Sun, 24 Mar 2024 17:42:49 +0100 Subject: [PATCH 039/426] Migrate margin and padding helpers to tailwind (#30043) This will conclude the refactor of 1:1 class replacements to tailwind, except `gt-hidden`. Commands ran: ```bash perl -p -i -e 's#gt-(p|m)([lrtbxy])?-0#tw-$1$2-0#g' {web_src/js,templates,routers,services}/**/* perl -p -i -e 's#gt-(p|m)([lrtbxy])?-1#tw-$1$2-0.5#g' {web_src/js,templates,routers,services}/**/* perl -p -i -e 's#gt-(p|m)([lrtbxy])?-2#tw-$1$2-1#g' {web_src/js,templates,routers,services}/**/* perl -p -i -e 's#gt-(p|m)([lrtbxy])?-3#tw-$1$2-2#g' {web_src/js,templates,routers,services}/**/* perl -p -i -e 's#gt-(p|m)([lrtbxy])?-4#tw-$1$2-4#g' {web_src/js,templates,routers,services}/**/* perl -p -i -e 's#gt-(p|m)([lrtbxy])?-5#tw-$1$2-8#g' {web_src/js,templates,routers,services}/**/* ``` (cherry picked from commit 68ec9b48592fe88765bcc3a73093d43c98b315de) Conflicts: routers/web/repo/view.go templates/base/head_navbar.tmpl templates/repo/code/recently_pushed_new_branches.tmpl templates/repo/diff/box.tmpl templates/repo/diff/compare.tmpl templates/repo/diff/conversation.tmpl templates/repo/header.tmpl templates/repo/issue/filter_list.tmpl templates/repo/issue/view_content/conversation.tmpl templates/repo/issue/view_content/sidebar.tmpl templates/repo/settings/options.tmpl templates/repo/view_file.tmpl templates/shared/user/blocked_users.tmpl templates/status/500.tmpl web_src/js/components/DashboardRepoList.vue resolved by prefering Forgejo version and applying the commands to all files --- routers/web/repo/blame.go | 2 +- routers/web/repo/issue_content_history.go | 2 +- routers/web/repo/view.go | 4 +- services/auth/source/oauth2/providers.go | 2 +- services/auth/source/oauth2/providers_base.go | 4 +- .../auth/source/oauth2/providers_openid.go | 2 +- templates/admin/config.tmpl | 4 +- templates/admin/config_settings.tmpl | 2 +- templates/admin/cron.tmpl | 2 +- templates/admin/dashboard.tmpl | 2 +- templates/admin/emails/list.tmpl | 4 +- templates/admin/org/list.tmpl | 4 +- templates/admin/queue_manage.tmpl | 2 +- templates/admin/repo/unadopted.tmpl | 4 +- templates/admin/self_check.tmpl | 6 +- templates/admin/stacktrace-row.tmpl | 10 +- templates/admin/user/edit.tmpl | 4 +- templates/base/head_navbar.tmpl | 28 +++--- templates/base/paginate.tmpl | 8 +- templates/devtest/fetch-action.tmpl | 2 +- templates/devtest/flex-list.tmpl | 4 +- templates/devtest/gitea-ui.tmpl | 12 +-- templates/explore/repo_list.tmpl | 2 +- templates/explore/search.tmpl | 4 +- templates/home.tmpl | 2 +- templates/install.tmpl | 16 +-- templates/org/follow_unfollow.tmpl | 2 +- templates/org/header.tmpl | 4 +- templates/org/home.tmpl | 2 +- templates/org/projects/list.tmpl | 2 +- templates/org/team/members.tmpl | 2 +- templates/org/team/new.tmpl | 6 +- templates/org/team/repositories.tmpl | 2 +- templates/package/metadata/alpine.tmpl | 2 +- templates/package/metadata/cargo.tmpl | 10 +- templates/package/metadata/chef.tmpl | 6 +- templates/package/metadata/composer.tmpl | 6 +- templates/package/metadata/conan.tmpl | 8 +- templates/package/metadata/conda.tmpl | 8 +- templates/package/metadata/container.tmpl | 14 +-- templates/package/metadata/helm.tmpl | 4 +- templates/package/metadata/maven.tmpl | 6 +- templates/package/metadata/npm.tmpl | 8 +- templates/package/metadata/nuget.tmpl | 6 +- templates/package/metadata/pub.tmpl | 6 +- templates/package/metadata/pypi.tmpl | 6 +- templates/package/metadata/rpm.tmpl | 4 +- templates/package/metadata/rubygems.tmpl | 6 +- templates/package/metadata/swift.tmpl | 2 +- templates/package/metadata/vagrant.tmpl | 6 +- templates/package/shared/list.tmpl | 2 +- templates/package/shared/versionlist.tmpl | 2 +- templates/package/view.tmpl | 14 +-- templates/projects/list.tmpl | 8 +- templates/projects/view.tmpl | 8 +- templates/repo/actions/list.tmpl | 2 +- templates/repo/actions/runs_list.tmpl | 4 +- templates/repo/blame.tmpl | 6 +- templates/repo/branch/list.tmpl | 40 ++++---- templates/repo/branch_dropdown.tmpl | 6 +- .../code/recently_pushed_new_branches.tmpl | 2 +- .../repo/commit_load_branches_and_tags.tmpl | 10 +- templates/repo/commit_page.tmpl | 66 ++++++------- templates/repo/commits.tmpl | 2 +- templates/repo/commits_list.tmpl | 10 +- templates/repo/commits_list_small.tmpl | 2 +- templates/repo/commits_table.tmpl | 4 +- templates/repo/diff/box.tmpl | 20 ++-- templates/repo/diff/comment_form.tmpl | 2 +- templates/repo/diff/comments.tmpl | 2 +- templates/repo/diff/compare.tmpl | 10 +- templates/repo/diff/conversation.tmpl | 16 +-- templates/repo/diff/new_review.tmpl | 2 +- templates/repo/diff/stats.tmpl | 2 +- templates/repo/diff/whitespace_dropdown.tmpl | 8 +- templates/repo/editor/commit_form.tmpl | 2 +- templates/repo/editor/patch.tmpl | 2 +- templates/repo/empty.tmpl | 2 +- templates/repo/file_info.tmpl | 2 +- templates/repo/find/files.tmpl | 4 +- templates/repo/flags.tmpl | 2 +- templates/repo/forks.tmpl | 4 +- templates/repo/graph.tmpl | 14 +-- templates/repo/graph/commits.tmpl | 10 +- templates/repo/header_fork.tmpl | 4 +- templates/repo/home.tmpl | 22 ++--- .../repo/issue/branch_selector_field.tmpl | 4 +- templates/repo/issue/card.tmpl | 20 ++-- templates/repo/issue/fields/checkboxes.tmpl | 2 +- templates/repo/issue/fields/textarea.tmpl | 2 +- templates/repo/issue/filter_actions.tmpl | 4 +- templates/repo/issue/filter_list.tmpl | 6 +- templates/repo/issue/filters.tmpl | 2 +- templates/repo/issue/labels.tmpl | 2 +- .../repo/issue/labels/edit_delete_label.tmpl | 4 +- templates/repo/issue/labels/label_list.tmpl | 2 +- .../issue/labels/labels_selector_field.tmpl | 2 +- .../repo/issue/milestone/select_menu.tmpl | 4 +- templates/repo/issue/milestone_issues.tmpl | 6 +- templates/repo/issue/milestones.tmpl | 4 +- templates/repo/issue/new_form.tmpl | 22 ++--- templates/repo/issue/openclose.tmpl | 8 +- .../repo/issue/view_content/attachments.tmpl | 4 +- .../repo/issue/view_content/comments.tmpl | 2 +- .../repo/issue/view_content/conversation.tmpl | 22 ++--- templates/repo/issue/view_content/pull.tmpl | 4 +- .../view_content/pull_merge_instruction.tmpl | 2 +- .../repo/issue/view_content/sidebar.tmpl | 76 +++++++------- .../repo/issue/view_content/watching.tmpl | 4 +- templates/repo/issue/view_title.tmpl | 14 +-- templates/repo/latest_commit.tmpl | 4 +- templates/repo/migrate/migrate.tmpl | 6 +- templates/repo/projects/view.tmpl | 2 +- templates/repo/pulls/tab_menu.tmpl | 2 +- templates/repo/pulse.tmpl | 12 +-- templates/repo/release/list.tmpl | 18 ++-- templates/repo/release/new.tmpl | 6 +- templates/repo/settings/branches.tmpl | 2 +- templates/repo/settings/deploy_keys.tmpl | 2 +- templates/repo/settings/githooks.tmpl | 6 +- templates/repo/settings/options.tmpl | 10 +- templates/repo/settings/protected_branch.tmpl | 8 +- templates/repo/settings/units/issues.tmpl | 4 +- templates/repo/settings/units/wiki.tmpl | 2 +- .../repo/settings/webhook/base_list.tmpl | 8 +- templates/repo/sub_menu.tmpl | 2 +- templates/repo/tag/list.tmpl | 20 ++-- templates/repo/view_file.tmpl | 12 +-- templates/repo/view_list.tmpl | 2 +- templates/repo/wiki/new.tmpl | 2 +- templates/repo/wiki/revision.tmpl | 2 +- templates/repo/wiki/start.tmpl | 2 +- templates/shared/actions/runner_edit.tmpl | 8 +- templates/shared/issuelist.tmpl | 4 +- templates/shared/repo_search.tmpl | 2 +- templates/shared/search/code/results.tmpl | 4 +- templates/shared/searchbottom.tmpl | 6 +- templates/shared/secrets/add_list.tmpl | 2 +- templates/shared/user/authorlink.tmpl | 2 +- templates/shared/user/blocked_users.tmpl | 83 ++++++++++++++++ templates/shared/user/profile_big_avatar.tmpl | 6 +- templates/shared/variables/variable_list.tmpl | 4 +- templates/status/500.tmpl | 8 +- templates/user/auth/signin_inner.tmpl | 4 +- templates/user/auth/signup_inner.tmpl | 4 +- templates/user/auth/webauthn.tmpl | 2 +- templates/user/auth/webauthn_error.tmpl | 2 +- templates/user/dashboard/feeds.tmpl | 2 +- templates/user/dashboard/issues.tmpl | 4 +- templates/user/dashboard/milestones.tmpl | 8 +- .../user/notification/notification_div.tmpl | 28 +++--- .../notification_subscriptions.tmpl | 4 +- templates/user/overview/package_versions.tmpl | 2 +- templates/user/overview/packages.tmpl | 2 +- templates/user/profile.tmpl | 2 +- templates/user/settings/account.tmpl | 4 +- templates/user/settings/applications.tmpl | 14 +-- .../applications_oauth2_edit_form.tmpl | 2 +- .../settings/applications_oauth2_list.tmpl | 4 +- templates/user/settings/grants_oauth2.tmpl | 2 +- templates/user/settings/keys_gpg.tmpl | 2 +- templates/user/settings/keys_ssh.tmpl | 2 +- templates/user/settings/profile.tmpl | 4 +- templates/user/settings/repos.tmpl | 20 ++-- web_src/css/helpers.css | 98 ------------------- web_src/js/components/DashboardRepoList.vue | 42 ++++---- web_src/js/components/DiffCommitSelector.vue | 2 +- web_src/js/components/DiffFileList.vue | 8 +- web_src/js/components/DiffFileTree.vue | 2 +- .../js/components/PullRequestMergeForm.vue | 6 +- web_src/js/components/RepoActionView.vue | 14 +-- .../js/components/RepoBranchTagSelector.vue | 10 +- web_src/js/components/RepoCodeFrequency.vue | 2 +- web_src/js/components/RepoContributors.vue | 4 +- web_src/js/components/RepoRecentCommits.vue | 2 +- .../components/ScopedAccessTokenSelector.vue | 2 +- web_src/js/features/repo-diff-commit.js | 2 +- web_src/js/features/repo-findfile.js | 2 +- web_src/js/features/repo-home.js | 2 +- web_src/js/features/repo-issue-content.js | 2 +- web_src/js/features/repo-issue-list.js | 2 +- web_src/js/features/repo-legacy.js | 6 +- web_src/js/features/tribute.js | 2 +- 183 files changed, 691 insertions(+), 706 deletions(-) create mode 100644 templates/shared/user/blocked_users.tmpl diff --git a/routers/web/repo/blame.go b/routers/web/repo/blame.go index 809a5009bb..eea3d4dc00 100644 --- a/routers/web/repo/blame.go +++ b/routers/web/repo/blame.go @@ -260,7 +260,7 @@ func renderBlame(ctx *context.Context, blameParts []*git.BlamePart, commitNames if commit.User != nil { avatar = string(avatarUtils.Avatar(commit.User, 18)) } else { - avatar = string(avatarUtils.AvatarByEmail(commit.Author.Email, commit.Author.Name, 18, "gt-mr-3")) + avatar = string(avatarUtils.AvatarByEmail(commit.Author.Email, commit.Author.Name, 18, "tw-mr-2")) } br.Avatar = gotemplate.HTML(avatar) diff --git a/routers/web/repo/issue_content_history.go b/routers/web/repo/issue_content_history.go index dfee2863b5..c817d6aa96 100644 --- a/routers/web/repo/issue_content_history.go +++ b/routers/web/repo/issue_content_history.go @@ -70,7 +70,7 @@ func GetContentHistoryList(ctx *context.Context) { } src := html.EscapeString(item.UserAvatarLink) - class := avatars.DefaultAvatarClass + " gt-mr-3" + class := avatars.DefaultAvatarClass + " tw-mr-2" name := html.EscapeString(username) avatarHTML := string(templates.AvatarHTML(src, 28, class, username)) timeSinceText := string(timeutil.TimeSinceUnix(item.EditedUnix, ctx.Locale)) diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index becb8748cd..a65a100212 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -938,9 +938,9 @@ func prepareOpenWithEditorApps(ctx *context.Context) { schema, _, _ := strings.Cut(app.OpenURL, ":") var iconHTML template.HTML if schema == "vscode" || schema == "vscodium" || schema == "jetbrains" { - iconHTML = svg.RenderHTML(fmt.Sprintf("gitea-open-with-%s", schema), 16, "gt-mr-3") + iconHTML = svg.RenderHTML(fmt.Sprintf("gitea-open-with-%s", schema), 16, "tw-mr-2") } else { - iconHTML = svg.RenderHTML("gitea-git", 16, "gt-mr-3") // TODO: it could support user's customized icon in the future + iconHTML = svg.RenderHTML("gitea-git", 16, "tw-mr-2") // TODO: it could support user's customized icon in the future } tmplApps = append(tmplApps, map[string]any{ "DisplayName": app.DisplayName, diff --git a/services/auth/source/oauth2/providers.go b/services/auth/source/oauth2/providers.go index c3edae4ab6..6ed6c184eb 100644 --- a/services/auth/source/oauth2/providers.go +++ b/services/auth/source/oauth2/providers.go @@ -59,7 +59,7 @@ func (p *AuthSourceProvider) DisplayName() string { func (p *AuthSourceProvider) IconHTML(size int) template.HTML { if p.iconURL != "" { - img := fmt.Sprintf(`%s`, + img := fmt.Sprintf(`%s`, size, size, html.EscapeString(p.iconURL), html.EscapeString(p.DisplayName()), diff --git a/services/auth/source/oauth2/providers_base.go b/services/auth/source/oauth2/providers_base.go index 5b6694487b..9d4ab106e5 100644 --- a/services/auth/source/oauth2/providers_base.go +++ b/services/auth/source/oauth2/providers_base.go @@ -35,10 +35,10 @@ func (b *BaseProvider) IconHTML(size int) template.HTML { case "github": svgName = "octicon-mark-github" } - svgHTML := svg.RenderHTML(svgName, size, "gt-mr-3") + svgHTML := svg.RenderHTML(svgName, size, "tw-mr-2") if svgHTML == "" { log.Error("No SVG icon for oauth2 provider %q", b.name) - svgHTML = svg.RenderHTML("gitea-openid", size, "gt-mr-3") + svgHTML = svg.RenderHTML("gitea-openid", size, "tw-mr-2") } return svgHTML } diff --git a/services/auth/source/oauth2/providers_openid.go b/services/auth/source/oauth2/providers_openid.go index a4dcfcafc7..285876d5ac 100644 --- a/services/auth/source/oauth2/providers_openid.go +++ b/services/auth/source/oauth2/providers_openid.go @@ -29,7 +29,7 @@ func (o *OpenIDProvider) DisplayName() string { // IconHTML returns icon HTML for this provider func (o *OpenIDProvider) IconHTML(size int) template.HTML { - return svg.RenderHTML("gitea-openid", size, "gt-mr-3") + return svg.RenderHTML("gitea-openid", size, "tw-mr-2") } // CreateGothProvider creates a GothProvider from this Provider diff --git a/templates/admin/config.tmpl b/templates/admin/config.tmpl index 0c944fcb8f..1e94935a16 100644 --- a/templates/admin/config.tmpl +++ b/templates/admin/config.tmpl @@ -231,7 +231,7 @@
{{ctx.Locale.Tr "admin.config.mailer_user"}}
{{if .Mailer.User}}{{.Mailer.User}}{{else}}(empty){{end}}
-
{{ctx.Locale.Tr "admin.config.send_test_mail"}}
+
{{ctx.Locale.Tr "admin.config.send_test_mail"}}
{{.CsrfTokenHtml}} @@ -334,7 +334,7 @@ {{range $loggerName, $loggerDetail := .Loggers}}
{{ctx.Locale.Tr "admin.config.logger_name_fmt" $loggerName}}
{{if $loggerDetail.IsEnabled}} -
{{$loggerDetail.EventWriters | JsonUtils.EncodeToString | JsonUtils.PrettyIndent}}
+
{{$loggerDetail.EventWriters | JsonUtils.EncodeToString | JsonUtils.PrettyIndent}}
{{else}}
{{ctx.Locale.Tr "admin.config.disabled_logger"}}
{{end}} diff --git a/templates/admin/config_settings.tmpl b/templates/admin/config_settings.tmpl index 22ad5c24ac..d7fb022274 100644 --- a/templates/admin/config_settings.tmpl +++ b/templates/admin/config_settings.tmpl @@ -28,7 +28,7 @@
{{ctx.Locale.Tr "admin.config.open_with_editor_app_help"}} -
{{.DefaultOpenWithEditorAppsString}}
+
{{.DefaultOpenWithEditorAppsString}}
diff --git a/templates/admin/cron.tmpl b/templates/admin/cron.tmpl index af30cc06e1..3cb641488c 100644 --- a/templates/admin/cron.tmpl +++ b/templates/admin/cron.tmpl @@ -5,7 +5,7 @@
- +
diff --git a/templates/admin/dashboard.tmpl b/templates/admin/dashboard.tmpl index cc7d338589..bfd2ee6670 100644 --- a/templates/admin/dashboard.tmpl +++ b/templates/admin/dashboard.tmpl @@ -11,7 +11,7 @@
{{.CsrfTokenHtml}} -
+
diff --git a/templates/admin/emails/list.tmpl b/templates/admin/emails/list.tmpl index b72aef8f35..388863df9b 100644 --- a/templates/admin/emails/list.tmpl +++ b/templates/admin/emails/list.tmpl @@ -4,12 +4,12 @@ {{ctx.Locale.Tr "admin.emails.email_manage_panel"}} ({{ctx.Locale.Tr "admin.total" .Total}})
-
+ {{svg "octicon-question" 16 "tw-ml-1"}} + {{svg "octicon-question" 16 "tw-ml-1"}} + {{svg "octicon-question" 16 "tw-ml-1"}} diff --git a/templates/org/team/repositories.tmpl b/templates/org/team/repositories.tmpl index 202279240b..98b4854eb8 100644 --- a/templates/org/team/repositories.tmpl +++ b/templates/org/team/repositories.tmpl @@ -17,7 +17,7 @@ - +
diff --git a/templates/package/metadata/alpine.tmpl b/templates/package/metadata/alpine.tmpl index 73cbc06aac..3e7f10f66a 100644 --- a/templates/package/metadata/alpine.tmpl +++ b/templates/package/metadata/alpine.tmpl @@ -1,5 +1,5 @@ {{if eq .PackageDescriptor.Package.Type "alpine"}} {{if .PackageDescriptor.Metadata.Maintainer}}
{{svg "octicon-person" 16 "mr-3"}} {{.PackageDescriptor.Metadata.Maintainer}}
{{end}} {{if .PackageDescriptor.Metadata.ProjectURL}}
{{svg "octicon-link-external" 16 "mr-3"}} {{ctx.Locale.Tr "packages.details.project_site"}}
{{end}} - {{if .PackageDescriptor.Metadata.License}}
{{svg "octicon-law" 16 "gt-mr-3"}} {{.PackageDescriptor.Metadata.License}}
{{end}} + {{if .PackageDescriptor.Metadata.License}}
{{svg "octicon-law" 16 "tw-mr-2"}} {{.PackageDescriptor.Metadata.License}}
{{end}} {{end}} diff --git a/templates/package/metadata/cargo.tmpl b/templates/package/metadata/cargo.tmpl index c8471a71ef..5ad3c20a93 100644 --- a/templates/package/metadata/cargo.tmpl +++ b/templates/package/metadata/cargo.tmpl @@ -1,7 +1,7 @@ {{if eq .PackageDescriptor.Package.Type "cargo"}} - {{range .PackageDescriptor.Metadata.Authors}}
{{svg "octicon-person" 16 "gt-mr-3"}} {{.}}
{{end}} - {{if .PackageDescriptor.Metadata.ProjectURL}}
{{svg "octicon-link-external" 16 "gt-mr-3"}} {{ctx.Locale.Tr "packages.details.project_site"}}
{{end}} - {{if .PackageDescriptor.Metadata.RepositoryURL}}
{{svg "octicon-link-external" 16 "gt-mr-3"}} {{ctx.Locale.Tr "packages.details.repository_site"}}
{{end}} - {{if .PackageDescriptor.Metadata.DocumentationURL}}
{{svg "octicon-link-external" 16 "gt-mr-3"}} {{ctx.Locale.Tr "packages.details.documentation_site"}}
{{end}} - {{if .PackageDescriptor.Metadata.License}}
{{svg "octicon-law" 16 "gt-mr-3"}} {{.PackageDescriptor.Metadata.License}}
{{end}} + {{range .PackageDescriptor.Metadata.Authors}}
{{svg "octicon-person" 16 "tw-mr-2"}} {{.}}
{{end}} + {{if .PackageDescriptor.Metadata.ProjectURL}}
{{svg "octicon-link-external" 16 "tw-mr-2"}} {{ctx.Locale.Tr "packages.details.project_site"}}
{{end}} + {{if .PackageDescriptor.Metadata.RepositoryURL}}
{{svg "octicon-link-external" 16 "tw-mr-2"}} {{ctx.Locale.Tr "packages.details.repository_site"}}
{{end}} + {{if .PackageDescriptor.Metadata.DocumentationURL}}
{{svg "octicon-link-external" 16 "tw-mr-2"}} {{ctx.Locale.Tr "packages.details.documentation_site"}}
{{end}} + {{if .PackageDescriptor.Metadata.License}}
{{svg "octicon-law" 16 "tw-mr-2"}} {{.PackageDescriptor.Metadata.License}}
{{end}} {{end}} diff --git a/templates/package/metadata/chef.tmpl b/templates/package/metadata/chef.tmpl index fa6e068d23..23a9ce3ec0 100644 --- a/templates/package/metadata/chef.tmpl +++ b/templates/package/metadata/chef.tmpl @@ -1,5 +1,5 @@ {{if eq .PackageDescriptor.Package.Type "chef"}} - {{if .PackageDescriptor.Metadata.Author}}
{{svg "octicon-person" 16 "gt-mr-3"}} {{.PackageDescriptor.Metadata.Author}}
{{end}} - {{if .PackageDescriptor.Metadata.RepositoryURL}}
{{svg "octicon-link-external" 16 "gt-mr-3"}} {{ctx.Locale.Tr "packages.details.repository_site"}}
{{end}} - {{if .PackageDescriptor.Metadata.License}}
{{svg "octicon-law" 16 "gt-mr-3"}} {{.PackageDescriptor.Metadata.License}}
{{end}} + {{if .PackageDescriptor.Metadata.Author}}
{{svg "octicon-person" 16 "tw-mr-2"}} {{.PackageDescriptor.Metadata.Author}}
{{end}} + {{if .PackageDescriptor.Metadata.RepositoryURL}}
{{svg "octicon-link-external" 16 "tw-mr-2"}} {{ctx.Locale.Tr "packages.details.repository_site"}}
{{end}} + {{if .PackageDescriptor.Metadata.License}}
{{svg "octicon-law" 16 "tw-mr-2"}} {{.PackageDescriptor.Metadata.License}}
{{end}} {{end}} diff --git a/templates/package/metadata/composer.tmpl b/templates/package/metadata/composer.tmpl index fbdc33f73d..0f6ff9d6f2 100644 --- a/templates/package/metadata/composer.tmpl +++ b/templates/package/metadata/composer.tmpl @@ -1,5 +1,5 @@ {{if eq .PackageDescriptor.Package.Type "composer"}} - {{range .PackageDescriptor.Metadata.Authors}}
{{svg "octicon-person" 16 "gt-mr-3"}} {{.Name}}
{{end}} - {{if .PackageDescriptor.Metadata.Homepage}}
{{svg "octicon-link-external" 16 "gt-mr-3"}} {{ctx.Locale.Tr "packages.details.project_site"}}
{{end}} - {{range .PackageDescriptor.Metadata.License}}
{{svg "octicon-law" 16 "gt-mr-3"}} {{.}}
{{end}} + {{range .PackageDescriptor.Metadata.Authors}}
{{svg "octicon-person" 16 "tw-mr-2"}} {{.Name}}
{{end}} + {{if .PackageDescriptor.Metadata.Homepage}}
{{svg "octicon-link-external" 16 "tw-mr-2"}} {{ctx.Locale.Tr "packages.details.project_site"}}
{{end}} + {{range .PackageDescriptor.Metadata.License}}
{{svg "octicon-law" 16 "tw-mr-2"}} {{.}}
{{end}} {{end}} diff --git a/templates/package/metadata/conan.tmpl b/templates/package/metadata/conan.tmpl index 40bda555bb..4e05ec2587 100644 --- a/templates/package/metadata/conan.tmpl +++ b/templates/package/metadata/conan.tmpl @@ -1,6 +1,6 @@ {{if eq .PackageDescriptor.Package.Type "conan"}} - {{if .PackageDescriptor.Metadata.Author}}
{{svg "octicon-person" 16 "gt-mr-3"}} {{.PackageDescriptor.Metadata.Author}}
{{end}} - {{if .PackageDescriptor.Metadata.ProjectURL}}
{{svg "octicon-link-external" 16 "gt-mr-3"}} {{ctx.Locale.Tr "packages.details.project_site"}}
{{end}} - {{if .PackageDescriptor.Metadata.License}}
{{svg "octicon-law" 16 "gt-mr-3"}} {{.PackageDescriptor.Metadata.License}}
{{end}} - {{if .PackageDescriptor.Metadata.RepositoryURL}}
{{svg "octicon-link-external" 16 "gt-mr-3"}} {{ctx.Locale.Tr "packages.details.repository_site"}}
{{end}} + {{if .PackageDescriptor.Metadata.Author}}
{{svg "octicon-person" 16 "tw-mr-2"}} {{.PackageDescriptor.Metadata.Author}}
{{end}} + {{if .PackageDescriptor.Metadata.ProjectURL}}
{{svg "octicon-link-external" 16 "tw-mr-2"}} {{ctx.Locale.Tr "packages.details.project_site"}}
{{end}} + {{if .PackageDescriptor.Metadata.License}}
{{svg "octicon-law" 16 "tw-mr-2"}} {{.PackageDescriptor.Metadata.License}}
{{end}} + {{if .PackageDescriptor.Metadata.RepositoryURL}}
{{svg "octicon-link-external" 16 "tw-mr-2"}} {{ctx.Locale.Tr "packages.details.repository_site"}}
{{end}} {{end}} diff --git a/templates/package/metadata/conda.tmpl b/templates/package/metadata/conda.tmpl index f70e2b2a1c..3628686e13 100644 --- a/templates/package/metadata/conda.tmpl +++ b/templates/package/metadata/conda.tmpl @@ -1,6 +1,6 @@ {{if eq .PackageDescriptor.Package.Type "conda"}} - {{if .PackageDescriptor.Metadata.License}}
{{svg "octicon-law" 16 "gt-mr-3"}} {{.PackageDescriptor.Metadata.License}}
{{end}} - {{if .PackageDescriptor.Metadata.ProjectURL}}
{{svg "octicon-link-external" 16 "gt-mr-3"}} {{ctx.Locale.Tr "packages.details.project_site"}}
{{end}} - {{if .PackageDescriptor.Metadata.RepositoryURL}}
{{svg "octicon-link-external" 16 "gt-mr-3"}} {{ctx.Locale.Tr "packages.details.repository_site"}}
{{end}} - {{if .PackageDescriptor.Metadata.DocumentationURL}}
{{svg "octicon-link-external" 16 "gt-mr-3"}} {{ctx.Locale.Tr "packages.details.documentation_site"}}
{{end}} + {{if .PackageDescriptor.Metadata.License}}
{{svg "octicon-law" 16 "tw-mr-2"}} {{.PackageDescriptor.Metadata.License}}
{{end}} + {{if .PackageDescriptor.Metadata.ProjectURL}}
{{svg "octicon-link-external" 16 "tw-mr-2"}} {{ctx.Locale.Tr "packages.details.project_site"}}
{{end}} + {{if .PackageDescriptor.Metadata.RepositoryURL}}
{{svg "octicon-link-external" 16 "tw-mr-2"}} {{ctx.Locale.Tr "packages.details.repository_site"}}
{{end}} + {{if .PackageDescriptor.Metadata.DocumentationURL}}
{{svg "octicon-link-external" 16 "tw-mr-2"}} {{ctx.Locale.Tr "packages.details.documentation_site"}}
{{end}} {{end}} diff --git a/templates/package/metadata/container.tmpl b/templates/package/metadata/container.tmpl index b05ef0b846..f5abb7ef6e 100644 --- a/templates/package/metadata/container.tmpl +++ b/templates/package/metadata/container.tmpl @@ -1,9 +1,9 @@ {{if eq .PackageDescriptor.Package.Type "container"}} -
{{svg "octicon-package" 16 "gt-mr-3"}} {{.PackageDescriptor.Metadata.Type.Name}}
- {{if .PackageDescriptor.Metadata.Platform}}
{{svg "octicon-cpu" 16 "gt-mr-3"}} {{.PackageDescriptor.Metadata.Platform}}
{{end}} - {{range .PackageDescriptor.Metadata.Authors}}
{{svg "octicon-person" 16 "gt-mr-3"}} {{.}}
{{end}} - {{if .PackageDescriptor.Metadata.Licenses}}
{{svg "octicon-law" 16 "gt-mr-3"}} {{.PackageDescriptor.Metadata.Licenses}}
{{end}} - {{if .PackageDescriptor.Metadata.ProjectURL}}
{{svg "octicon-link-external" 16 "gt-mr-3"}} {{ctx.Locale.Tr "packages.details.project_site"}}
{{end}} - {{if .PackageDescriptor.Metadata.RepositoryURL}}
{{svg "octicon-link-external" 16 "gt-mr-3"}} {{ctx.Locale.Tr "packages.details.repository_site"}}
{{end}} - {{if .PackageDescriptor.Metadata.DocumentationURL}}
{{svg "octicon-link-external" 16 "gt-mr-3"}} {{ctx.Locale.Tr "packages.details.documentation_site"}}
{{end}} +
{{svg "octicon-package" 16 "tw-mr-2"}} {{.PackageDescriptor.Metadata.Type.Name}}
+ {{if .PackageDescriptor.Metadata.Platform}}
{{svg "octicon-cpu" 16 "tw-mr-2"}} {{.PackageDescriptor.Metadata.Platform}}
{{end}} + {{range .PackageDescriptor.Metadata.Authors}}
{{svg "octicon-person" 16 "tw-mr-2"}} {{.}}
{{end}} + {{if .PackageDescriptor.Metadata.Licenses}}
{{svg "octicon-law" 16 "tw-mr-2"}} {{.PackageDescriptor.Metadata.Licenses}}
{{end}} + {{if .PackageDescriptor.Metadata.ProjectURL}}
{{svg "octicon-link-external" 16 "tw-mr-2"}} {{ctx.Locale.Tr "packages.details.project_site"}}
{{end}} + {{if .PackageDescriptor.Metadata.RepositoryURL}}
{{svg "octicon-link-external" 16 "tw-mr-2"}} {{ctx.Locale.Tr "packages.details.repository_site"}}
{{end}} + {{if .PackageDescriptor.Metadata.DocumentationURL}}
{{svg "octicon-link-external" 16 "tw-mr-2"}} {{ctx.Locale.Tr "packages.details.documentation_site"}}
{{end}} {{end}} diff --git a/templates/package/metadata/helm.tmpl b/templates/package/metadata/helm.tmpl index 499f77e80d..50ea484999 100644 --- a/templates/package/metadata/helm.tmpl +++ b/templates/package/metadata/helm.tmpl @@ -1,4 +1,4 @@ {{if eq .PackageDescriptor.Package.Type "helm"}} - {{range .PackageDescriptor.Metadata.Maintainers}}
{{svg "octicon-person" 16 "gt-mr-3"}} {{.Name}}
{{end}} - {{if .PackageDescriptor.Metadata.Home}}
{{svg "octicon-link-external" 16 "gt-mr-3"}} {{ctx.Locale.Tr "packages.details.project_site"}}
{{end}} + {{range .PackageDescriptor.Metadata.Maintainers}}
{{svg "octicon-person" 16 "tw-mr-2"}} {{.Name}}
{{end}} + {{if .PackageDescriptor.Metadata.Home}}
{{svg "octicon-link-external" 16 "tw-mr-2"}} {{ctx.Locale.Tr "packages.details.project_site"}}
{{end}} {{end}} diff --git a/templates/package/metadata/maven.tmpl b/templates/package/metadata/maven.tmpl index 36f5eca840..548be61790 100644 --- a/templates/package/metadata/maven.tmpl +++ b/templates/package/metadata/maven.tmpl @@ -1,5 +1,5 @@ {{if eq .PackageDescriptor.Package.Type "maven"}} - {{if .PackageDescriptor.Metadata.Name}}
{{svg "octicon-note" 16 "gt-mr-3"}} {{.PackageDescriptor.Metadata.Name}}
{{end}} - {{if .PackageDescriptor.Metadata.ProjectURL}}
{{svg "octicon-link-external" 16 "gt-mr-3"}} {{ctx.Locale.Tr "packages.details.project_site"}}
{{end}} - {{range .PackageDescriptor.Metadata.Licenses}}
{{svg "octicon-law" 16 "gt-mr-3"}} {{.}}
{{end}} + {{if .PackageDescriptor.Metadata.Name}}
{{svg "octicon-note" 16 "tw-mr-2"}} {{.PackageDescriptor.Metadata.Name}}
{{end}} + {{if .PackageDescriptor.Metadata.ProjectURL}}
{{svg "octicon-link-external" 16 "tw-mr-2"}} {{ctx.Locale.Tr "packages.details.project_site"}}
{{end}} + {{range .PackageDescriptor.Metadata.Licenses}}
{{svg "octicon-law" 16 "tw-mr-2"}} {{.}}
{{end}} {{end}} diff --git a/templates/package/metadata/npm.tmpl b/templates/package/metadata/npm.tmpl index 9794d851af..df37504e37 100644 --- a/templates/package/metadata/npm.tmpl +++ b/templates/package/metadata/npm.tmpl @@ -1,8 +1,8 @@ {{if eq .PackageDescriptor.Package.Type "npm"}} - {{if .PackageDescriptor.Metadata.Author}}
{{svg "octicon-person" 16 "gt-mr-3"}} {{.PackageDescriptor.Metadata.Author}}
{{end}} - {{if .PackageDescriptor.Metadata.ProjectURL}}
{{svg "octicon-link-external" 16 "gt-mr-3"}} {{ctx.Locale.Tr "packages.details.project_site"}}
{{end}} - {{if .PackageDescriptor.Metadata.License}}
{{svg "octicon-law" 16 "gt-mr-3"}} {{.PackageDescriptor.Metadata.License}}
{{end}} + {{if .PackageDescriptor.Metadata.Author}}
{{svg "octicon-person" 16 "tw-mr-2"}} {{.PackageDescriptor.Metadata.Author}}
{{end}} + {{if .PackageDescriptor.Metadata.ProjectURL}}
{{svg "octicon-link-external" 16 "tw-mr-2"}} {{ctx.Locale.Tr "packages.details.project_site"}}
{{end}} + {{if .PackageDescriptor.Metadata.License}}
{{svg "octicon-law" 16 "tw-mr-2"}} {{.PackageDescriptor.Metadata.License}}
{{end}} {{range .PackageDescriptor.VersionProperties}} - {{if eq .Name "npm.tag"}}
{{svg "octicon-versions" 16 "gt-mr-3"}} {{.Value}}
{{end}} + {{if eq .Name "npm.tag"}}
{{svg "octicon-versions" 16 "tw-mr-2"}} {{.Value}}
{{end}} {{end}} {{end}} diff --git a/templates/package/metadata/nuget.tmpl b/templates/package/metadata/nuget.tmpl index f25e1c3b63..5534577bd2 100644 --- a/templates/package/metadata/nuget.tmpl +++ b/templates/package/metadata/nuget.tmpl @@ -1,5 +1,5 @@ {{if eq .PackageDescriptor.Package.Type "nuget"}} - {{if .PackageDescriptor.Metadata.Authors}}
{{svg "octicon-person" 16 "gt-mr-3"}} {{.PackageDescriptor.Metadata.Authors}}
{{end}} - {{if .PackageDescriptor.Metadata.ProjectURL}}
{{svg "octicon-link-external" 16 "gt-mr-3"}} {{ctx.Locale.Tr "packages.details.project_site"}}
{{end}} - {{if .PackageDescriptor.Metadata.RepositoryURL}}
{{svg "octicon-link-external" 16 "gt-mr-3"}} {{ctx.Locale.Tr "packages.details.repository_site"}}
{{end}} + {{if .PackageDescriptor.Metadata.Authors}}
{{svg "octicon-person" 16 "tw-mr-2"}} {{.PackageDescriptor.Metadata.Authors}}
{{end}} + {{if .PackageDescriptor.Metadata.ProjectURL}}
{{svg "octicon-link-external" 16 "tw-mr-2"}} {{ctx.Locale.Tr "packages.details.project_site"}}
{{end}} + {{if .PackageDescriptor.Metadata.RepositoryURL}}
{{svg "octicon-link-external" 16 "tw-mr-2"}} {{ctx.Locale.Tr "packages.details.repository_site"}}
{{end}} {{end}} diff --git a/templates/package/metadata/pub.tmpl b/templates/package/metadata/pub.tmpl index 1e4a90e78c..16f7cec370 100644 --- a/templates/package/metadata/pub.tmpl +++ b/templates/package/metadata/pub.tmpl @@ -1,5 +1,5 @@ {{if eq .PackageDescriptor.Package.Type "pub"}} - {{if .PackageDescriptor.Metadata.ProjectURL}}
{{svg "octicon-link-external" 16 "gt-mr-3"}} {{ctx.Locale.Tr "packages.details.project_site"}}
{{end}} - {{if .PackageDescriptor.Metadata.RepositoryURL}}
{{svg "octicon-link-external" 16 "gt-mr-3"}} {{ctx.Locale.Tr "packages.details.repository_site"}}
{{end}} - {{if .PackageDescriptor.Metadata.DocumentationURL}}
{{svg "octicon-link-external" 16 "gt-mr-3"}} {{ctx.Locale.Tr "packages.details.documentation_site"}}
{{end}} + {{if .PackageDescriptor.Metadata.ProjectURL}}
{{svg "octicon-link-external" 16 "tw-mr-2"}} {{ctx.Locale.Tr "packages.details.project_site"}}
{{end}} + {{if .PackageDescriptor.Metadata.RepositoryURL}}
{{svg "octicon-link-external" 16 "tw-mr-2"}} {{ctx.Locale.Tr "packages.details.repository_site"}}
{{end}} + {{if .PackageDescriptor.Metadata.DocumentationURL}}
{{svg "octicon-link-external" 16 "tw-mr-2"}} {{ctx.Locale.Tr "packages.details.documentation_site"}}
{{end}} {{end}} diff --git a/templates/package/metadata/pypi.tmpl b/templates/package/metadata/pypi.tmpl index f447cb7f4f..3d9b213907 100644 --- a/templates/package/metadata/pypi.tmpl +++ b/templates/package/metadata/pypi.tmpl @@ -1,5 +1,5 @@ {{if eq .PackageDescriptor.Package.Type "pypi"}} - {{if .PackageDescriptor.Metadata.Author}}
{{svg "octicon-person" 16 "gt-mr-3"}} {{.PackageDescriptor.Metadata.Author}}
{{end}} - {{if .PackageDescriptor.Metadata.ProjectURL}}
{{svg "octicon-link-external" 16 "gt-mr-3"}} {{ctx.Locale.Tr "packages.details.project_site"}}
{{end}} - {{if .PackageDescriptor.Metadata.License}}
{{svg "octicon-law" 16 "gt-mr-3"}} {{.PackageDescriptor.Metadata.License}}
{{end}} + {{if .PackageDescriptor.Metadata.Author}}
{{svg "octicon-person" 16 "tw-mr-2"}} {{.PackageDescriptor.Metadata.Author}}
{{end}} + {{if .PackageDescriptor.Metadata.ProjectURL}}
{{svg "octicon-link-external" 16 "tw-mr-2"}} {{ctx.Locale.Tr "packages.details.project_site"}}
{{end}} + {{if .PackageDescriptor.Metadata.License}}
{{svg "octicon-law" 16 "tw-mr-2"}} {{.PackageDescriptor.Metadata.License}}
{{end}} {{end}} diff --git a/templates/package/metadata/rpm.tmpl b/templates/package/metadata/rpm.tmpl index 026f129590..eda8a489f3 100644 --- a/templates/package/metadata/rpm.tmpl +++ b/templates/package/metadata/rpm.tmpl @@ -1,4 +1,4 @@ {{if eq .PackageDescriptor.Package.Type "rpm"}} - {{if .PackageDescriptor.Metadata.ProjectURL}}
{{svg "octicon-link-external" 16 "gt-mr-3"}} {{ctx.Locale.Tr "packages.details.project_site"}}
{{end}} - {{if .PackageDescriptor.Metadata.License}}
{{svg "octicon-law" 16 "gt-mr-3"}} {{.PackageDescriptor.Metadata.License}}
{{end}} + {{if .PackageDescriptor.Metadata.ProjectURL}}
{{svg "octicon-link-external" 16 "tw-mr-2"}} {{ctx.Locale.Tr "packages.details.project_site"}}
{{end}} + {{if .PackageDescriptor.Metadata.License}}
{{svg "octicon-law" 16 "tw-mr-2"}} {{.PackageDescriptor.Metadata.License}}
{{end}} {{end}} diff --git a/templates/package/metadata/rubygems.tmpl b/templates/package/metadata/rubygems.tmpl index 62150b1a43..9b11287691 100644 --- a/templates/package/metadata/rubygems.tmpl +++ b/templates/package/metadata/rubygems.tmpl @@ -1,5 +1,5 @@ {{if eq .PackageDescriptor.Package.Type "rubygems"}} - {{range .PackageDescriptor.Metadata.Authors}}
{{svg "octicon-person" 16 "gt-mr-3"}} {{.}}
{{end}} - {{if .PackageDescriptor.Metadata.ProjectURL}}
{{svg "octicon-link-external" 16 "gt-mr-3"}} {{ctx.Locale.Tr "packages.details.project_site"}}
{{end}} - {{range .PackageDescriptor.Metadata.Licenses}}
{{svg "octicon-law" 16 "gt-mr-3"}} {{.}}
{{end}} + {{range .PackageDescriptor.Metadata.Authors}}
{{svg "octicon-person" 16 "tw-mr-2"}} {{.}}
{{end}} + {{if .PackageDescriptor.Metadata.ProjectURL}}
{{svg "octicon-link-external" 16 "tw-mr-2"}} {{ctx.Locale.Tr "packages.details.project_site"}}
{{end}} + {{range .PackageDescriptor.Metadata.Licenses}}
{{svg "octicon-law" 16 "tw-mr-2"}} {{.}}
{{end}} {{end}} diff --git a/templates/package/metadata/swift.tmpl b/templates/package/metadata/swift.tmpl index 326ebe1a94..fdffb6dede 100644 --- a/templates/package/metadata/swift.tmpl +++ b/templates/package/metadata/swift.tmpl @@ -1,4 +1,4 @@ {{if eq .PackageDescriptor.Package.Type "swift"}} {{if .PackageDescriptor.Metadata.Author.String}}
{{svg "octicon-person" 16 "mr-3"}} {{.PackageDescriptor.Metadata.Author}}
{{end}} - {{if .PackageDescriptor.Metadata.RepositoryURL}}
{{svg "octicon-link-external" 16 "gt-mr-3"}} {{ctx.Locale.Tr "packages.details.repository_site"}}
{{end}} + {{if .PackageDescriptor.Metadata.RepositoryURL}}
{{svg "octicon-link-external" 16 "tw-mr-2"}} {{ctx.Locale.Tr "packages.details.repository_site"}}
{{end}} {{end}} diff --git a/templates/package/metadata/vagrant.tmpl b/templates/package/metadata/vagrant.tmpl index a92398a275..4628a2dcbb 100644 --- a/templates/package/metadata/vagrant.tmpl +++ b/templates/package/metadata/vagrant.tmpl @@ -1,5 +1,5 @@ {{if eq .PackageDescriptor.Package.Type "vagrant"}} - {{if .PackageDescriptor.Metadata.Author}}
{{svg "octicon-person" 16 "gt-mr-3"}} {{.PackageDescriptor.Metadata.Author}}
{{end}} - {{if .PackageDescriptor.Metadata.ProjectURL}}
{{svg "octicon-link-external" 16 "gt-mr-3"}} {{ctx.Locale.Tr "packages.details.project_site"}}
{{end}} - {{if .PackageDescriptor.Metadata.RepositoryURL}}
{{svg "octicon-link-external" 16 "gt-mr-3"}} {{ctx.Locale.Tr "packages.details.repository_site"}}
{{end}} + {{if .PackageDescriptor.Metadata.Author}}
{{svg "octicon-person" 16 "tw-mr-2"}} {{.PackageDescriptor.Metadata.Author}}
{{end}} + {{if .PackageDescriptor.Metadata.ProjectURL}}
{{svg "octicon-link-external" 16 "tw-mr-2"}} {{ctx.Locale.Tr "packages.details.project_site"}}
{{end}} + {{if .PackageDescriptor.Metadata.RepositoryURL}}
{{svg "octicon-link-external" 16 "tw-mr-2"}} {{ctx.Locale.Tr "packages.details.repository_site"}}
{{end}} {{end}} diff --git a/templates/package/shared/list.tmpl b/templates/package/shared/list.tmpl index 3e99285a06..36f8bc1522 100644 --- a/templates/package/shared/list.tmpl +++ b/templates/package/shared/list.tmpl @@ -50,7 +50,7 @@

{{ctx.Locale.Tr "packages.empty.documentation" "https://forgejo.org/docs/latest/user/packages/"}}

{{else}} -

{{ctx.Locale.Tr "packages.filter.no_result"}}

+

{{ctx.Locale.Tr "packages.filter.no_result"}}

{{end}} {{end}} {{template "base/paginate" .}} diff --git a/templates/package/shared/versionlist.tmpl b/templates/package/shared/versionlist.tmpl index fc34ccc938..e5c568e059 100644 --- a/templates/package/shared/versionlist.tmpl +++ b/templates/package/shared/versionlist.tmpl @@ -31,7 +31,7 @@ {{else}} -

{{ctx.Locale.Tr "packages.filter.no_result"}}

+

{{ctx.Locale.Tr "packages.filter.no_result"}}

{{end}} {{template "base/paginate" .}} diff --git a/templates/package/view.tmpl b/templates/package/view.tmpl index e81a714895..6beb249a7f 100644 --- a/templates/package/view.tmpl +++ b/templates/package/view.tmpl @@ -43,12 +43,12 @@
{{ctx.Locale.Tr "packages.details"}}
-
{{svg .PackageDescriptor.Package.Type.SVGName 16 "gt-mr-3"}} {{.PackageDescriptor.Package.Type.Name}}
+
{{svg .PackageDescriptor.Package.Type.SVGName 16 "tw-mr-2"}} {{.PackageDescriptor.Package.Type.Name}}
{{if .HasRepositoryAccess}} -
{{svg "octicon-repo" 16 "gt-mr-3"}} {{.PackageDescriptor.Repository.FullName}}
+
{{svg "octicon-repo" 16 "tw-mr-2"}} {{.PackageDescriptor.Repository.FullName}}
{{end}} -
{{svg "octicon-calendar" 16 "gt-mr-3"}} {{TimeSinceUnix .PackageDescriptor.Version.CreatedUnix ctx.Locale}}
-
{{svg "octicon-download" 16 "gt-mr-3"}} {{.PackageDescriptor.Version.DownloadCount}}
+
{{svg "octicon-calendar" 16 "tw-mr-2"}} {{TimeSinceUnix .PackageDescriptor.Version.CreatedUnix ctx.Locale}}
+
{{svg "octicon-download" 16 "tw-mr-2"}} {{.PackageDescriptor.Version.DownloadCount}}
{{template "package/metadata/alpine" .}} {{template "package/metadata/cargo" .}} {{template "package/metadata/chef" .}} @@ -70,7 +70,7 @@ {{template "package/metadata/swift" .}} {{template "package/metadata/vagrant" .}} {{if not (and (eq .PackageDescriptor.Package.Type "container") .PackageDescriptor.Metadata.Manifests)}} -
{{svg "octicon-database" 16 "gt-mr-3"}} {{FileSize .PackageDescriptor.CalculateBlobSize}}
+
{{svg "octicon-database" 16 "tw-mr-2"}} {{FileSize .PackageDescriptor.CalculateBlobSize}}
{{end}}
{{if not (eq .PackageDescriptor.Package.Type "container")}} @@ -100,10 +100,10 @@
{{if .HasRepositoryAccess}} -
{{svg "octicon-issue-opened" 16 "gt-mr-3"}} {{ctx.Locale.Tr "repo.issues"}}
+
{{svg "octicon-issue-opened" 16 "tw-mr-2"}} {{ctx.Locale.Tr "repo.issues"}}
{{end}} {{if .CanWritePackages}} -
{{svg "octicon-tools" 16 "gt-mr-3"}} {{ctx.Locale.Tr "repo.settings"}}
+
{{svg "octicon-tools" 16 "tw-mr-2"}} {{ctx.Locale.Tr "repo.settings"}}
{{end}}
{{end}} diff --git a/templates/projects/list.tmpl b/templates/projects/list.tmpl index f33f9180bb..ec02e9a6fc 100644 --- a/templates/projects/list.tmpl +++ b/templates/projects/list.tmpl @@ -1,12 +1,12 @@ {{if and $.CanWriteProjects (not $.Repository.IsArchived)}} -
+
@@ -41,7 +41,7 @@
{{range .Projects}}
  • -

    +

    {{svg .IconName 16}} {{.Title}}

    diff --git a/templates/projects/view.tmpl b/templates/projects/view.tmpl index 93c2cdbb57..ba5cbc3b45 100644 --- a/templates/projects/view.tmpl +++ b/templates/projects/view.tmpl @@ -1,8 +1,8 @@ {{$canWriteProject := and .CanWriteProjects (or (not .Repository) (not .Repository.IsArchived))}}
  • {{ctx.Locale.Tr "admin.dashboard.delete_inactive_accounts"}}
    {{ctx.Locale.Tr "units.unit"}} {{ctx.Locale.Tr "org.teams.none_access"}} - {{svg "octicon-question" 16 "gt-ml-2"}} {{ctx.Locale.Tr "org.teams.read_access"}} - {{svg "octicon-question" 16 "gt-ml-2"}} {{ctx.Locale.Tr "org.teams.write_access"}} - {{svg "octicon-question" 16 "gt-ml-2"}}
    - +
    {{ctx.Locale.Tr "repo.file_too_large"}}{{ctx.Locale.Tr "repo.file_too_large"}}
    @@ -78,7 +78,7 @@ {{end}} - {{$row.Code}} + {{$row.Code}} {{end}} diff --git a/templates/repo/branch/list.tmpl b/templates/repo/branch/list.tmpl index 7e061696e4..77cccd65b7 100644 --- a/templates/repo/branch/list.tmpl +++ b/templates/repo/branch/list.tmpl @@ -22,14 +22,14 @@
    {{if .DefaultBranchBranch.IsProtected}}{{svg "octicon-shield-lock"}}{{end}} {{.DefaultBranchBranch.DBBranch.Name}} - + {{template "repo/commit_statuses" dict "Status" (index $.CommitStatus .DefaultBranchBranch.DBBranch.CommitID) "Statuses" (index $.CommitStatuses .DefaultBranchBranch.DBBranch.CommitID)}}
    -

    {{svg "octicon-git-commit" 16 "gt-mr-2"}}{{ShortSha .DefaultBranchBranch.DBBranch.CommitID}} · {{RenderCommitMessage $.Context .DefaultBranchBranch.DBBranch.CommitMessage (.Repository.ComposeMetas ctx)}} · {{ctx.Locale.Tr "org.repo_updated"}} {{TimeSince .DefaultBranchBranch.DBBranch.CommitTime.AsTime ctx.Locale}}{{if .DefaultBranchBranch.DBBranch.Pusher}}  {{template "shared/user/avatarlink" dict "user" .DefaultBranchBranch.DBBranch.Pusher}}{{template "shared/user/namelink" .DefaultBranchBranch.DBBranch.Pusher}}{{end}}

    +

    {{svg "octicon-git-commit" 16 "tw-mr-1"}}{{ShortSha .DefaultBranchBranch.DBBranch.CommitID}} · {{RenderCommitMessage $.Context .DefaultBranchBranch.DBBranch.CommitMessage (.Repository.ComposeMetas ctx)}} · {{ctx.Locale.Tr "org.repo_updated"}} {{TimeSince .DefaultBranchBranch.DBBranch.CommitTime.AsTime ctx.Locale}}{{if .DefaultBranchBranch.DBBranch.Pusher}}  {{template "shared/user/avatarlink" dict "user" .DefaultBranchBranch.DBBranch.Pusher}}{{template "shared/user/namelink" .DefaultBranchBranch.DBBranch.Pusher}}{{end}}

    {{if and $.IsWriter (not $.Repository.IsArchived) (not .IsDeleted)}} - +

    {{ctx.Locale.Tr "repo.branch.deleted_by" .DBBranch.DeletedBy.Name}} {{TimeSinceUnix .DBBranch.DeletedUnix ctx.Locale}}

    {{else}}
    {{if .IsProtected}}{{svg "octicon-shield-lock"}}{{end}} {{.DBBranch.Name}} - + {{template "repo/commit_statuses" dict "Status" (index $.CommitStatus .DBBranch.CommitID) "Statuses" (index $.CommitStatuses .DBBranch.CommitID)}}
    -

    {{svg "octicon-git-commit" 16 "gt-mr-2"}}{{ShortSha .DBBranch.CommitID}} · {{RenderCommitMessage $.Context .DBBranch.CommitMessage ($.Repository.ComposeMetas ctx)}} · {{ctx.Locale.Tr "org.repo_updated"}} {{TimeSince .DBBranch.CommitTime.AsTime ctx.Locale}}{{if .DBBranch.Pusher}}  {{template "shared/user/avatarlink" dict "user" .DBBranch.Pusher}}  {{template "shared/user/namelink" .DBBranch.Pusher}}{{end}}

    +

    {{svg "octicon-git-commit" 16 "tw-mr-1"}}{{ShortSha .DBBranch.CommitID}} · {{RenderCommitMessage $.Context .DBBranch.CommitMessage ($.Repository.ComposeMetas ctx)}} · {{ctx.Locale.Tr "org.repo_updated"}} {{TimeSince .DBBranch.CommitTime.AsTime ctx.Locale}}{{if .DBBranch.Pusher}}  {{template "shared/user/avatarlink" dict "user" .DBBranch.Pusher}}  {{template "shared/user/namelink" .DBBranch.Pusher}}{{end}}

    {{end}} @@ -124,29 +124,29 @@ {{else if and (not .DBBranch.IsDeleted) $.AllowsPulls (gt .CommitsAhead 0)}} - + {{end}} {{else if and .LatestPullRequest.HasMerged .MergeMovedOn}} {{if and (not .DBBranch.IsDeleted) $.AllowsPulls (gt .CommitsAhead 0)}} - + {{end}} {{else}} {{if not .LatestPullRequest.IsSameRepo}}{{.LatestPullRequest.BaseRepo.FullName}}{{end}}#{{.LatestPullRequest.Issue.Index}} {{if .LatestPullRequest.HasMerged}} - {{svg "octicon-git-merge" 16 "gt-mr-2"}}{{ctx.Locale.Tr "repo.pulls.merged"}} + {{svg "octicon-git-merge" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.pulls.merged"}} {{else if .LatestPullRequest.Issue.IsClosed}} - {{svg "octicon-git-pull-request" 16 "gt-mr-2"}}{{ctx.Locale.Tr "repo.issues.closed_title"}} + {{svg "octicon-git-pull-request" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.issues.closed_title"}} {{else}} - {{svg "octicon-git-pull-request" 16 "gt-mr-2"}}{{ctx.Locale.Tr "repo.issues.open_title"}} + {{svg "octicon-git-pull-request" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.issues.open_title"}} {{end}} {{end}} {{if and $.IsWriter (not $.Repository.IsArchived) (not .DBBranch.IsDeleted)}} - {{else}} - {{end}} diff --git a/templates/repo/branch_dropdown.tmpl b/templates/repo/branch_dropdown.tmpl index e389a3c758..707f670e97 100644 --- a/templates/repo/branch_dropdown.tmpl +++ b/templates/repo/branch_dropdown.tmpl @@ -70,8 +70,8 @@
    {{/* show dummy elements before Vue componment is mounted, this code must match the code in BranchTagSelector.vue */}} - + {{ctx.Locale.Tr "repo.pulls.compare_changes"}}
    diff --git a/templates/repo/commit_load_branches_and_tags.tmpl b/templates/repo/commit_load_branches_and_tags.tmpl index 49f7323845..9ab1e2fe05 100644 --- a/templates/repo/commit_load_branches_and_tags.tmpl +++ b/templates/repo/commit_load_branches_and_tags.tmpl @@ -1,18 +1,18 @@ {{if not .PageIsWiki}}
    -
    {{ctx.Locale.Tr "repo.commit.contained_in"}}
    -
    -
    {{svg "octicon-git-branch"}}
    +
    +
    {{svg "octicon-git-branch"}}
    -
    -
    {{svg "octicon-tag"}}
    +
    +
    {{svg "octicon-tag"}}
    diff --git a/templates/repo/commit_page.tmpl b/templates/repo/commit_page.tmpl index 345c28f475..3ae7fffa1c 100644 --- a/templates/repo/commit_page.tmpl +++ b/templates/repo/commit_page.tmpl @@ -18,8 +18,8 @@ {{end}} {{end}}
    -
    -

    {{RenderCommitMessage $.Context .Commit.Message ($.Repository.ComposeMetas ctx)}}{{template "repo/commit_statuses" dict "Status" .CommitStatus "Statuses" .CommitStatuses}}

    +
    +

    {{RenderCommitMessage $.Context .Commit.Message ($.Repository.ComposeMetas ctx)}}{{template "repo/commit_statuses" dict "Status" .CommitStatus "Statuses" .CommitStatuses}}

    {{if not $.PageIsWiki}} -
    +
    {{if .Author}} - {{ctx.AvatarUtils.Avatar .Author 28 "gt-mr-3"}} + {{ctx.AvatarUtils.Avatar .Author 28 "tw-mr-2"}} {{if .Author.FullName}} {{.Author.FullName}} {{else}} {{.Commit.Author.Name}} {{end}} {{else}} - {{ctx.AvatarUtils.AvatarByEmail .Commit.Author.Email .Commit.Author.Email 28 "gt-mr-3"}} + {{ctx.AvatarUtils.AvatarByEmail .Commit.Author.Email .Commit.Author.Email 28 "tw-mr-2"}} {{.Commit.Author.Name}} {{end}} - {{TimeSince .Commit.Author.When ctx.Locale}} + {{TimeSince .Commit.Author.When ctx.Locale}} {{if or (ne .Commit.Committer.Name .Commit.Author.Name) (ne .Commit.Committer.Email .Commit.Author.Email)}} - {{ctx.Locale.Tr "repo.diff.committed_by"}} + {{ctx.Locale.Tr "repo.diff.committed_by"}} {{if ne .Verification.CommittingUser.ID 0}} - {{ctx.AvatarUtils.Avatar .Verification.CommittingUser 28 "gt-mx-3"}} + {{ctx.AvatarUtils.Avatar .Verification.CommittingUser 28 "tw-mx-2"}} {{.Commit.Committer.Name}} {{else}} - {{ctx.AvatarUtils.AvatarByEmail .Commit.Committer.Email .Commit.Committer.Name 28 "gt-mr-3"}} + {{ctx.AvatarUtils.AvatarByEmail .Commit.Committer.Email .Commit.Committer.Name 28 "tw-mr-2"}} {{.Commit.Committer.Name}} {{end}} {{end}} @@ -184,73 +184,73 @@
    {{if .Commit.Signature}} -
    +
    {{if .Verification.Verified}} {{if ne .Verification.SigningUser.ID 0}} - {{svg "gitea-lock" 16 "gt-mr-3"}} + {{svg "gitea-lock" 16 "tw-mr-2"}} {{if eq .Verification.TrustStatus "trusted"}} - {{ctx.Locale.Tr "repo.commits.signed_by"}}: + {{ctx.Locale.Tr "repo.commits.signed_by"}}: {{else if eq .Verification.TrustStatus "untrusted"}} - {{ctx.Locale.Tr "repo.commits.signed_by_untrusted_user"}}: + {{ctx.Locale.Tr "repo.commits.signed_by_untrusted_user"}}: {{else}} - {{ctx.Locale.Tr "repo.commits.signed_by_untrusted_user_unmatched"}}: + {{ctx.Locale.Tr "repo.commits.signed_by_untrusted_user_unmatched"}}: {{end}} - {{ctx.AvatarUtils.Avatar .Verification.SigningUser 28 "gt-mr-3"}} + {{ctx.AvatarUtils.Avatar .Verification.SigningUser 28 "tw-mr-2"}} {{.Verification.SigningUser.GetDisplayName}} {{else}} - {{svg "gitea-lock-cog" 16 "gt-mr-3"}} - {{ctx.Locale.Tr "repo.commits.signed_by"}}: - {{ctx.AvatarUtils.AvatarByEmail .Verification.SigningEmail "" 28 "gt-mr-3"}} + {{svg "gitea-lock-cog" 16 "tw-mr-2"}} + {{ctx.Locale.Tr "repo.commits.signed_by"}}: + {{ctx.AvatarUtils.AvatarByEmail .Verification.SigningEmail "" 28 "tw-mr-2"}} {{.Verification.SigningUser.GetDisplayName}} {{end}} {{else}} - {{svg "gitea-unlock" 16 "gt-mr-3"}} + {{svg "gitea-unlock" 16 "tw-mr-2"}} {{ctx.Locale.Tr .Verification.Reason}} {{end}}
    {{if .Verification.Verified}} {{if ne .Verification.SigningUser.ID 0}} - {{svg "octicon-verified" 16 "gt-mr-3"}} + {{svg "octicon-verified" 16 "tw-mr-2"}} {{if .Verification.SigningSSHKey}} - {{ctx.Locale.Tr "repo.commits.ssh_key_fingerprint"}}: + {{ctx.Locale.Tr "repo.commits.ssh_key_fingerprint"}}: {{.Verification.SigningSSHKey.Fingerprint}} {{else}} - {{ctx.Locale.Tr "repo.commits.gpg_key_id"}}: + {{ctx.Locale.Tr "repo.commits.gpg_key_id"}}: {{.Verification.SigningKey.PaddedKeyID}} {{end}} {{else}} - {{svg "octicon-unverified" 16 "gt-mr-3"}} + {{svg "octicon-unverified" 16 "tw-mr-2"}} {{if .Verification.SigningSSHKey}} - {{ctx.Locale.Tr "repo.commits.ssh_key_fingerprint"}}: + {{ctx.Locale.Tr "repo.commits.ssh_key_fingerprint"}}: {{.Verification.SigningSSHKey.Fingerprint}} {{else}} - {{ctx.Locale.Tr "repo.commits.gpg_key_id"}}: + {{ctx.Locale.Tr "repo.commits.gpg_key_id"}}: {{.Verification.SigningKey.PaddedKeyID}} {{end}} {{end}} {{else if .Verification.Warning}} - {{svg "octicon-unverified" 16 "gt-mr-3"}} + {{svg "octicon-unverified" 16 "tw-mr-2"}} {{if .Verification.SigningSSHKey}} - {{ctx.Locale.Tr "repo.commits.ssh_key_fingerprint"}}: + {{ctx.Locale.Tr "repo.commits.ssh_key_fingerprint"}}: {{.Verification.SigningSSHKey.Fingerprint}} {{else}} - {{ctx.Locale.Tr "repo.commits.gpg_key_id"}}: + {{ctx.Locale.Tr "repo.commits.gpg_key_id"}}: {{.Verification.SigningKey.PaddedKeyID}} {{end}} {{else}} {{if .Verification.SigningKey}} {{if ne .Verification.SigningKey.KeyID ""}} - {{svg "octicon-verified" 16 "gt-mr-3"}} - {{ctx.Locale.Tr "repo.commits.gpg_key_id"}}: + {{svg "octicon-verified" 16 "tw-mr-2"}} + {{ctx.Locale.Tr "repo.commits.gpg_key_id"}}: {{.Verification.SigningKey.PaddedKeyID}} {{end}} {{end}} {{if .Verification.SigningSSHKey}} {{if ne .Verification.SigningSSHKey.Fingerprint ""}} - {{svg "octicon-verified" 16 "gt-mr-3"}} - {{ctx.Locale.Tr "repo.commits.ssh_key_fingerprint"}}: + {{svg "octicon-verified" 16 "tw-mr-2"}} + {{ctx.Locale.Tr "repo.commits.ssh_key_fingerprint"}}: {{.Verification.SigningSSHKey.Fingerprint}} {{end}} {{end}} @@ -260,7 +260,7 @@ {{end}} {{if .NoteRendered}}
    - {{svg "octicon-note" 16 "gt-mr-3"}} + {{svg "octicon-note" 16 "tw-mr-2"}} {{ctx.Locale.Tr "repo.diff.git-notes"}}: {{if .NoteAuthor}} diff --git a/templates/repo/commits.tmpl b/templates/repo/commits.tmpl index 1263bc9dd4..86df88f6e3 100644 --- a/templates/repo/commits.tmpl +++ b/templates/repo/commits.tmpl @@ -5,7 +5,7 @@ {{template "repo/sub_menu" .}}
    - {{template "repo/branch_dropdown" dict "root" . "ContainerClasses" "gt-mr-2"}} + {{template "repo/branch_dropdown" dict "root" . "ContainerClasses" "tw-mr-1"}} {{svg "octicon-git-branch"}} {{ctx.Locale.Tr "repo.commit_graph"}} diff --git a/templates/repo/commits_list.tmpl b/templates/repo/commits_list.tmpl index 4eb31e0e8e..99787f715f 100644 --- a/templates/repo/commits_list.tmpl +++ b/templates/repo/commits_list.tmpl @@ -19,9 +19,9 @@ {{if .User.FullName}} {{$userName = .User.FullName}} {{end}} - {{ctx.AvatarUtils.Avatar .User 28 "gt-mr-2"}}{{$userName}} + {{ctx.AvatarUtils.Avatar .User 28 "tw-mr-1"}}{{$userName}} {{else}} - {{ctx.AvatarUtils.AvatarByEmail .Author.Email .Author.Name 28 "gt-mr-2"}} + {{ctx.AvatarUtils.AvatarByEmail .Author.Email .Author.Name 28 "tw-mr-1"}} {{$userName}} {{end}} @@ -76,10 +76,10 @@ {{else}} {{TimeSince .Author.When ctx.Locale}} {{end}} - - + + {{svg "octicon-file-code"}} diff --git a/templates/repo/commits_list_small.tmpl b/templates/repo/commits_list_small.tmpl index b195f06483..cb867df65a 100644 --- a/templates/repo/commits_list_small.tmpl +++ b/templates/repo/commits_list_small.tmpl @@ -30,7 +30,7 @@ {{$class = (print $class " isWarning")}} {{end}} {{end}} - + {{ShortSha .ID.String}} {{if .Signature}} {{template "repo/shabox_badge" dict "root" $.root "verification" .Verification}} diff --git a/templates/repo/commits_table.tmpl b/templates/repo/commits_table.tmpl index f04dedd49d..7249becbab 100644 --- a/templates/repo/commits_table.tmpl +++ b/templates/repo/commits_table.tmpl @@ -10,9 +10,9 @@
    {{if .IsDiffCompare}} {{end}} diff --git a/templates/repo/diff/box.tmpl b/templates/repo/diff/box.tmpl index b5c13938e8..1dcca2b907 100644 --- a/templates/repo/diff/box.tmpl +++ b/templates/repo/diff/box.tmpl @@ -1,7 +1,7 @@ {{$showFileTree := (and (not .DiffNotAvailable) (gt .Diff.NumFiles 1))}}
    -
    +
    {{if $showFileTree}}
    {{if and .PageIsPullFiles $.SignedUserID (not .IsArchived) (not .DiffNotAvailable)}} -
    +
    @@ -109,10 +109,10 @@ {{$showFileViewToggle := or $isImage (and (not $file.IsIncomplete) $isCsv)}} {{$isExpandable := or (gt $file.Addition 0) (gt $file.Deletion 0) $file.IsBin}} {{$isReviewFile := and $.IsSigned $.PageIsPullFiles (not $.IsArchived) $.IsShowingAllCommits}} -
    +

    -
    {{if $file.IsBin}} - + {{ctx.Locale.Tr "repo.diff.bin"}} {{else}} @@ -129,7 +129,7 @@ {{end}}
    {{if $file.IsRenamed}}{{$file.OldName}} → {{end}}{{$file.Name}}{{if .IsLFSFile}} ({{ctx.Locale.Tr "repo.stored_lfs"}}){{end}} - + {{if $file.IsGenerated}} {{ctx.Locale.Tr "repo.diff.generated"}} {{end}} @@ -139,9 +139,9 @@ {{if and $file.Mode $file.OldMode}} {{$old := ctx.Locale.Tr ($file.ModeTranslationKey $file.OldMode)}} {{$new := ctx.Locale.Tr ($file.ModeTranslationKey $file.Mode)}} - {{ctx.Locale.Tr "git.filemode.changed_filemode" $old $new}} + {{ctx.Locale.Tr "git.filemode.changed_filemode" $old $new}} {{else if $file.Mode}} - {{ctx.Locale.Tr ($file.ModeTranslationKey $file.Mode)}} + {{ctx.Locale.Tr ($file.ModeTranslationKey $file.Mode)}} {{end}}
    @@ -220,7 +220,7 @@ {{end}} {{if .Diff.IsIncomplete}} -
    +

    {{ctx.Locale.Tr "repo.diff.too_many_files"}} {{ctx.Locale.Tr "repo.diff.show_more"}} diff --git a/templates/repo/diff/comment_form.tmpl b/templates/repo/diff/comment_form.tmpl index 6005ea28ef..d797e89444 100644 --- a/templates/repo/diff/comment_form.tmpl +++ b/templates/repo/diff/comment_form.tmpl @@ -25,7 +25,7 @@

    {{end}} -

    -
    +
    {{template "repo/graph/svgcontainer" .}} {{template "repo/graph/commits" .}}
    diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl index 2cb18bf8e7..8a8664f147 100644 --- a/templates/repo/home.tmpl +++ b/templates/repo/home.tmpl @@ -24,7 +24,7 @@
    {{end}} {{if and .Permission.IsAdmin (not .Repository.IsArchived)}} -
    +
    -