From 7dde3e64894edf9949f5199c0d1084bb3015cac9 Mon Sep 17 00:00:00 2001 From: Bruno Sofiato Date: Tue, 17 Sep 2024 15:33:11 -0300 Subject: [PATCH 1/9] Included tag search capabilities (#32045) Resolves #31998 The first screenshot shows the tag page without any filter being applied: ![image](https://github.com/user-attachments/assets/eac0e51c-9e48-42b2-bb1c-a25896ca40cb) The second one, shows the page when the given filter returns no tag: ![image](https://github.com/user-attachments/assets/98df191e-1a7b-4947-b0ef-4987a0293c3e) The last one shows a single tag being filtered: ![image](https://github.com/user-attachments/assets/79c7e05e-8c86-4f06-b17e-15818b7b9291) Signed-off-by: Bruno Sofiato --- models/repo/release.go | 6 ++++++ options/locale/locale_en-US.ini | 2 ++ routers/web/repo/release.go | 18 ++++++++++++++---- templates/repo/tag/list.tmpl | 16 ++++++++++++---- 4 files changed, 34 insertions(+), 8 deletions(-) diff --git a/models/repo/release.go b/models/repo/release.go index 1643258301..7c66cbc1af 100644 --- a/models/repo/release.go +++ b/models/repo/release.go @@ -234,6 +234,7 @@ type FindReleasesOptions struct { IsDraft optional.Option[bool] TagNames []string HasSha1 optional.Option[bool] // useful to find draft releases which are created with existing tags + NamePattern optional.Option[string] } func (opts FindReleasesOptions) ToConds() builder.Cond { @@ -261,6 +262,11 @@ func (opts FindReleasesOptions) ToConds() builder.Cond { cond = cond.And(builder.Eq{"sha1": ""}) } } + + if opts.NamePattern.Has() && opts.NamePattern.Value() != "" { + cond = cond.And(builder.Like{"lower_tag_name", strings.ToLower(opts.NamePattern.Value())}) + } + return cond } diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 601671d8b9..951994253a 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -178,6 +178,8 @@ code_search_by_git_grep = Current code search results are provided by "git grep" package_kind = Search packages... project_kind = Search projects... branch_kind = Search branches... +tag_kind = Search tags... +tag_tooltip = Search for matching tags. Use '%' to match any sequence of numbers. commit_kind = Search commits... runner_kind = Search runners... no_results = No matching results found. diff --git a/routers/web/repo/release.go b/routers/web/repo/release.go index a283303492..f551fffe95 100644 --- a/routers/web/repo/release.go +++ b/routers/web/repo/release.go @@ -214,6 +214,8 @@ func TagsList(ctx *context.Context) { ctx.Data["HideBranchesInDropdown"] = true ctx.Data["CanCreateRelease"] = ctx.Repo.CanWrite(unit.TypeReleases) && !ctx.Repo.Repository.IsArchived + namePattern := ctx.FormTrim("q") + listOptions := db.ListOptions{ Page: ctx.FormInt("page"), PageSize: ctx.FormInt("limit"), @@ -233,6 +235,7 @@ func TagsList(ctx *context.Context) { IncludeTags: true, HasSha1: optional.Some(true), RepoID: ctx.Repo.Repository.ID, + NamePattern: optional.Some(namePattern), } releases, err := db.Find[repo_model.Release](ctx, opts) @@ -241,14 +244,21 @@ func TagsList(ctx *context.Context) { return } - ctx.Data["Releases"] = releases + count, err := db.Count[repo_model.Release](ctx, opts) + if err != nil { + ctx.ServerError("GetReleasesByRepoID", err) + return + } - numTags := ctx.Data["NumTags"].(int64) - pager := context.NewPagination(int(numTags), opts.PageSize, opts.Page, 5) + ctx.Data["Keyword"] = namePattern + ctx.Data["Releases"] = releases + ctx.Data["TagCount"] = count + + pager := context.NewPagination(int(count), opts.PageSize, opts.Page, 5) pager.SetDefaultParams(ctx) ctx.Data["Page"] = pager - ctx.Data["PageIsViewCode"] = !ctx.Repo.Repository.UnitEnabled(ctx, unit.TypeReleases) + ctx.HTML(http.StatusOK, tplTagsList) } diff --git a/templates/repo/tag/list.tmpl b/templates/repo/tag/list.tmpl index 354808ed2e..eac846da7d 100644 --- a/templates/repo/tag/list.tmpl +++ b/templates/repo/tag/list.tmpl @@ -4,14 +4,19 @@
{{template "base/alert" .}} {{template "repo/release_tag_header" .}} - {{if .Releases}}

- {{svg "octicon-tag" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.release.tags"}} + {{.TagCount}} {{ctx.Locale.Tr "repo.release.tags"}}

{{$canReadReleases := $.Permission.CanRead ctx.Consts.RepoUnitTypeReleases}} +
+
+ {{template "shared/search/combo" dict "Value" .Keyword "Placeholder" (ctx.Locale.Tr "search.tag_kind") "Tooltip" (ctx.Locale.Tr "search.tag_tooltip")}} +
+
+ {{if .Releases}} {{range $idx, $release := .Releases}} @@ -57,9 +62,12 @@ {{end}}
+ {{else}} + {{if .NumTags}} +

{{ctx.Locale.Tr "no_results_found"}}

+ {{end}} + {{end}}
- {{end}} - {{template "base/paginate" .}}
From f38e1014483b84f4541ffb354cd5dfdd7e000e2c Mon Sep 17 00:00:00 2001 From: hiifong Date: Wed, 18 Sep 2024 03:02:48 +0800 Subject: [PATCH 2/9] Lazy load avatar images (#32051) --- modules/templates/util_avatar.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/templates/util_avatar.go b/modules/templates/util_avatar.go index 85832cf264..afc1091516 100644 --- a/modules/templates/util_avatar.go +++ b/modules/templates/util_avatar.go @@ -34,7 +34,7 @@ func AvatarHTML(src string, size int, class, name string) template.HTML { name = "avatar" } - return template.HTML(``) + return template.HTML(``) } // Avatar renders user avatars. args: user, size (int), class (string) From 8cdf4e29c4646cd3c35453e09a153406650c4fbc Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 18 Sep 2024 03:35:59 +0800 Subject: [PATCH 3/9] Fix CI (#32062) --- go.mod | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/go.mod b/go.mod index fae080f12f..dd36f63986 100644 --- a/go.mod +++ b/go.mod @@ -2,6 +2,11 @@ module code.gitea.io/gitea go 1.23 +// rfc5280 said: "The serial number is an integer assigned by the CA to each certificate." +// But some CAs use negative serial number, just relax the check. related: +// Default TLS cert uses negative serial number #895 https://github.com/microsoft/mssql-docker/issues/895 +godebug x509negativeserial=1 + require ( code.gitea.io/actions-proto-go v0.4.0 code.gitea.io/gitea-vet v0.2.3 From 55f1fcf0ad20d69fcd62c764f6da74ae56bd5f73 Mon Sep 17 00:00:00 2001 From: KN4CK3R Date: Tue, 17 Sep 2024 22:56:26 +0200 Subject: [PATCH 4/9] Add missing comment reply handling (#32050) Fixes #31937 - Add missing comment reply handling - Use `onGiteaRun` in the test because the fixtures are not present otherwise (did this behaviour change?) Compare without whitespaces. --- services/mailer/incoming/incoming_handler.go | 62 ++-- tests/integration/incoming_email_test.go | 304 ++++++++++--------- 2 files changed, 186 insertions(+), 180 deletions(-) diff --git a/services/mailer/incoming/incoming_handler.go b/services/mailer/incoming/incoming_handler.go index dc0b539822..38a234eac1 100644 --- a/services/mailer/incoming/incoming_handler.go +++ b/services/mailer/incoming/incoming_handler.go @@ -82,43 +82,40 @@ func (h *ReplyHandler) Handle(ctx context.Context, content *MailContent, doer *u return nil } + attachmentIDs := make([]string, 0, len(content.Attachments)) + if setting.Attachment.Enabled { + for _, attachment := range content.Attachments { + a, err := attachment_service.UploadAttachment(ctx, bytes.NewReader(attachment.Content), setting.Attachment.AllowedTypes, int64(len(attachment.Content)), &repo_model.Attachment{ + Name: attachment.Name, + UploaderID: doer.ID, + RepoID: issue.Repo.ID, + }) + if err != nil { + if upload.IsErrFileTypeForbidden(err) { + log.Info("Skipping disallowed attachment type: %s", attachment.Name) + continue + } + return err + } + attachmentIDs = append(attachmentIDs, a.UUID) + } + } + + if content.Content == "" && len(attachmentIDs) == 0 { + return nil + } + switch r := ref.(type) { case *issues_model.Issue: - attachmentIDs := make([]string, 0, len(content.Attachments)) - if setting.Attachment.Enabled { - for _, attachment := range content.Attachments { - a, err := attachment_service.UploadAttachment(ctx, bytes.NewReader(attachment.Content), setting.Attachment.AllowedTypes, int64(len(attachment.Content)), &repo_model.Attachment{ - Name: attachment.Name, - UploaderID: doer.ID, - RepoID: issue.Repo.ID, - }) - if err != nil { - if upload.IsErrFileTypeForbidden(err) { - log.Info("Skipping disallowed attachment type: %s", attachment.Name) - continue - } - return err - } - attachmentIDs = append(attachmentIDs, a.UUID) - } - } - - if content.Content == "" && len(attachmentIDs) == 0 { - return nil - } - - _, err = issue_service.CreateIssueComment(ctx, doer, issue.Repo, issue, content.Content, attachmentIDs) + _, err := issue_service.CreateIssueComment(ctx, doer, issue.Repo, issue, content.Content, attachmentIDs) if err != nil { return fmt.Errorf("CreateIssueComment failed: %w", err) } case *issues_model.Comment: comment := r - if content.Content == "" { - return nil - } - - if comment.Type == issues_model.CommentTypeCode { + switch comment.Type { + case issues_model.CommentTypeCode: _, err := pull_service.CreateCodeComment( ctx, doer, @@ -130,11 +127,16 @@ func (h *ReplyHandler) Handle(ctx context.Context, content *MailContent, doer *u false, // not pending review but a single review comment.ReviewID, "", - nil, + attachmentIDs, ) if err != nil { return fmt.Errorf("CreateCodeComment failed: %w", err) } + default: + _, err := issue_service.CreateIssueComment(ctx, doer, issue.Repo, issue, content.Content, attachmentIDs) + if err != nil { + return fmt.Errorf("CreateIssueComment failed: %w", err) + } } } return nil diff --git a/tests/integration/incoming_email_test.go b/tests/integration/incoming_email_test.go index 1284833864..88571303ac 100644 --- a/tests/integration/incoming_email_test.go +++ b/tests/integration/incoming_email_test.go @@ -7,6 +7,7 @@ import ( "io" "net" "net/smtp" + "net/url" "strings" "testing" "time" @@ -26,187 +27,190 @@ import ( ) func TestIncomingEmail(t *testing.T) { - defer tests.PrepareTestEnv(t)() + onGiteaRun(t, func(t *testing.T, u *url.URL) { + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}) - user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) - issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}) + t.Run("Payload", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() - t.Run("Payload", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() + comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 1}) - comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 1}) + _, err := incoming_payload.CreateReferencePayload(user) + assert.Error(t, err) - _, err := incoming_payload.CreateReferencePayload(user) - assert.Error(t, err) + issuePayload, err := incoming_payload.CreateReferencePayload(issue) + assert.NoError(t, err) + commentPayload, err := incoming_payload.CreateReferencePayload(comment) + assert.NoError(t, err) - issuePayload, err := incoming_payload.CreateReferencePayload(issue) - assert.NoError(t, err) - commentPayload, err := incoming_payload.CreateReferencePayload(comment) - assert.NoError(t, err) + _, err = incoming_payload.GetReferenceFromPayload(db.DefaultContext, []byte{1, 2, 3}) + assert.Error(t, err) - _, err = incoming_payload.GetReferenceFromPayload(db.DefaultContext, []byte{1, 2, 3}) - assert.Error(t, err) + ref, err := incoming_payload.GetReferenceFromPayload(db.DefaultContext, issuePayload) + assert.NoError(t, err) + assert.IsType(t, ref, new(issues_model.Issue)) + assert.EqualValues(t, issue.ID, ref.(*issues_model.Issue).ID) - ref, err := incoming_payload.GetReferenceFromPayload(db.DefaultContext, issuePayload) - assert.NoError(t, err) - assert.IsType(t, ref, new(issues_model.Issue)) - assert.EqualValues(t, issue.ID, ref.(*issues_model.Issue).ID) + ref, err = incoming_payload.GetReferenceFromPayload(db.DefaultContext, commentPayload) + assert.NoError(t, err) + assert.IsType(t, ref, new(issues_model.Comment)) + assert.EqualValues(t, comment.ID, ref.(*issues_model.Comment).ID) + }) - ref, err = incoming_payload.GetReferenceFromPayload(db.DefaultContext, commentPayload) - assert.NoError(t, err) - assert.IsType(t, ref, new(issues_model.Comment)) - assert.EqualValues(t, comment.ID, ref.(*issues_model.Comment).ID) - }) + t.Run("Token", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() - t.Run("Token", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() + payload := []byte{1, 2, 3, 4, 5} - payload := []byte{1, 2, 3, 4, 5} + token, err := token_service.CreateToken(token_service.ReplyHandlerType, user, payload) + assert.NoError(t, err) + assert.NotEmpty(t, token) - token, err := token_service.CreateToken(token_service.ReplyHandlerType, user, payload) - assert.NoError(t, err) - assert.NotEmpty(t, token) + ht, u, p, err := token_service.ExtractToken(db.DefaultContext, token) + assert.NoError(t, err) + assert.Equal(t, token_service.ReplyHandlerType, ht) + assert.Equal(t, user.ID, u.ID) + assert.Equal(t, payload, p) + }) - ht, u, p, err := token_service.ExtractToken(db.DefaultContext, token) - assert.NoError(t, err) - assert.Equal(t, token_service.ReplyHandlerType, ht) - assert.Equal(t, user.ID, u.ID) - assert.Equal(t, payload, p) - }) + t.Run("Handler", func(t *testing.T) { + t.Run("Reply", func(t *testing.T) { + t.Run("Comment", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() - t.Run("Handler", func(t *testing.T) { - t.Run("Reply", func(t *testing.T) { - t.Run("Comment", func(t *testing.T) { + handler := &incoming.ReplyHandler{} + + payload, err := incoming_payload.CreateReferencePayload(issue) + assert.NoError(t, err) + + assert.Error(t, handler.Handle(db.DefaultContext, &incoming.MailContent{}, nil, payload)) + assert.NoError(t, handler.Handle(db.DefaultContext, &incoming.MailContent{}, user, payload)) + + content := &incoming.MailContent{ + Content: "reply by mail", + Attachments: []*incoming.Attachment{ + { + Name: "attachment.txt", + Content: []byte("test"), + }, + }, + } + + assert.NoError(t, handler.Handle(db.DefaultContext, content, user, payload)) + + comments, err := issues_model.FindComments(db.DefaultContext, &issues_model.FindCommentsOptions{ + IssueID: issue.ID, + Type: issues_model.CommentTypeComment, + }) + assert.NoError(t, err) + assert.NotEmpty(t, comments) + comment := comments[len(comments)-1] + assert.Equal(t, user.ID, comment.PosterID) + assert.Equal(t, content.Content, comment.Content) + assert.NoError(t, comment.LoadAttachments(db.DefaultContext)) + assert.Len(t, comment.Attachments, 1) + attachment := comment.Attachments[0] + assert.Equal(t, content.Attachments[0].Name, attachment.Name) + assert.EqualValues(t, 4, attachment.Size) + }) + + t.Run("CodeComment", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 6}) + issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: comment.IssueID}) + + handler := &incoming.ReplyHandler{} + content := &incoming.MailContent{ + Content: "code reply by mail", + Attachments: []*incoming.Attachment{ + { + Name: "attachment.txt", + Content: []byte("test"), + }, + }, + } + + payload, err := incoming_payload.CreateReferencePayload(comment) + assert.NoError(t, err) + + assert.NoError(t, handler.Handle(db.DefaultContext, content, user, payload)) + + comments, err := issues_model.FindComments(db.DefaultContext, &issues_model.FindCommentsOptions{ + IssueID: issue.ID, + Type: issues_model.CommentTypeCode, + }) + assert.NoError(t, err) + assert.NotEmpty(t, comments) + comment = comments[len(comments)-1] + assert.Equal(t, user.ID, comment.PosterID) + assert.Equal(t, content.Content, comment.Content) + assert.NoError(t, comment.LoadAttachments(db.DefaultContext)) + assert.Len(t, comment.Attachments, 1) + attachment := comment.Attachments[0] + assert.Equal(t, content.Attachments[0].Name, attachment.Name) + assert.EqualValues(t, 4, attachment.Size) + }) + }) + + t.Run("Unsubscribe", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - handler := &incoming.ReplyHandler{} + watching, err := issues_model.CheckIssueWatch(db.DefaultContext, user, issue) + assert.NoError(t, err) + assert.True(t, watching) + + handler := &incoming.UnsubscribeHandler{} + + content := &incoming.MailContent{ + Content: "unsub me", + } payload, err := incoming_payload.CreateReferencePayload(issue) assert.NoError(t, err) - assert.Error(t, handler.Handle(db.DefaultContext, &incoming.MailContent{}, nil, payload)) - assert.NoError(t, handler.Handle(db.DefaultContext, &incoming.MailContent{}, user, payload)) - - content := &incoming.MailContent{ - Content: "reply by mail", - Attachments: []*incoming.Attachment{ - { - Name: "attachment.txt", - Content: []byte("test"), - }, - }, - } - assert.NoError(t, handler.Handle(db.DefaultContext, content, user, payload)) - comments, err := issues_model.FindComments(db.DefaultContext, &issues_model.FindCommentsOptions{ - IssueID: issue.ID, - Type: issues_model.CommentTypeComment, - }) + watching, err = issues_model.CheckIssueWatch(db.DefaultContext, user, issue) assert.NoError(t, err) - assert.NotEmpty(t, comments) - comment := comments[len(comments)-1] - assert.Equal(t, user.ID, comment.PosterID) - assert.Equal(t, content.Content, comment.Content) - assert.NoError(t, comment.LoadAttachments(db.DefaultContext)) - assert.Len(t, comment.Attachments, 1) - attachment := comment.Attachments[0] - assert.Equal(t, content.Attachments[0].Name, attachment.Name) - assert.EqualValues(t, 4, attachment.Size) + assert.False(t, watching) }) + }) - t.Run("CodeComment", func(t *testing.T) { + if setting.IncomingEmail.Enabled { + // This test connects to the configured email server and is currently only enabled for MySql integration tests. + // It sends a reply to create a comment. If the comment is not detected after 10 seconds the test fails. + t.Run("IMAP", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 6}) - issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: comment.IssueID}) - - handler := &incoming.ReplyHandler{} - content := &incoming.MailContent{ - Content: "code reply by mail", - Attachments: []*incoming.Attachment{ - { - Name: "attachment.txt", - Content: []byte("test"), - }, - }, - } - - payload, err := incoming_payload.CreateReferencePayload(comment) + payload, err := incoming_payload.CreateReferencePayload(issue) + assert.NoError(t, err) + token, err := token_service.CreateToken(token_service.ReplyHandlerType, user, payload) assert.NoError(t, err) - assert.NoError(t, handler.Handle(db.DefaultContext, content, user, payload)) - - comments, err := issues_model.FindComments(db.DefaultContext, &issues_model.FindCommentsOptions{ - IssueID: issue.ID, - Type: issues_model.CommentTypeCode, - }) + msg := gomail.NewMessage() + msg.SetHeader("To", strings.Replace(setting.IncomingEmail.ReplyToAddress, setting.IncomingEmail.TokenPlaceholder, token, 1)) + msg.SetHeader("From", user.Email) + msg.SetBody("text/plain", token) + err = gomail.Send(&smtpTestSender{}, msg) assert.NoError(t, err) - assert.NotEmpty(t, comments) - comment = comments[len(comments)-1] - assert.Equal(t, user.ID, comment.PosterID) - assert.Equal(t, content.Content, comment.Content) - assert.NoError(t, comment.LoadAttachments(db.DefaultContext)) - assert.Empty(t, comment.Attachments) + + assert.Eventually(t, func() bool { + comments, err := issues_model.FindComments(db.DefaultContext, &issues_model.FindCommentsOptions{ + IssueID: issue.ID, + Type: issues_model.CommentTypeComment, + }) + assert.NoError(t, err) + assert.NotEmpty(t, comments) + + comment := comments[len(comments)-1] + + return comment.PosterID == user.ID && comment.Content == token + }, 10*time.Second, 1*time.Second) }) - }) - - t.Run("Unsubscribe", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - watching, err := issues_model.CheckIssueWatch(db.DefaultContext, user, issue) - assert.NoError(t, err) - assert.True(t, watching) - - handler := &incoming.UnsubscribeHandler{} - - content := &incoming.MailContent{ - Content: "unsub me", - } - - payload, err := incoming_payload.CreateReferencePayload(issue) - assert.NoError(t, err) - - assert.NoError(t, handler.Handle(db.DefaultContext, content, user, payload)) - - watching, err = issues_model.CheckIssueWatch(db.DefaultContext, user, issue) - assert.NoError(t, err) - assert.False(t, watching) - }) + } }) - - if setting.IncomingEmail.Enabled { - // This test connects to the configured email server and is currently only enabled for MySql integration tests. - // It sends a reply to create a comment. If the comment is not detected after 10 seconds the test fails. - t.Run("IMAP", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - payload, err := incoming_payload.CreateReferencePayload(issue) - assert.NoError(t, err) - token, err := token_service.CreateToken(token_service.ReplyHandlerType, user, payload) - assert.NoError(t, err) - - msg := gomail.NewMessage() - msg.SetHeader("To", strings.Replace(setting.IncomingEmail.ReplyToAddress, setting.IncomingEmail.TokenPlaceholder, token, 1)) - msg.SetHeader("From", user.Email) - msg.SetBody("text/plain", token) - err = gomail.Send(&smtpTestSender{}, msg) - assert.NoError(t, err) - - assert.Eventually(t, func() bool { - comments, err := issues_model.FindComments(db.DefaultContext, &issues_model.FindCommentsOptions{ - IssueID: issue.ID, - Type: issues_model.CommentTypeComment, - }) - assert.NoError(t, err) - assert.NotEmpty(t, comments) - - comment := comments[len(comments)-1] - - return comment.PosterID == user.ID && comment.Content == token - }, 10*time.Second, 1*time.Second) - }) - } } // A simple SMTP mail sender used for integration tests. From 1fede04b83288d8a91304a83b7601699bb5cba04 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Wed, 18 Sep 2024 15:17:25 +0800 Subject: [PATCH 5/9] Refactor CSRF protector (#32057) Remove unused CSRF options, decouple "new csrf protector" and "prepare" logic, do not redirect to home page if CSRF validation falis (it shouldn't happen in daily usage, if it happens, redirecting to home doesn't help either but just makes the problem more complex for "fetch") --- options/locale/locale_en-US.ini | 2 - routers/web/web.go | 2 + services/context/context.go | 6 +- services/context/csrf.go | 192 ++++++++------------------ tests/integration/attachment_test.go | 3 +- tests/integration/csrf_test.go | 26 +--- tests/integration/repo_branch_test.go | 12 +- 7 files changed, 71 insertions(+), 172 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 951994253a..dc85d7c97c 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -222,8 +222,6 @@ string.desc = Z - A [error] occurred = An error occurred report_message = If you believe that this is a Gitea bug, please search for issues on GitHub or open a new issue if necessary. -missing_csrf = Bad Request: no CSRF token present -invalid_csrf = Bad Request: invalid CSRF token not_found = The target couldn't be found. network_error = Network error diff --git a/routers/web/web.go b/routers/web/web.go index 41b019e4b5..f1e941a84e 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -129,6 +129,8 @@ func webAuth(authMethod auth_service.Method) func(*context.Context) { // ensure the session uid is deleted _ = ctx.Session.Delete("uid") } + + ctx.Csrf.PrepareForSessionUser(ctx) } } diff --git a/services/context/context.go b/services/context/context.go index 69b65cbddb..42f7c3d9d1 100644 --- a/services/context/context.go +++ b/services/context/context.go @@ -138,10 +138,8 @@ func Contexter() func(next http.Handler) http.Handler { csrfOpts := CsrfOptions{ Secret: hex.EncodeToString(setting.GetGeneralTokenSigningSecret()), Cookie: setting.CSRFCookieName, - SetCookie: true, Secure: setting.SessionConfig.Secure, CookieHTTPOnly: setting.CSRFCookieHTTPOnly, - Header: "X-Csrf-Token", CookieDomain: setting.SessionConfig.Domain, CookiePath: setting.SessionConfig.CookiePath, SameSite: setting.SessionConfig.SameSite, @@ -167,7 +165,7 @@ func Contexter() func(next http.Handler) http.Handler { ctx.Base.AppendContextValue(WebContextKey, ctx) ctx.Base.AppendContextValueFunc(gitrepo.RepositoryContextKey, func() any { return ctx.Repo.GitRepo }) - ctx.Csrf = PrepareCSRFProtector(csrfOpts, ctx) + ctx.Csrf = NewCSRFProtector(csrfOpts) // Get the last flash message from cookie lastFlashCookie := middleware.GetSiteCookie(ctx.Req, CookieNameFlash) @@ -204,8 +202,6 @@ func Contexter() func(next http.Handler) http.Handler { ctx.Resp.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions) ctx.Data["SystemConfig"] = setting.Config() - ctx.Data["CsrfToken"] = ctx.Csrf.GetToken() - ctx.Data["CsrfTokenHtml"] = template.HTML(``) // FIXME: do we really always need these setting? There should be someway to have to avoid having to always set these ctx.Data["DisableMigrations"] = setting.Repository.DisableMigrations diff --git a/services/context/csrf.go b/services/context/csrf.go index 9b0dc2923b..9b66d613e3 100644 --- a/services/context/csrf.go +++ b/services/context/csrf.go @@ -20,64 +20,42 @@ package context import ( - "encoding/base32" - "fmt" + "html/template" "net/http" "strconv" "time" "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" - "code.gitea.io/gitea/modules/web/middleware" +) + +const ( + CsrfHeaderName = "X-Csrf-Token" + CsrfFormName = "_csrf" ) // CSRFProtector represents a CSRF protector and is used to get the current token and validate the token. type CSRFProtector interface { - // GetHeaderName returns HTTP header to search for token. - GetHeaderName() string - // GetFormName returns form value to search for token. - GetFormName() string - // GetToken returns the token. - GetToken() string - // Validate validates the token in http context. + // PrepareForSessionUser prepares the csrf protector for the current session user. + PrepareForSessionUser(ctx *Context) + // Validate validates the csrf token in http context. Validate(ctx *Context) - // DeleteCookie deletes the cookie + // DeleteCookie deletes the csrf cookie DeleteCookie(ctx *Context) } type csrfProtector struct { opt CsrfOptions - // Token generated to pass via header, cookie, or hidden form value. - Token string - // This value must be unique per user. - ID string -} - -// GetHeaderName returns the name of the HTTP header for csrf token. -func (c *csrfProtector) GetHeaderName() string { - return c.opt.Header -} - -// GetFormName returns the name of the form value for csrf token. -func (c *csrfProtector) GetFormName() string { - return c.opt.Form -} - -// GetToken returns the current token. This is typically used -// to populate a hidden form in an HTML template. -func (c *csrfProtector) GetToken() string { - return c.Token + // id must be unique per user. + id string + // token is the valid one which wil be used by end user and passed via header, cookie, or hidden form value. + token string } // CsrfOptions maintains options to manage behavior of Generate. type CsrfOptions struct { // The global secret value used to generate Tokens. Secret string - // HTTP header used to set and get token. - Header string - // Form value used to set and get token. - Form string // Cookie value used to set and get token. Cookie string // Cookie domain. @@ -87,103 +65,64 @@ type CsrfOptions struct { CookieHTTPOnly bool // SameSite set the cookie SameSite type SameSite http.SameSite - // Key used for getting the unique ID per user. - SessionKey string - // oldSessionKey saves old value corresponding to SessionKey. - oldSessionKey string - // If true, send token via X-Csrf-Token header. - SetHeader bool - // If true, send token via _csrf cookie. - SetCookie bool // Set the Secure flag to true on the cookie. Secure bool - // Disallow Origin appear in request header. - Origin bool - // Cookie lifetime. Default is 0 - CookieLifeTime int + // sessionKey is the key used for getting the unique ID per user. + sessionKey string + // oldSessionKey saves old value corresponding to sessionKey. + oldSessionKey string } -func prepareDefaultCsrfOptions(opt CsrfOptions) CsrfOptions { - if opt.Secret == "" { - randBytes, err := util.CryptoRandomBytes(8) - if err != nil { - // this panic can be handled by the recover() in http handlers - panic(fmt.Errorf("failed to generate random bytes: %w", err)) - } - opt.Secret = base32.StdEncoding.EncodeToString(randBytes) - } - if opt.Header == "" { - opt.Header = "X-Csrf-Token" - } - if opt.Form == "" { - opt.Form = "_csrf" - } - if opt.Cookie == "" { - opt.Cookie = "_csrf" - } - if opt.CookiePath == "" { - opt.CookiePath = "/" - } - if opt.SessionKey == "" { - opt.SessionKey = "uid" - } - if opt.CookieLifeTime == 0 { - opt.CookieLifeTime = int(CsrfTokenTimeout.Seconds()) - } - - opt.oldSessionKey = "_old_" + opt.SessionKey - return opt -} - -func newCsrfCookie(c *csrfProtector, value string) *http.Cookie { +func newCsrfCookie(opt *CsrfOptions, value string) *http.Cookie { return &http.Cookie{ - Name: c.opt.Cookie, + Name: opt.Cookie, Value: value, - Path: c.opt.CookiePath, - Domain: c.opt.CookieDomain, - MaxAge: c.opt.CookieLifeTime, - Secure: c.opt.Secure, - HttpOnly: c.opt.CookieHTTPOnly, - SameSite: c.opt.SameSite, + Path: opt.CookiePath, + Domain: opt.CookieDomain, + MaxAge: int(CsrfTokenTimeout.Seconds()), + Secure: opt.Secure, + HttpOnly: opt.CookieHTTPOnly, + SameSite: opt.SameSite, } } -// PrepareCSRFProtector returns a CSRFProtector to be used for every request. -// Additionally, depending on options set, generated tokens will be sent via Header and/or Cookie. -func PrepareCSRFProtector(opt CsrfOptions, ctx *Context) CSRFProtector { - opt = prepareDefaultCsrfOptions(opt) - x := &csrfProtector{opt: opt} - - if opt.Origin && len(ctx.Req.Header.Get("Origin")) > 0 { - return x +func NewCSRFProtector(opt CsrfOptions) CSRFProtector { + if opt.Secret == "" { + panic("CSRF secret is empty but it must be set") // it shouldn't happen because it is always set in code } + opt.Cookie = util.IfZero(opt.Cookie, "_csrf") + opt.CookiePath = util.IfZero(opt.CookiePath, "/") + opt.sessionKey = "uid" + opt.oldSessionKey = "_old_" + opt.sessionKey + return &csrfProtector{opt: opt} +} - x.ID = "0" - uidAny := ctx.Session.Get(opt.SessionKey) - if uidAny != nil { +func (c *csrfProtector) PrepareForSessionUser(ctx *Context) { + c.id = "0" + if uidAny := ctx.Session.Get(c.opt.sessionKey); uidAny != nil { switch uidVal := uidAny.(type) { case string: - x.ID = uidVal + c.id = uidVal case int64: - x.ID = strconv.FormatInt(uidVal, 10) + c.id = strconv.FormatInt(uidVal, 10) default: log.Error("invalid uid type in session: %T", uidAny) } } - oldUID := ctx.Session.Get(opt.oldSessionKey) - uidChanged := oldUID == nil || oldUID.(string) != x.ID - cookieToken := ctx.GetSiteCookie(opt.Cookie) + oldUID := ctx.Session.Get(c.opt.oldSessionKey) + uidChanged := oldUID == nil || oldUID.(string) != c.id + cookieToken := ctx.GetSiteCookie(c.opt.Cookie) needsNew := true if uidChanged { - _ = ctx.Session.Set(opt.oldSessionKey, x.ID) + _ = ctx.Session.Set(c.opt.oldSessionKey, c.id) } else if cookieToken != "" { // If cookie token presents, re-use existing unexpired token, else generate a new one. if issueTime, ok := ParseCsrfToken(cookieToken); ok { dur := time.Since(issueTime) // issueTime is not a monotonic-clock, the server time may change a lot to an early time. if dur >= -CsrfTokenRegenerationInterval && dur <= CsrfTokenRegenerationInterval { - x.Token = cookieToken + c.token = cookieToken needsNew = false } } @@ -191,42 +130,33 @@ func PrepareCSRFProtector(opt CsrfOptions, ctx *Context) CSRFProtector { if needsNew { // FIXME: actionId. - x.Token = GenerateCsrfToken(x.opt.Secret, x.ID, "POST", time.Now()) - if opt.SetCookie { - cookie := newCsrfCookie(x, x.Token) - ctx.Resp.Header().Add("Set-Cookie", cookie.String()) - } + c.token = GenerateCsrfToken(c.opt.Secret, c.id, "POST", time.Now()) + cookie := newCsrfCookie(&c.opt, c.token) + ctx.Resp.Header().Add("Set-Cookie", cookie.String()) } - if opt.SetHeader { - ctx.Resp.Header().Add(opt.Header, x.Token) - } - return x + ctx.Data["CsrfToken"] = c.token + ctx.Data["CsrfTokenHtml"] = template.HTML(``) } func (c *csrfProtector) validateToken(ctx *Context, token string) { - if !ValidCsrfToken(token, c.opt.Secret, c.ID, "POST", time.Now()) { + if !ValidCsrfToken(token, c.opt.Secret, c.id, "POST", time.Now()) { c.DeleteCookie(ctx) - if middleware.IsAPIPath(ctx.Req) { - // currently, there should be no access to the APIPath with CSRF token. because templates shouldn't use the `/api/` endpoints. - http.Error(ctx.Resp, "Invalid CSRF token.", http.StatusBadRequest) - } else { - ctx.Flash.Error(ctx.Tr("error.invalid_csrf")) - ctx.Redirect(setting.AppSubURL + "/") - } + // currently, there should be no access to the APIPath with CSRF token. because templates shouldn't use the `/api/` endpoints. + // FIXME: distinguish what the response is for: HTML (web page) or JSON (fetch) + http.Error(ctx.Resp, "Invalid CSRF token.", http.StatusBadRequest) } } // Validate should be used as a per route middleware. It attempts to get a token from an "X-Csrf-Token" // HTTP header and then a "_csrf" form value. If one of these is found, the token will be validated. -// If this validation fails, custom Error is sent in the reply. -// If neither a header nor form value is found, http.StatusBadRequest is sent. +// If this validation fails, http.StatusBadRequest is sent. func (c *csrfProtector) Validate(ctx *Context) { - if token := ctx.Req.Header.Get(c.GetHeaderName()); token != "" { + if token := ctx.Req.Header.Get(CsrfHeaderName); token != "" { c.validateToken(ctx, token) return } - if token := ctx.Req.FormValue(c.GetFormName()); token != "" { + if token := ctx.Req.FormValue(CsrfFormName); token != "" { c.validateToken(ctx, token) return } @@ -234,9 +164,7 @@ func (c *csrfProtector) Validate(ctx *Context) { } func (c *csrfProtector) DeleteCookie(ctx *Context) { - if c.opt.SetCookie { - cookie := newCsrfCookie(c, "") - cookie.MaxAge = -1 - ctx.Resp.Header().Add("Set-Cookie", cookie.String()) - } + cookie := newCsrfCookie(&c.opt, "") + cookie.MaxAge = -1 + ctx.Resp.Header().Add("Set-Cookie", cookie.String()) } diff --git a/tests/integration/attachment_test.go b/tests/integration/attachment_test.go index 8206d8f4dc..40969d26f2 100644 --- a/tests/integration/attachment_test.go +++ b/tests/integration/attachment_test.go @@ -59,7 +59,8 @@ func createAttachment(t *testing.T, session *TestSession, repoURL, filename stri func TestCreateAnonymousAttachment(t *testing.T) { defer tests.PrepareTestEnv(t)() session := emptyTestSession(t) - createAttachment(t, session, "user2/repo1", "image.png", generateImg(), http.StatusSeeOther) + // this test is not right because it just doesn't pass the CSRF validation + createAttachment(t, session, "user2/repo1", "image.png", generateImg(), http.StatusBadRequest) } func TestCreateIssueAttachment(t *testing.T) { diff --git a/tests/integration/csrf_test.go b/tests/integration/csrf_test.go index a789859889..fcb9661b8a 100644 --- a/tests/integration/csrf_test.go +++ b/tests/integration/csrf_test.go @@ -5,12 +5,10 @@ package integration import ( "net/http" - "strings" "testing" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/tests" "github.com/stretchr/testify/assert" @@ -25,28 +23,12 @@ func TestCsrfProtection(t *testing.T) { req := NewRequestWithValues(t, "POST", "/user/settings", map[string]string{ "_csrf": "fake_csrf", }) - session.MakeRequest(t, req, http.StatusSeeOther) - - resp := session.MakeRequest(t, req, http.StatusSeeOther) - loc := resp.Header().Get("Location") - assert.Equal(t, setting.AppSubURL+"/", loc) - resp = session.MakeRequest(t, NewRequest(t, "GET", loc), http.StatusOK) - htmlDoc := NewHTMLParser(t, resp.Body) - assert.Equal(t, "Bad Request: invalid CSRF token", - strings.TrimSpace(htmlDoc.doc.Find(".ui.message").Text()), - ) + resp := session.MakeRequest(t, req, http.StatusBadRequest) + assert.Contains(t, resp.Body.String(), "Invalid CSRF token") // test web form csrf via header. TODO: should use an UI api to test req = NewRequest(t, "POST", "/user/settings") req.Header.Add("X-Csrf-Token", "fake_csrf") - session.MakeRequest(t, req, http.StatusSeeOther) - - resp = session.MakeRequest(t, req, http.StatusSeeOther) - loc = resp.Header().Get("Location") - assert.Equal(t, setting.AppSubURL+"/", loc) - resp = session.MakeRequest(t, NewRequest(t, "GET", loc), http.StatusOK) - htmlDoc = NewHTMLParser(t, resp.Body) - assert.Equal(t, "Bad Request: invalid CSRF token", - strings.TrimSpace(htmlDoc.doc.Find(".ui.message").Text()), - ) + resp = session.MakeRequest(t, req, http.StatusBadRequest) + assert.Contains(t, resp.Body.String(), "Invalid CSRF token") } diff --git a/tests/integration/repo_branch_test.go b/tests/integration/repo_branch_test.go index d1bc9198c3..f5217374b0 100644 --- a/tests/integration/repo_branch_test.go +++ b/tests/integration/repo_branch_test.go @@ -17,7 +17,6 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/models/unittest" - "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/modules/translation" @@ -146,15 +145,8 @@ func TestCreateBranchInvalidCSRF(t *testing.T) { "_csrf": "fake_csrf", "new_branch_name": "test", }) - resp := session.MakeRequest(t, req, http.StatusSeeOther) - loc := resp.Header().Get("Location") - assert.Equal(t, setting.AppSubURL+"/", loc) - resp = session.MakeRequest(t, NewRequest(t, "GET", loc), http.StatusOK) - htmlDoc := NewHTMLParser(t, resp.Body) - assert.Equal(t, - "Bad Request: invalid CSRF token", - strings.TrimSpace(htmlDoc.doc.Find(".ui.message").Text()), - ) + resp := session.MakeRequest(t, req, http.StatusBadRequest) + assert.Contains(t, resp.Body.String(), "Invalid CSRF token") } func prepareBranch(t *testing.T, session *TestSession, repo *repo_model.Repository) { From adea500aa07eb05e4456b516b4d9ca5f41adb937 Mon Sep 17 00:00:00 2001 From: Kemal Zebari <60799661+kemzeb@users.noreply.github.com> Date: Wed, 18 Sep 2024 12:46:41 -0700 Subject: [PATCH 6/9] Resolve duplicate local string key related to PR comments (#32073) A regression in #31924 caused there to be two `issues.review.comment` keys in the English language locale file, leading to a problem when reading PR review histories that contain comments. This snapshot addresses this by making the newer key unique. --- models/issues/review.go | 2 +- options/locale/locale_en-US.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/models/issues/review.go b/models/issues/review.go index 9a08e265ff..9fb6d7573a 100644 --- a/models/issues/review.go +++ b/models/issues/review.go @@ -247,7 +247,7 @@ func (r *Review) TooltipContent() string { } return "repo.issues.review.official" case ReviewTypeComment: - return "repo.issues.review.comment" + return "repo.issues.review.commented" case ReviewTypeReject: return "repo.issues.review.rejected" case ReviewTypeRequest: diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index dc85d7c97c..957e73b171 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1757,7 +1757,7 @@ issues.review.hide_resolved = Hide resolved issues.review.resolve_conversation = Resolve conversation issues.review.un_resolve_conversation = Unresolve conversation issues.review.resolved_by = marked this conversation as resolved -issues.review.comment = Comment +issues.review.commented = Comment issues.review.official = Approved issues.review.requested = Review pending issues.review.rejected = Changes requested From 269c63092369322df8e4c6864acc9a433e5477a8 Mon Sep 17 00:00:00 2001 From: Exploding Dragon Date: Thu, 19 Sep 2024 04:15:03 +0800 Subject: [PATCH 7/9] Fix: database not update release when using `git push --tags --force` (#32040) link: https://codeberg.org/forgejo/forgejo/issues/4274 --------- Co-authored-by: Lunny Xiao --- services/repository/push.go | 18 ++++++++------- tests/integration/repo_tag_test.go | 36 ++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/services/repository/push.go b/services/repository/push.go index f27e6a58dd..8b81588c07 100644 --- a/services/repository/push.go +++ b/services/repository/push.go @@ -320,8 +320,9 @@ func pushUpdateAddTags(ctx context.Context, repo *repo_model.Repository, gitRepo } releases, err := db.Find[repo_model.Release](ctx, repo_model.FindReleasesOptions{ - RepoID: repo.ID, - TagNames: tags, + RepoID: repo.ID, + TagNames: tags, + IncludeTags: true, }) if err != nil { return fmt.Errorf("db.Find[repo_model.Release]: %w", err) @@ -382,12 +383,12 @@ func pushUpdateAddTags(ctx context.Context, repo *repo_model.Repository, gitRepo rel, has := relMap[lowerTag] + parts := strings.SplitN(tag.Message, "\n", 2) + note := "" + if len(parts) > 1 { + note = parts[1] + } if !has { - parts := strings.SplitN(tag.Message, "\n", 2) - note := "" - if len(parts) > 1 { - note = parts[1] - } rel = &repo_model.Release{ RepoID: repo.ID, Title: parts[0], @@ -408,10 +409,11 @@ func pushUpdateAddTags(ctx context.Context, repo *repo_model.Repository, gitRepo newReleases = append(newReleases, rel) } else { + rel.Title = parts[0] + rel.Note = note rel.Sha1 = commit.ID.String() rel.CreatedUnix = timeutil.TimeStamp(createdAt.Unix()) rel.NumCommits = commitsCount - rel.IsDraft = false if rel.IsTag && author != nil { rel.PublisherID = author.ID } diff --git a/tests/integration/repo_tag_test.go b/tests/integration/repo_tag_test.go index 6d3d85532c..d649f041cc 100644 --- a/tests/integration/repo_tag_test.go +++ b/tests/integration/repo_tag_test.go @@ -4,6 +4,7 @@ package integration import ( + "net/http" "net/url" "testing" @@ -18,6 +19,7 @@ import ( "code.gitea.io/gitea/tests" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestCreateNewTagProtected(t *testing.T) { @@ -60,6 +62,40 @@ func TestCreateNewTagProtected(t *testing.T) { }) }) + t.Run("GitTagForce", func(t *testing.T) { + onGiteaRun(t, func(t *testing.T, u *url.URL) { + httpContext := NewAPITestContext(t, owner.Name, repo.Name) + + dstPath := t.TempDir() + + u.Path = httpContext.GitPath() + u.User = url.UserPassword(owner.Name, userPassword) + + doGitClone(dstPath, u)(t) + + _, _, err := git.NewCommand(git.DefaultContext, "tag", "v-1.1", "-m", "force update", "--force").RunStdString(&git.RunOpts{Dir: dstPath}) + require.NoError(t, err) + + _, _, err = git.NewCommand(git.DefaultContext, "push", "--tags").RunStdString(&git.RunOpts{Dir: dstPath}) + require.NoError(t, err) + + _, _, err = git.NewCommand(git.DefaultContext, "tag", "v-1.1", "-m", "force update v2", "--force").RunStdString(&git.RunOpts{Dir: dstPath}) + require.NoError(t, err) + + _, _, err = git.NewCommand(git.DefaultContext, "push", "--tags").RunStdString(&git.RunOpts{Dir: dstPath}) + require.Error(t, err) + assert.Contains(t, err.Error(), "the tag already exists in the remote") + + _, _, err = git.NewCommand(git.DefaultContext, "push", "--tags", "--force").RunStdString(&git.RunOpts{Dir: dstPath}) + require.NoError(t, err) + req := NewRequestf(t, "GET", "/%s/releases/tag/v-1.1", repo.FullName()) + resp := MakeRequest(t, req, http.StatusOK) + htmlDoc := NewHTMLParser(t, resp.Body) + tagsTab := htmlDoc.Find(".release-list-title") + assert.Contains(t, tagsTab.Text(), "force update v2") + }) + }) + // Cleanup releases, err := db.Find[repo_model.Release](db.DefaultContext, repo_model.FindReleasesOptions{ IncludeTags: true, From 2fc347bcb362af533d0e99b3a34788128ac97010 Mon Sep 17 00:00:00 2001 From: GiteaBot Date: Thu, 19 Sep 2024 00:30:35 +0000 Subject: [PATCH 8/9] [skip ci] Updated translations via Crowdin --- options/locale/locale_cs-CZ.ini | 4 +--- options/locale/locale_de-DE.ini | 4 +--- options/locale/locale_el-GR.ini | 4 +--- options/locale/locale_es-ES.ini | 4 +--- options/locale/locale_fa-IR.ini | 3 +-- options/locale/locale_fi-FI.ini | 3 +-- options/locale/locale_fr-FR.ini | 4 +--- options/locale/locale_hu-HU.ini | 2 +- options/locale/locale_is-IS.ini | 4 +--- options/locale/locale_it-IT.ini | 4 +--- options/locale/locale_ja-JP.ini | 4 +--- options/locale/locale_ko-KR.ini | 2 +- options/locale/locale_lv-LV.ini | 4 +--- options/locale/locale_nl-NL.ini | 4 +--- options/locale/locale_pl-PL.ini | 4 +--- options/locale/locale_pt-BR.ini | 4 +--- options/locale/locale_pt-PT.ini | 6 +++--- options/locale/locale_ru-RU.ini | 4 +--- options/locale/locale_si-LK.ini | 3 +-- options/locale/locale_sk-SK.ini | 2 -- options/locale/locale_sv-SE.ini | 2 +- options/locale/locale_tr-TR.ini | 4 +--- options/locale/locale_uk-UA.ini | 3 +-- options/locale/locale_zh-CN.ini | 4 +--- options/locale/locale_zh-TW.ini | 4 +--- 25 files changed, 26 insertions(+), 64 deletions(-) diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini index 271bf11402..07cbfe2c8f 100644 --- a/options/locale/locale_cs-CZ.ini +++ b/options/locale/locale_cs-CZ.ini @@ -218,8 +218,6 @@ string.desc=Z – A [error] occurred=Došlo k chybě -missing_csrf=Špatný požadavek: Neexistuje CSRF token -invalid_csrf=Špatný požadavek: Neplatný CSRF token not_found=Cíl nebyl nalezen. network_error=Chyba sítě @@ -1701,7 +1699,6 @@ issues.dependency.add_error_dep_not_same_repo=Oba úkoly musí být ve stejném issues.review.self.approval=Nemůžete schválit svůj pull request. issues.review.self.rejection=Nemůžete požadovat změny ve svém vlastním pull requestu. issues.review.approve=schválil tyto změny %s -issues.review.comment=Okomentovat issues.review.dismissed=zamítl/a posouzení od %s %s issues.review.dismissed_label=Zamítnuto issues.review.left_comment=zanechal komentář @@ -1726,6 +1723,7 @@ issues.review.hide_resolved=Skrýt vyřešené issues.review.resolve_conversation=Vyřešit konverzaci issues.review.un_resolve_conversation=Nevyřešit konverzaci issues.review.resolved_by=označil tuto konverzaci jako vyřešenou +issues.review.commented=Okomentovat issues.assignee.error=Ne všichni zpracovatelé byli přidáni z důvodu neočekávané chyby. issues.reference_issue.body=Tělo zprávy issues.content_history.deleted=vymazáno diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index 78c9f0b41d..7512c54101 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -213,8 +213,6 @@ string.desc=Z–A [error] occurred=Ein Fehler ist aufgetreten -missing_csrf=Fehlerhafte Anfrage: Kein CSRF Token verfügbar -invalid_csrf=Fehlerhafte Anfrage: Ungültiger CSRF Token not_found=Das Ziel konnte nicht gefunden werden. network_error=Netzwerkfehler @@ -1681,7 +1679,6 @@ issues.dependency.add_error_dep_not_same_repo=Beide Issues müssen sich im selbe issues.review.self.approval=Du kannst nicht dein eigenen Pull-Request genehmigen. issues.review.self.rejection=Du kannst keine Änderungen an deinem eigenen Pull-Request anfragen. issues.review.approve=hat die Änderungen %s genehmigt -issues.review.comment=Kommentieren issues.review.dismissed=verwarf %ss Review %s issues.review.dismissed_label=Verworfen issues.review.left_comment=hat einen Kommentar hinterlassen @@ -1706,6 +1703,7 @@ issues.review.hide_resolved=Gelöste ausblenden issues.review.resolve_conversation=Diskussion als "erledigt" markieren issues.review.un_resolve_conversation=Diskussion als "nicht-erledigt" markieren issues.review.resolved_by=markierte diese Unterhaltung als gelöst +issues.review.commented=Kommentieren issues.assignee.error=Aufgrund eines unerwarteten Fehlers konnten nicht alle Beauftragten hinzugefügt werden. issues.reference_issue.body=Beschreibung issues.content_history.deleted=gelöscht diff --git a/options/locale/locale_el-GR.ini b/options/locale/locale_el-GR.ini index ef314b1392..48610abb34 100644 --- a/options/locale/locale_el-GR.ini +++ b/options/locale/locale_el-GR.ini @@ -184,8 +184,6 @@ string.desc=Z - A [error] occurred=Παρουσιάστηκε ένα σφάλμα -missing_csrf=Bad Request: δεν υπάρχει διακριτικό CSRF -invalid_csrf=Λάθος Αίτημα: μη έγκυρο διακριτικό CSRF not_found=Ο προορισμός δεν βρέθηκε. network_error=Σφάλμα δικτύου @@ -1603,7 +1601,6 @@ issues.dependency.add_error_dep_not_same_repo=Και τα δύο ζητήματ issues.review.self.approval=Δεν μπορείτε να εγκρίνετε το δικό σας pull request. issues.review.self.rejection=Δεν μπορείτε να ζητήσετε αλλαγές στο δικό σας pull request. issues.review.approve=ενέκρινε αυτές τις αλλαγές %s -issues.review.comment=Σχόλιο issues.review.dismissed=απέρριψε την αξιολόγηση %s %s issues.review.dismissed_label=Απορρίφθηκε issues.review.left_comment=άφησε ένα σχόλιο @@ -1628,6 +1625,7 @@ issues.review.hide_resolved=Απόκρυψη επιλυμένων issues.review.resolve_conversation=Επίλυση συνομιλίας issues.review.un_resolve_conversation=Ανεπίλυτη συνομιλία issues.review.resolved_by=σημείωση αυτή την συνομιλία ως επιλυμένη +issues.review.commented=Σχόλιο issues.assignee.error=Δεν προστέθηκαν όλοι οι παραλήπτες λόγω απροσδόκητου σφάλματος. issues.reference_issue.body=Σώμα issues.content_history.deleted=διαγράφηκε diff --git a/options/locale/locale_es-ES.ini b/options/locale/locale_es-ES.ini index 5c0384b447..7d637f3d3a 100644 --- a/options/locale/locale_es-ES.ini +++ b/options/locale/locale_es-ES.ini @@ -182,8 +182,6 @@ string.desc=Z - A [error] occurred=Ha ocurrido un error -missing_csrf=Solicitud incorrecta: sin token CSRF -invalid_csrf=Solicitud incorrecta: el token CSRF no es válido not_found=El objetivo no pudo ser encontrado. network_error=Error de red @@ -1593,7 +1591,6 @@ issues.dependency.add_error_dep_not_same_repo=Ambas incidencias deben estar en e issues.review.self.approval=No puede aprobar su propio pull request. issues.review.self.rejection=No puede sugerir cambios en su propio pull request. issues.review.approve=aprobado estos cambios %s -issues.review.comment=Comentario issues.review.dismissed=descartó la revisión de %s %s issues.review.dismissed_label=Descartado issues.review.left_comment=dejó un comentario @@ -1618,6 +1615,7 @@ issues.review.hide_resolved=Ocultar resueltos issues.review.resolve_conversation=Resolver conversación issues.review.un_resolve_conversation=Marcar conversación sin resolver issues.review.resolved_by=ha marcado esta conversación como resuelta +issues.review.commented=Comentario issues.assignee.error=No todos los asignados fueron añadidos debido a un error inesperado. issues.reference_issue.body=Cuerpo issues.content_history.deleted=borrado diff --git a/options/locale/locale_fa-IR.ini b/options/locale/locale_fa-IR.ini index b141c7959e..38fb70dae0 100644 --- a/options/locale/locale_fa-IR.ini +++ b/options/locale/locale_fa-IR.ini @@ -118,7 +118,6 @@ filter.private=خصوصی [filter] [error] -missing_csrf=درخواست بد: بلیط CSRF ندارد [startpage] app_desc=یک سرویس گیت بی‌درد سر و راحت @@ -1235,7 +1234,6 @@ issues.dependency.add_error_dep_not_same_repo=هر دو موضوع باید از issues.review.self.approval=شما نمی‌توانید تقاضای واکشی خود را تایید کنید. issues.review.self.rejection=شما نمی‌توانید تقاضا تغییرات تقاضای واکشی خود را تغییر دهید. issues.review.approve=این تغییرات را تایید شدند %s -issues.review.comment=دیدگاه issues.review.dismissed=بررسی %s %s را رد شده issues.review.dismissed_label=رها شده issues.review.left_comment=یک نظر ثبت کرد @@ -1256,6 +1254,7 @@ issues.review.hide_resolved=مخفی کردن حل شده ها issues.review.resolve_conversation=مکالمه را بعنوان حل شده علامت گذاری کردن issues.review.un_resolve_conversation=مکالمه را بعنوان حل نشده علامت گذاری کردن issues.review.resolved_by=علامت گذاری این مکالمه بعنوان حل شده +issues.review.commented=دیدگاه issues.assignee.error=به دلیل خطای غیرمنتظره همه تکالیف اضافه نشد. issues.reference_issue.body=Body issues.content_history.deleted=حذف شده diff --git a/options/locale/locale_fi-FI.ini b/options/locale/locale_fi-FI.ini index 1f8143ad84..200e104f46 100644 --- a/options/locale/locale_fi-FI.ini +++ b/options/locale/locale_fi-FI.ini @@ -133,8 +133,6 @@ filter.private=Yksityinen [error] occurred=Virhe tapahtui -missing_csrf=Virheellinen pyyntö: CSRF-tunnusta ei ole olemassa -invalid_csrf=Virheellinen pyyntö: Virheellinen CSRF-tunniste not_found=Kohdetta ei löytynyt. network_error=Verkkovirhe @@ -955,6 +953,7 @@ issues.review.left_comment=jätti kommentin issues.review.pending=Odottaa issues.review.show_resolved=Näytä ratkaisu issues.review.hide_resolved=Piilota ratkaisu +issues.review.commented=Kommentoi issues.reference_issue.body=Kuvaus issues.content_history.deleted=poistettu issues.content_history.edited=muokattu diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini index 8491e9b607..a61a7f7ebc 100644 --- a/options/locale/locale_fr-FR.ini +++ b/options/locale/locale_fr-FR.ini @@ -217,8 +217,6 @@ string.desc=Z - A [error] occurred=Une erreur s’est produite -missing_csrf=Requête incorrecte: aucun jeton CSRF présent -invalid_csrf=Requête incorrecte : jeton CSRF invalide not_found=La cible n'a pu être trouvée. network_error=Erreur réseau @@ -1704,7 +1702,6 @@ issues.dependency.add_error_dep_not_same_repo=Les deux tickets doivent être dan issues.review.self.approval=Vous ne pouvez approuver vos propres demandes d'ajout. issues.review.self.rejection=Vous ne pouvez demander de changements sur vos propres demandes de changement. issues.review.approve=a approuvé ces modifications %s. -issues.review.comment=Commenter issues.review.dismissed=a révoqué l’évaluation de %s %s. issues.review.dismissed_label=Révoquée issues.review.left_comment=laisser un commentaire @@ -1729,6 +1726,7 @@ issues.review.hide_resolved=Réduire issues.review.resolve_conversation=Clore la conversation issues.review.un_resolve_conversation=Rouvrir la conversation issues.review.resolved_by=a marqué cette conversation comme résolue. +issues.review.commented=Commenter issues.assignee.error=Tous les assignés n'ont pas été ajoutés en raison d'une erreur inattendue. issues.reference_issue.body=Corps issues.content_history.deleted=a supprimé diff --git a/options/locale/locale_hu-HU.ini b/options/locale/locale_hu-HU.ini index 6f057dc4c8..28605d48cf 100644 --- a/options/locale/locale_hu-HU.ini +++ b/options/locale/locale_hu-HU.ini @@ -901,13 +901,13 @@ issues.dependency.add_error_dep_issue_not_exist=Függő hibajegy nem létezik. issues.dependency.add_error_dep_not_exist=A függőség nem létezik. issues.dependency.add_error_dep_exists=A függőség már létezik. issues.dependency.add_error_dep_not_same_repo=Mindkét hibajegynek ugyanabban a tárolóban kell lennie. -issues.review.comment=Hozzászólás issues.review.reject=%s változtatások kérése issues.review.pending=Függőben issues.review.review=Értékelés issues.review.reviewers=Véleményezők issues.review.show_outdated=Elavultak mutatása issues.review.hide_outdated=Elavultak elrejtése +issues.review.commented=Hozzászólás issues.assignee.error=Nem minden megbízott lett hozzáadva egy nem várt hiba miatt. diff --git a/options/locale/locale_is-IS.ini b/options/locale/locale_is-IS.ini index 01219b16c4..656b5e1c56 100644 --- a/options/locale/locale_is-IS.ini +++ b/options/locale/locale_is-IS.ini @@ -129,8 +129,6 @@ filter.public=Opinbert [error] occurred=Villa kom upp -missing_csrf=Slæm beiðni: enginn CSRF lykill -invalid_csrf=Slæm beiðni: ógildur CSRF lykill not_found=Markmiðið fannst ekki. network_error=Netkerfisvilla @@ -850,13 +848,13 @@ issues.dependency.remove_header=Fjarlægja Kröfu issues.dependency.add_error_dep_not_exist=Krafa er ekki til. issues.dependency.add_error_dep_exists=Krafa er nú þegar til. issues.review.approve=samþykkti þessar breytingar %s -issues.review.comment=Senda Ummæli issues.review.dismissed_label=Hunsað issues.review.left_comment=gerði ummæli issues.review.pending=Í bið issues.review.outdated=Úrelt issues.review.show_outdated=Sýna úrelt issues.review.hide_outdated=Fela úreld +issues.review.commented=Senda Ummæli issues.reference_issue.body=Meginmál issues.content_history.deleted=eytt issues.content_history.edited=breytt diff --git a/options/locale/locale_it-IT.ini b/options/locale/locale_it-IT.ini index 1f7fe7939c..3ddd2bbddf 100644 --- a/options/locale/locale_it-IT.ini +++ b/options/locale/locale_it-IT.ini @@ -135,8 +135,6 @@ filter.private=Privati [error] occurred=Si è verificato un errore -missing_csrf=Richiesta errata: nessun token CSRF presente -invalid_csrf=Richiesta errata: token CSRF non valido not_found=Il bersaglio non è stato trovato. network_error=Errore di rete @@ -1331,7 +1329,6 @@ issues.dependency.add_error_dep_not_same_repo=Entrambi i problemi devono essere issues.review.self.approval=Non puoi approvare la tua pull request. issues.review.self.rejection=Non puoi richiedere modifiche sulla tua pull request. issues.review.approve=hanno approvato queste modifiche %s -issues.review.comment=Commentare issues.review.dismissed=recensione %s di %s respinta issues.review.dismissed_label=Respinta issues.review.left_comment=lascia un commento @@ -1352,6 +1349,7 @@ issues.review.hide_resolved=Nascondi risolte issues.review.resolve_conversation=Risolvi la conversazione issues.review.un_resolve_conversation=Segnala la conversazione come non risolta issues.review.resolved_by=ha contrassegnato questa conversazione come risolta +issues.review.commented=Commentare issues.assignee.error=Non tutte le assegnazioni sono state aggiunte a causa di un errore imprevisto. issues.reference_issue.body=Corpo issues.content_history.deleted=eliminato diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini index 92d040541a..3edf68e943 100644 --- a/options/locale/locale_ja-JP.ini +++ b/options/locale/locale_ja-JP.ini @@ -218,8 +218,6 @@ string.desc=Z - A [error] occurred=エラーが発生しました -missing_csrf=不正なリクエスト: CSRFトークンがありません -invalid_csrf=不正なリクエスト: CSRFトークンが無効です not_found=ターゲットが見つかりませんでした。 network_error=ネットワークエラー @@ -1714,7 +1712,6 @@ issues.dependency.add_error_dep_not_same_repo=両方とも同じリポジトリ issues.review.self.approval=自分のプルリクエストを承認することはできません。 issues.review.self.rejection=自分のプルリクエストに対して修正を要求することはできません。 issues.review.approve=が変更を承認 %s -issues.review.comment=コメント issues.review.dismissed=が %s のレビューを棄却 %s issues.review.dismissed_label=棄却 issues.review.left_comment=がコメント @@ -1739,6 +1736,7 @@ issues.review.hide_resolved=解決済みを隠す issues.review.resolve_conversation=解決済みにする issues.review.un_resolve_conversation=未解決にする issues.review.resolved_by=がこの会話を解決済みにしました +issues.review.commented=コメント issues.assignee.error=予期しないエラーにより、一部の担当者を追加できませんでした。 issues.reference_issue.body=内容 issues.content_history.deleted=削除しました diff --git a/options/locale/locale_ko-KR.ini b/options/locale/locale_ko-KR.ini index ef11284b08..dc122ec4c3 100644 --- a/options/locale/locale_ko-KR.ini +++ b/options/locale/locale_ko-KR.ini @@ -818,12 +818,12 @@ issues.dependency.add_error_dep_not_same_repo=두 이슈는 같은 레포지토 issues.review.self.approval=자신의 풀 리퀘스트를 승인할 수 없습니다. issues.review.self.rejection=자신의 풀 리퀘스트에 대한 변경을 요청할 수 없습니다. issues.review.approve="이 변경사항을 승인하였습니다. %s" -issues.review.comment=댓글 issues.review.pending=보류 issues.review.review=검토 issues.review.reviewers=리뷰어 issues.review.show_outdated=오래된 내역 보기 issues.review.hide_outdated=오래된 내역 숨기기 +issues.review.commented=댓글 pulls.new=새 풀 리퀘스트 diff --git a/options/locale/locale_lv-LV.ini b/options/locale/locale_lv-LV.ini index ee7ab1192c..d8ab21e170 100644 --- a/options/locale/locale_lv-LV.ini +++ b/options/locale/locale_lv-LV.ini @@ -187,8 +187,6 @@ string.desc=Z - A [error] occurred=Radusies kļūda -missing_csrf=Kļūdains pieprasījums: netika iesūtīta drošības pilnvara -invalid_csrf=Kļūdains pieprasījums: iesūtīta kļūdaina drošības pilnvara not_found=Pieprasītie dati netika atrasti. network_error=Tīkla kļūda @@ -1609,7 +1607,6 @@ issues.dependency.add_error_dep_not_same_repo=Abām problēmām ir jābūt no vi issues.review.self.approval=Nevar apstiprināt savu izmaiņu pieprasījumi. issues.review.self.rejection=Nevar pieprasīt izmaiņas savam izmaiņu pieprasījumam. issues.review.approve=apstiprināja izmaiņas %s -issues.review.comment=Komentēt issues.review.dismissed=atmeta %s recenziju %s issues.review.dismissed_label=Atmesta issues.review.left_comment=atstāja komentāru @@ -1634,6 +1631,7 @@ issues.review.hide_resolved=Paslēpt atrisināto issues.review.resolve_conversation=Atrisināt sarunu issues.review.un_resolve_conversation=Atcelt sarunas atrisinājumu issues.review.resolved_by=atzīmēja sarunu kā atrisinātu +issues.review.commented=Komentēt issues.assignee.error=Ne visi atbildīgie tika pievienoti, jo radās neparedzēta kļūda. issues.reference_issue.body=Saturs issues.content_history.deleted=dzēsts diff --git a/options/locale/locale_nl-NL.ini b/options/locale/locale_nl-NL.ini index 5de274da2b..5ed3417b4a 100644 --- a/options/locale/locale_nl-NL.ini +++ b/options/locale/locale_nl-NL.ini @@ -134,8 +134,6 @@ filter.private=Prive [error] occurred=Er is een fout opgetreden -missing_csrf=Foutief verzoek: geen CSRF-token aanwezig -invalid_csrf=Verkeerd verzoek: ongeldig CSRF-token not_found=Het doel kon niet worden gevonden. network_error=Netwerk fout @@ -1328,7 +1326,6 @@ issues.dependency.add_error_dep_not_same_repo=Beide kwesties moeten in dezelfde issues.review.self.approval=Je kan je eigen pull-aanvraag niet goedkeuren. issues.review.self.rejection=Je kan geen wijzigingen aanvragen op je eigen pull-aanvraag. issues.review.approve=heeft deze veranderingen %s goedgekeurd -issues.review.comment=Opmerking issues.review.dismissed=%s's beoordeling afgewezen %s issues.review.dismissed_label=Afgewezen issues.review.left_comment=heeft een reactie achtergelaten @@ -1349,6 +1346,7 @@ issues.review.hide_resolved=Verbergen afgehandeld issues.review.resolve_conversation=Gesprek oplossen issues.review.un_resolve_conversation=Gesprek niet oplossen issues.review.resolved_by=markeerde dit gesprek als opgelost +issues.review.commented=Opmerking issues.assignee.error=Niet alle aangewezen personen zijn toegevoegd vanwege een onverwachte fout. issues.reference_issue.body=Inhoud issues.content_history.deleted=verwijderd diff --git a/options/locale/locale_pl-PL.ini b/options/locale/locale_pl-PL.ini index 88e54fc3be..911a4b9e0e 100644 --- a/options/locale/locale_pl-PL.ini +++ b/options/locale/locale_pl-PL.ini @@ -131,8 +131,6 @@ filter.private=Prywatne [error] occurred=Wystąpił błąd -missing_csrf=Błędne żądanie: brak tokenu CSRF -invalid_csrf=Błędne żądanie: nieprawidłowy token CSRF not_found=Nie można odnaleźć celu. network_error=Błąd sieci @@ -1220,7 +1218,6 @@ issues.dependency.add_error_dep_not_same_repo=Oba zgłoszenia muszą być w tym issues.review.self.approval=Nie możesz zatwierdzić swojego własnego Pull Requesta. issues.review.self.rejection=Nie możesz zażądać zmian w swoim własnym Pull Requeście. issues.review.approve=zatwierdza te zmiany %s -issues.review.comment=Skomentuj issues.review.dismissed_label=Odrzucony issues.review.left_comment=zostawił komentarz issues.review.content.empty=Musisz pozostawić komentarz o pożądanej zmianie/zmianach. @@ -1240,6 +1237,7 @@ issues.review.hide_resolved=Ukryj rozwiązane issues.review.resolve_conversation=Rozwiąż dyskusję issues.review.un_resolve_conversation=Oznacz dyskusję jako nierozstrzygniętą issues.review.resolved_by=oznaczył(-a) tę rozmowę jako rozwiązaną +issues.review.commented=Skomentuj issues.assignee.error=Nie udało się dodać wszystkich wybranych osób do przypisanych przez nieoczekiwany błąd. issues.reference_issue.body=Treść issues.content_history.edited=edytowano diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini index e172aa0a01..6f4f710d7f 100644 --- a/options/locale/locale_pt-BR.ini +++ b/options/locale/locale_pt-BR.ini @@ -184,8 +184,6 @@ string.desc=Z - A [error] occurred=Ocorreu um erro -missing_csrf=Pedido inválido: não tem token CSRF presente -invalid_csrf=Requisição Inválida: token CSRF inválido not_found=Não foi possível encontrar o destino. network_error=Erro de rede @@ -1599,7 +1597,6 @@ issues.dependency.add_error_dep_not_same_repo=Ambas as issues devem estar no mes issues.review.self.approval=Você não pode aprovar o seu próprio pull request. issues.review.self.rejection=Você não pode solicitar alterações em seu próprio pull request. issues.review.approve=aprovou estas alterações %s -issues.review.comment=Comentar issues.review.dismissed=rejeitou a revisão de %s %s issues.review.dismissed_label=Rejeitada issues.review.left_comment=deixou um comentário @@ -1624,6 +1621,7 @@ issues.review.hide_resolved=Ocultar resolvidas issues.review.resolve_conversation=Resolver conversa issues.review.un_resolve_conversation=Conversa não resolvida issues.review.resolved_by=marcou esta conversa como resolvida +issues.review.commented=Comentar issues.assignee.error=Nem todos os responsáveis foram adicionados devido a um erro inesperado. issues.reference_issue.body=Conteúdo issues.content_history.deleted=excluído diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini index d33cddeaf7..4ddd54e3bf 100644 --- a/options/locale/locale_pt-PT.ini +++ b/options/locale/locale_pt-PT.ini @@ -178,6 +178,8 @@ code_search_by_git_grep=Os resultados da pesquisa no código-fonte neste momento package_kind=Pesquisar pacotes... project_kind=Pesquisar planeamentos... branch_kind=Pesquisar ramos... +tag_kind=Pesquisar etiquetas... +tag_tooltip=Pesquisar etiquetas correspondentes. Use '%' para corresponder a qualquer sequência de números. commit_kind=Pesquisar cometimentos... runner_kind=Pesquisar executores... no_results=Não foram encontrados resultados correspondentes. @@ -220,8 +222,6 @@ string.desc=Z - A [error] occurred=Ocorreu um erro report_message=Se acredita tratar-se de um erro do Gitea, procure questões relacionadas no GitHub ou abra uma nova questão, se necessário. -missing_csrf=Pedido inválido: não há código CSRF -invalid_csrf=Pedido inválido: código CSRF inválido not_found=Não foi possível encontrar o destino. network_error=Erro de rede @@ -1731,7 +1731,6 @@ issues.dependency.add_error_dep_not_same_repo=Ambas as questões têm que estar issues.review.self.approval=Não pode aprovar o seu próprio pedido de integração. issues.review.self.rejection=Não pode solicitar modificações sobre o seu próprio pedido de integração. issues.review.approve=aprovou estas modificações %s -issues.review.comment=Comentar issues.review.dismissed=descartou a revisão de %s %s issues.review.dismissed_label=Descartada issues.review.left_comment=deixou um comentário @@ -1756,6 +1755,7 @@ issues.review.hide_resolved=Ocultar os concluídos issues.review.resolve_conversation=Passar diálogo ao estado de resolvido issues.review.un_resolve_conversation=Passar diálogo ao estado de não resolvido issues.review.resolved_by=marcou este diálogo como estando concluído +issues.review.commented=Comentar issues.review.official=Aprovada issues.review.requested=Revisão pendente issues.review.rejected=Modificações solicitadas diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini index 20e82273cb..03c6e2d073 100644 --- a/options/locale/locale_ru-RU.ini +++ b/options/locale/locale_ru-RU.ini @@ -182,8 +182,6 @@ string.desc=Я - А [error] occurred=Произошла ошибка -missing_csrf=Некорректный запрос: отсутствует токен CSRF -invalid_csrf=Некорректный запрос: неверный токен CSRF not_found=Цель не найдена. network_error=Ошибка сети @@ -1578,7 +1576,6 @@ issues.dependency.add_error_dep_not_same_repo=Обе задачи должны issues.review.self.approval=Вы не можете одобрить собственный запрос на слияние. issues.review.self.rejection=Невозможно запрашивать изменения своего запроса на слияние. issues.review.approve=одобрил(а) эти изменения %s -issues.review.comment=Комментировать issues.review.dismissed=отклонен отзыв %s %s issues.review.dismissed_label=Отклонено issues.review.left_comment=оставил комментарий @@ -1602,6 +1599,7 @@ issues.review.hide_resolved=Скрыть разрешенные issues.review.resolve_conversation=Покинуть диалог issues.review.un_resolve_conversation=Незавершённый разговор issues.review.resolved_by=пометить этот разговор как разрешённый +issues.review.commented=Комментировать issues.assignee.error=Не все назначения были добавлены из-за непредвиденной ошибки. issues.reference_issue.body=Тело issues.content_history.deleted=удалено diff --git a/options/locale/locale_si-LK.ini b/options/locale/locale_si-LK.ini index 077a4014e8..c9521d80f8 100644 --- a/options/locale/locale_si-LK.ini +++ b/options/locale/locale_si-LK.ini @@ -118,7 +118,6 @@ filter.private=පෞද්ගලික [filter] [error] -missing_csrf=නරක ඉල්ලීම: CSRF ටෝකන් නොමැත [startpage] app_desc=වේදනාකාරී, ස්වයං-සත්කාරක Git සේවාවක් @@ -1200,7 +1199,6 @@ issues.dependency.add_error_dep_not_same_repo=මෙම ගැටළු දෙ issues.review.self.approval=ඔබ ඔබේ ම අදින්න ඉල්ලීම අනුමත කළ නොහැක. issues.review.self.rejection=ඔබ ඔබේ ම අදින්න ඉල්ලීම මත වෙනස්කම් ඉල්ලා සිටිය නොහැක. issues.review.approve=මෙම වෙනස්කම් අනුමත %s -issues.review.comment=අදහස issues.review.dismissed=%sහි සමාලෝචනය %sප්රතික්ෂේප කරන ලද issues.review.dismissed_label=බැහැර issues.review.left_comment=අදහසක් හැරගියා @@ -1221,6 +1219,7 @@ issues.review.hide_resolved=විසඳා සඟවන්න issues.review.resolve_conversation=සංවාදය විසඳන්න issues.review.un_resolve_conversation=නොවිසඳිය හැකි සංවාදය issues.review.resolved_by=මෙම සංවාදය විසඳා ඇති පරිදි සලකුණු කර ඇත +issues.review.commented=අදහස issues.assignee.error=අනපේක්ෂිත දෝෂයක් හේතුවෙන් සියලුම ඇසිග්නස් එකතු නොකළේය. issues.reference_issue.body=ශරීරය issues.content_history.deleted=මකා දැමූ diff --git a/options/locale/locale_sk-SK.ini b/options/locale/locale_sk-SK.ini index d27dfa83bc..484fa320fa 100644 --- a/options/locale/locale_sk-SK.ini +++ b/options/locale/locale_sk-SK.ini @@ -181,8 +181,6 @@ string.desc=Z - A [error] occurred=Vyskytla sa chyba -missing_csrf=Nesprávna žiadosť: neprítomný CSFR token -invalid_csrf=Nesprávna žiadosť: nesprávny CSFR token not_found=Nebolo možné nájsť cieľ. network_error=Chyba siete diff --git a/options/locale/locale_sv-SE.ini b/options/locale/locale_sv-SE.ini index 34c70ab422..459b704506 100644 --- a/options/locale/locale_sv-SE.ini +++ b/options/locale/locale_sv-SE.ini @@ -1039,7 +1039,6 @@ issues.dependency.add_error_dep_not_same_repo=Båda ärendena måste vara i samm issues.review.self.approval=Du kan inte godkänna din egen pull-begäran. issues.review.self.rejection=Du kan inte begära ändringar för din egna pull-förfrågan. issues.review.approve=godkände dessa ändringar %s -issues.review.comment=Kommentar issues.review.left_comment=lämnade en kommentar issues.review.content.empty=Du måste skriva en kommentar som anger de önskade ändringarna. issues.review.reject=begärda ändringar %s @@ -1056,6 +1055,7 @@ issues.review.show_resolved=Visa löst issues.review.hide_resolved=Dölj löst issues.review.resolve_conversation=Lös konversation issues.review.resolved_by=markerade denna konversation som löst +issues.review.commented=Kommentar issues.assignee.error=Inte alla tilldelade har lagts till på grund av ett oväntat fel. issues.content_history.options=Alternativ diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini index d388e79c27..7ef6c1d35e 100644 --- a/options/locale/locale_tr-TR.ini +++ b/options/locale/locale_tr-TR.ini @@ -218,8 +218,6 @@ string.desc=Z - A [error] occurred=Bir hata oluştu -missing_csrf=Hatalı İstek: CSRF anahtarı yok -invalid_csrf=Hatalı İstek: geçersiz CSRF erişim anahtarı not_found=Hedef bulunamadı. network_error=Ağ hatası @@ -1704,7 +1702,6 @@ issues.dependency.add_error_dep_not_same_repo=Her iki konu da aynı depoda olmal issues.review.self.approval=Kendi değişiklik isteğinizi onaylayamazsınız. issues.review.self.rejection=Kendi değişiklik isteğinizde değişiklik isteyemezsiniz. issues.review.approve=%s bu değişiklikleri onayladı -issues.review.comment=Yorum Yap issues.review.dismissed=%s incelemesini %s reddetti issues.review.dismissed_label=Reddedildi issues.review.left_comment=bir yorum yaptı @@ -1729,6 +1726,7 @@ issues.review.hide_resolved=Çözülenleri gizle issues.review.resolve_conversation=Konuşmayı çöz issues.review.un_resolve_conversation=Konuşmayı çözme issues.review.resolved_by=bu konuşmayı çözümlenmiş olarak işaretledi +issues.review.commented=Yorum Yap issues.assignee.error=Beklenmeyen bir hata nedeniyle tüm atananlar eklenmedi. issues.reference_issue.body=Gövde issues.content_history.deleted=silindi diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini index b8b7a29037..129ab1b7f5 100644 --- a/options/locale/locale_uk-UA.ini +++ b/options/locale/locale_uk-UA.ini @@ -120,7 +120,6 @@ filter.private=Приватний [error] occurred=Сталася помилка -missing_csrf=Некоректний запит: токен CSRF не задано network_error=Помилка мережі [startpage] @@ -1245,7 +1244,6 @@ issues.dependency.add_error_dep_not_same_repo=Обидві задачі пови issues.review.self.approval=Ви не можете схвалити власний пулл-реквест. issues.review.self.rejection=Ви не можете надіслати запит на зміну на власний пулл-реквест. issues.review.approve=зміни затверджено %s -issues.review.comment=Коментар issues.review.dismissed=відхилено відгук %s %s issues.review.dismissed_label=Відхилено issues.review.left_comment=додав коментар @@ -1266,6 +1264,7 @@ issues.review.hide_resolved=Приховати вирішене issues.review.resolve_conversation=Завершити обговорення issues.review.un_resolve_conversation=Поновити обговорення issues.review.resolved_by=позначив обговорення завершеним +issues.review.commented=Коментар issues.assignee.error=Додано не всіх виконавців через непередбачену помилку. issues.reference_issue.body=Тіло issues.content_history.deleted=видалено diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index 8fe44e6009..2827a8cd35 100644 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -217,8 +217,6 @@ string.desc=Z - A [error] occurred=发生了一个错误 -missing_csrf=错误的请求:没有 CSRF 令牌 -invalid_csrf=错误的请求:无效的 CSRF 令牌 not_found=找不到目标。 network_error=网络错误 @@ -1692,7 +1690,6 @@ issues.dependency.add_error_dep_not_same_repo=这两个工单必须在同一仓 issues.review.self.approval=您不能批准您自己的合并请求。 issues.review.self.rejection=您不能请求对您自己的合并请求进行更改。 issues.review.approve=于 %s 批准此合并请求 -issues.review.comment=评论 issues.review.dismissed=于 %[2]s 取消了 %[1]s 的评审 issues.review.dismissed_label=已取消 issues.review.left_comment=留下了一条评论 @@ -1717,6 +1714,7 @@ issues.review.hide_resolved=隐藏已解决的 issues.review.resolve_conversation=已解决问题 issues.review.un_resolve_conversation=未解决问题 issues.review.resolved_by=标记问题为已解决 +issues.review.commented=评论 issues.assignee.error=因为未知原因,并非所有的指派都成功。 issues.reference_issue.body=内容 issues.content_history.deleted=删除于 diff --git a/options/locale/locale_zh-TW.ini b/options/locale/locale_zh-TW.ini index 2eb03fa84c..9406419e6b 100644 --- a/options/locale/locale_zh-TW.ini +++ b/options/locale/locale_zh-TW.ini @@ -167,8 +167,6 @@ string.desc=Z - A [error] occurred=發生錯誤 -missing_csrf=錯誤的請求:未提供 CSRF token -invalid_csrf=錯誤的請求:無效的 CSRF token not_found=找不到目標。 network_error=網路錯誤 @@ -1467,7 +1465,6 @@ issues.dependency.add_error_dep_not_same_repo=這兩個問題必須在同一個 issues.review.self.approval=您不能核可自己的合併請求。 issues.review.self.rejection=您不能對自己的合併請求提出請求變更。 issues.review.approve=核可了這些變更 %s -issues.review.comment=留言 issues.review.dismissed=取消 %s 的審核 %s issues.review.dismissed_label=已取消 issues.review.left_comment=留下了回應 @@ -1488,6 +1485,7 @@ issues.review.hide_resolved=隱藏已解決 issues.review.resolve_conversation=解決對話 issues.review.un_resolve_conversation=取消解決對話 issues.review.resolved_by=標記了此對話為已解決 +issues.review.commented=留言 issues.assignee.error=因為未預期的錯誤,未能成功加入所有負責人。 issues.reference_issue.body=內容 issues.content_history.deleted=刪除 From 55d5a74bb3a992dd56152e2f5266c7fd883c1948 Mon Sep 17 00:00:00 2001 From: GiteaBot Date: Fri, 20 Sep 2024 00:29:58 +0000 Subject: [PATCH 9/9] [skip ci] Updated translations via Crowdin --- options/locale/locale_pt-PT.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini index 4ddd54e3bf..199c4add08 100644 --- a/options/locale/locale_pt-PT.ini +++ b/options/locale/locale_pt-PT.ini @@ -1731,6 +1731,7 @@ issues.dependency.add_error_dep_not_same_repo=Ambas as questões têm que estar issues.review.self.approval=Não pode aprovar o seu próprio pedido de integração. issues.review.self.rejection=Não pode solicitar modificações sobre o seu próprio pedido de integração. issues.review.approve=aprovou estas modificações %s +issues.review.comment=reviu %s issues.review.dismissed=descartou a revisão de %s %s issues.review.dismissed_label=Descartada issues.review.left_comment=deixou um comentário