diff --git a/.deadcode-out b/.deadcode-out index a44599b6f1..70a74128eb 100644 --- a/.deadcode-out +++ b/.deadcode-out @@ -22,11 +22,6 @@ code.gitea.io/gitea/models/actions ScheduleList.LoadTriggerUser ScheduleList.LoadRepos -code.gitea.io/gitea/models/asymkey - ErrGPGKeyAccessDenied.Error - ErrGPGKeyAccessDenied.Unwrap - HasDeployKey - code.gitea.io/gitea/models/auth GetSourceByName WebAuthnCredentials @@ -54,9 +49,7 @@ code.gitea.io/gitea/models/git code.gitea.io/gitea/models/issues IsErrUnknownDependencyType - ErrNewIssueInsert.Error IsErrIssueWasClosed - ChangeMilestoneStatus code.gitea.io/gitea/models/organization GetTeamNamesByID @@ -81,16 +74,12 @@ code.gitea.io/gitea/models/repo WatchRepoMode code.gitea.io/gitea/models/user - ErrUserInactive.Error - ErrUserInactive.Unwrap IsErrExternalLoginUserAlreadyExist IsErrExternalLoginUserNotExist NewFederatedUser IsErrUserSettingIsNotExist GetUserAllSettings DeleteUserSetting - GetUserEmailsByNames - GetUserNamesByIDs code.gitea.io/gitea/modules/activitypub NewContext diff --git a/.forgejo/workflows-composite/build-backend/action.yaml b/.forgejo/workflows-composite/build-backend/action.yaml index ada372b834..68a99ffaf9 100644 --- a/.forgejo/workflows-composite/build-backend/action.yaml +++ b/.forgejo/workflows-composite/build-backend/action.yaml @@ -3,7 +3,7 @@ runs: steps: - run: | su forgejo -c 'make deps-backend' - - uses: actions/cache@v4 + - uses: https://data.forgejo.org/actions/cache@v4 id: cache-backend with: path: ${{github.workspace}}/gitea diff --git a/.forgejo/workflows-composite/setup-cache-go/action.yaml b/.forgejo/workflows-composite/setup-cache-go/action.yaml index 1b1d37bb6b..cc21485d14 100644 --- a/.forgejo/workflows-composite/setup-cache-go/action.yaml +++ b/.forgejo/workflows-composite/setup-cache-go/action.yaml @@ -49,7 +49,7 @@ runs: - name: "Restore Go dependencies from cache or mark for later caching" id: cache-deps - uses: actions/cache@v4 + uses: https://data.forgejo.org/actions/cache@v4 with: key: setup-cache-go-deps-${{ runner.os }}-${{ inputs.username }}-${{ steps.go-version.outputs.go_version }}-${{ hashFiles('go.sum', 'go.mod') }} restore-keys: | diff --git a/.forgejo/workflows/build-release-integration.yml b/.forgejo/workflows/build-release-integration.yml index 6410915644..1af6d567dd 100644 --- a/.forgejo/workflows/build-release-integration.yml +++ b/.forgejo/workflows/build-release-integration.yml @@ -25,7 +25,7 @@ jobs: if: vars.ROLE == 'forgejo-coding' runs-on: lxc-bookworm steps: - - uses: actions/checkout@v4 + - uses: https://data.forgejo.org/actions/checkout@v4 - id: forgejo uses: https://data.forgejo.org/actions/setup-forgejo@v2.0.4 diff --git a/.forgejo/workflows/build-release.yml b/.forgejo/workflows/build-release.yml index 9d88cb43dd..0d7f94c5a6 100644 --- a/.forgejo/workflows/build-release.yml +++ b/.forgejo/workflows/build-release.yml @@ -33,7 +33,7 @@ jobs: # root is used for testing, allow it if: vars.ROLE == 'forgejo-integration' || github.repository_owner == 'root' steps: - - uses: actions/checkout@v4 + - uses: https://data.forgejo.org/actions/checkout@v4 with: fetch-depth: 0 diff --git a/.forgejo/workflows/cascade-setup-end-to-end.yml b/.forgejo/workflows/cascade-setup-end-to-end.yml index 710cd27ba4..bcc7821f4f 100644 --- a/.forgejo/workflows/cascade-setup-end-to-end.yml +++ b/.forgejo/workflows/cascade-setup-end-to-end.yml @@ -37,11 +37,11 @@ jobs: container: image: data.forgejo.org/oci/node:20-bookworm steps: - - uses: actions/checkout@v4 + - uses: https://data.forgejo.org/actions/checkout@v4 with: fetch-depth: '0' show-progress: 'false' - - uses: https://code.forgejo.org/actions/cascading-pr@v2.2.0 + - uses: https://data.forgejo.org/actions/cascading-pr@v2.2.0 with: origin-url: ${{ env.GITHUB_SERVER_URL }} origin-repo: ${{ github.repository }} diff --git a/.forgejo/workflows/publish-release.yml b/.forgejo/workflows/publish-release.yml index 0e75912a3a..b44d670fc4 100644 --- a/.forgejo/workflows/publish-release.yml +++ b/.forgejo/workflows/publish-release.yml @@ -39,7 +39,7 @@ jobs: runs-on: lxc-bookworm if: vars.DOER != '' && vars.FORGEJO != '' && vars.TO_OWNER != '' && vars.FROM_OWNER != '' && secrets.TOKEN != '' steps: - - uses: actions/checkout@v4 + - uses: https://data.forgejo.org/actions/checkout@v4 - name: copy & sign uses: https://data.forgejo.org/forgejo/forgejo-build-publish/publish@v5.3.1 diff --git a/.forgejo/workflows/testing.yml b/.forgejo/workflows/testing.yml index eb3163d3ae..784bc45736 100644 --- a/.forgejo/workflows/testing.yml +++ b/.forgejo/workflows/testing.yml @@ -46,7 +46,7 @@ jobs: apt-get update -qq apt-get -q install -qq -y zstd - name: "Cache frontend build for playwright testing" - uses: actions/cache/save@v4 + uses: https://data.forgejo.org/actions/cache/save@v4 with: path: ${{github.workspace}}/public/assets key: frontend-build-${{ github.sha }} @@ -104,7 +104,7 @@ jobs: fetch-depth: 20 - uses: ./.forgejo/workflows-composite/setup-env - name: "Restore frontend build" - uses: actions/cache/restore@v4 + uses: https://data.forgejo.org/actions/cache/restore@v4 id: cache-frontend with: path: ${{github.workspace}}/public/assets diff --git a/Dockerfile b/Dockerfile index 3f7f3e7d1f..2df13c2326 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ -FROM --platform=$BUILDPLATFORM code.forgejo.org/oci/xx AS xx +FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/xx AS xx -FROM --platform=$BUILDPLATFORM code.forgejo.org/oci/golang:1.23-alpine3.21 as build-env +FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/golang:1.23-alpine3.21 as build-env ARG GOPROXY ENV GOPROXY=${GOPROXY:-direct} @@ -51,7 +51,7 @@ RUN chmod 755 /tmp/local/usr/bin/entrypoint \ /go/src/code.gitea.io/gitea/environment-to-ini RUN chmod 644 /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete -FROM code.forgejo.org/oci/alpine:3.21 +FROM data.forgejo.org/oci/alpine:3.21 ARG RELEASE_VERSION LABEL maintainer="contact@forgejo.org" \ org.opencontainers.image.authors="Forgejo" \ diff --git a/Dockerfile.rootless b/Dockerfile.rootless index 129bd6f1ba..8aec4d828f 100644 --- a/Dockerfile.rootless +++ b/Dockerfile.rootless @@ -1,6 +1,6 @@ -FROM --platform=$BUILDPLATFORM code.forgejo.org/oci/xx AS xx +FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/xx AS xx -FROM --platform=$BUILDPLATFORM code.forgejo.org/oci/golang:1.23-alpine3.21 as build-env +FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/golang:1.23-alpine3.21 as build-env ARG GOPROXY ENV GOPROXY=${GOPROXY:-direct} @@ -49,7 +49,7 @@ RUN chmod 755 /tmp/local/usr/local/bin/docker-entrypoint.sh \ /go/src/code.gitea.io/gitea/environment-to-ini RUN chmod 644 /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete -FROM code.forgejo.org/oci/alpine:3.21 +FROM data.forgejo.org/oci/alpine:3.21 LABEL maintainer="contact@forgejo.org" \ org.opencontainers.image.authors="Forgejo" \ org.opencontainers.image.url="https://forgejo.org" \ diff --git a/Makefile b/Makefile index 8ea7062f57..151e752545 100644 --- a/Makefile +++ b/Makefile @@ -37,7 +37,7 @@ endif XGO_VERSION := go-1.21.x AIR_PACKAGE ?= github.com/air-verse/air@v1 # renovate: datasource=go -EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3.1.1 # renovate: datasource=go +EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3.2.0 # renovate: datasource=go GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.7.0 # renovate: datasource=go GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.62.2 # renovate: datasource=go GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11 # renovate: datasource=go diff --git a/models/asymkey/error.go b/models/asymkey/error.go index 03bc82302f..e38ba121c6 100644 --- a/models/asymkey/error.go +++ b/models/asymkey/error.go @@ -192,28 +192,6 @@ func (err ErrGPGKeyIDAlreadyUsed) Unwrap() error { return util.ErrAlreadyExist } -// ErrGPGKeyAccessDenied represents a "GPGKeyAccessDenied" kind of Error. -type ErrGPGKeyAccessDenied struct { - UserID int64 - KeyID int64 -} - -// IsErrGPGKeyAccessDenied checks if an error is a ErrGPGKeyAccessDenied. -func IsErrGPGKeyAccessDenied(err error) bool { - _, ok := err.(ErrGPGKeyAccessDenied) - return ok -} - -// Error pretty-prints an error of type ErrGPGKeyAccessDenied. -func (err ErrGPGKeyAccessDenied) Error() string { - return fmt.Sprintf("user does not have access to the key [user_id: %d, key_id: %d]", - err.UserID, err.KeyID) -} - -func (err ErrGPGKeyAccessDenied) Unwrap() error { - return util.ErrPermissionDenied -} - // ErrKeyAccessDenied represents a "KeyAccessDenied" kind of error. type ErrKeyAccessDenied struct { UserID int64 diff --git a/models/asymkey/ssh_key_deploy.go b/models/asymkey/ssh_key_deploy.go index 923c5020ed..429635330d 100644 --- a/models/asymkey/ssh_key_deploy.go +++ b/models/asymkey/ssh_key_deploy.go @@ -105,14 +105,6 @@ func addDeployKey(ctx context.Context, keyID, repoID int64, name, fingerprint st return key, db.Insert(ctx, key) } -// HasDeployKey returns true if public key is a deploy key of given repository. -func HasDeployKey(ctx context.Context, keyID, repoID int64) bool { - has, _ := db.GetEngine(ctx). - Where("key_id = ? AND repo_id = ?", keyID, repoID). - Get(new(DeployKey)) - return has -} - // AddDeployKey add new deploy key to database and authorized_keys file. func AddDeployKey(ctx context.Context, repoID int64, name, content string, readOnly bool) (*DeployKey, error) { fingerprint, err := CalcFingerprint(content) diff --git a/models/issues/issue.go b/models/issues/issue.go index 1e969790d7..6bd91797e7 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -63,21 +63,6 @@ func (err ErrIssueIsClosed) Error() string { return fmt.Sprintf("issue is closed [id: %d, repo_id: %d, index: %d]", err.ID, err.RepoID, err.Index) } -// ErrNewIssueInsert is used when the INSERT statement in newIssue fails -type ErrNewIssueInsert struct { - OriginalError error -} - -// IsErrNewIssueInsert checks if an error is a ErrNewIssueInsert. -func IsErrNewIssueInsert(err error) bool { - _, ok := err.(ErrNewIssueInsert) - return ok -} - -func (err ErrNewIssueInsert) Error() string { - return err.OriginalError.Error() -} - // ErrIssueWasClosed is used when close a closed issue type ErrIssueWasClosed struct { ID int64 diff --git a/models/issues/issue_update.go b/models/issues/issue_update.go index 31c8bdc17b..775ebfadad 100644 --- a/models/issues/issue_update.go +++ b/models/issues/issue_update.go @@ -433,7 +433,7 @@ func NewIssue(ctx context.Context, repo *repo_model.Repository, issue *Issue, la LabelIDs: labelIDs, Attachments: uuids, }); err != nil { - if repo_model.IsErrUserDoesNotHaveAccessToRepo(err) || IsErrNewIssueInsert(err) { + if repo_model.IsErrUserDoesNotHaveAccessToRepo(err) { return err } return fmt.Errorf("newIssue: %w", err) diff --git a/models/issues/milestone.go b/models/issues/milestone.go index 4b3cb0e858..03fab8b3ee 100644 --- a/models/issues/milestone.go +++ b/models/issues/milestone.go @@ -251,21 +251,6 @@ func ChangeMilestoneStatusByRepoIDAndID(ctx context.Context, repoID, milestoneID return committer.Commit() } -// ChangeMilestoneStatus changes the milestone open/closed status. -func ChangeMilestoneStatus(ctx context.Context, m *Milestone, isClosed bool) (err error) { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() - - if err := changeMilestoneStatus(ctx, m, isClosed); err != nil { - return err - } - - return committer.Commit() -} - func changeMilestoneStatus(ctx context.Context, m *Milestone, isClosed bool) error { m.IsClosed = isClosed if isClosed { diff --git a/models/issues/milestone_test.go b/models/issues/milestone_test.go index 314cba308c..c83450e9db 100644 --- a/models/issues/milestone_test.go +++ b/models/issues/milestone_test.go @@ -298,17 +298,16 @@ func TestNewMilestone(t *testing.T) { unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: milestone.RepoID}, &issues_model.Milestone{}) } -func TestChangeMilestoneStatus(t *testing.T) { +func TestChangeMilestoneStatusByRepoIDAndID(t *testing.T) { require.NoError(t, unittest.PrepareTestDatabase()) - milestone := unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1}) - require.NoError(t, issues_model.ChangeMilestoneStatus(db.DefaultContext, milestone, true)) - unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1}, "is_closed=1") - unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: milestone.RepoID}, &issues_model.Milestone{}) + require.NoError(t, issues_model.ChangeMilestoneStatusByRepoIDAndID(db.DefaultContext, 1, 1, true)) + unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1, IsClosed: true}) + unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: 1}, &issues_model.Milestone{}) - require.NoError(t, issues_model.ChangeMilestoneStatus(db.DefaultContext, milestone, false)) + require.NoError(t, issues_model.ChangeMilestoneStatusByRepoIDAndID(db.DefaultContext, 1, 1, false)) unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1}, "is_closed=0") - unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: milestone.RepoID}, &issues_model.Milestone{}) + unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: 1}, &issues_model.Milestone{}) } func TestDeleteMilestoneByRepoID(t *testing.T) { diff --git a/models/issues/pull.go b/models/issues/pull.go index 13eafccdc7..708dff9b8e 100644 --- a/models/issues/pull.go +++ b/models/issues/pull.go @@ -575,7 +575,7 @@ func NewPullRequest(ctx context.Context, repo *repo_model.Repository, issue *Iss Attachments: uuids, IsPull: true, }); err != nil { - if repo_model.IsErrUserDoesNotHaveAccessToRepo(err) || IsErrNewIssueInsert(err) { + if repo_model.IsErrUserDoesNotHaveAccessToRepo(err) { return err } return fmt.Errorf("newIssue: %w", err) diff --git a/models/user/error.go b/models/user/error.go index cbf19998d1..5a956a2afe 100644 --- a/models/user/error.go +++ b/models/user/error.go @@ -71,27 +71,6 @@ func (err ErrUserProhibitLogin) Unwrap() error { return util.ErrPermissionDenied } -// ErrUserInactive represents a "ErrUserInactive" kind of error. -type ErrUserInactive struct { - UID int64 - Name string -} - -// IsErrUserInactive checks if an error is a ErrUserInactive -func IsErrUserInactive(err error) bool { - _, ok := err.(ErrUserInactive) - return ok -} - -func (err ErrUserInactive) Error() string { - return fmt.Sprintf("user is inactive [uid: %d, name: %s]", err.UID, err.Name) -} - -// Unwrap unwraps this error as a ErrPermission error -func (err ErrUserInactive) Unwrap() error { - return util.ErrPermissionDenied -} - // ErrUserIsNotLocal represents a "ErrUserIsNotLocal" kind of error. type ErrUserIsNotLocal struct { UID int64 diff --git a/models/user/search.go b/models/user/search.go index cb90ca850e..ecc95caf3d 100644 --- a/models/user/search.go +++ b/models/user/search.go @@ -40,6 +40,7 @@ type SearchUserOptions struct { IsProhibitLogin optional.Option[bool] IncludeReserved bool + Load2FAStatus bool ExtraParamStrings map[string]string } @@ -126,17 +127,15 @@ func (opts *SearchUserOptions) toSearchQueryBase(ctx context.Context) *xorm.Sess return e.Where(cond) } - // 2fa filter uses LEFT JOIN to check whether a user has a 2fa record - // While using LEFT JOIN, sometimes the performance might not be good, but it won't be a problem now, such SQL is seldom executed. - // There are some possible methods to refactor this SQL in future when we really need to optimize the performance (but not now): - // (1) add a column in user table (2) add a setting value in user_setting table (3) use search engines (bleve/elasticsearch) + // Check if the user has two factor enabled, which is TOTP or Webauthn. if opts.IsTwoFactorEnabled.Value() { - cond = cond.And(builder.Expr("two_factor.uid IS NOT NULL")) + cond = cond.And(builder.Expr("two_factor.uid IS NOT NULL OR webauthn_credential.user_id IS NOT NULL")) } else { - cond = cond.And(builder.Expr("two_factor.uid IS NULL")) + cond = cond.And(builder.Expr("two_factor.uid IS NULL AND webauthn_credential.user_id IS NULL")) } return e.Join("LEFT OUTER", "two_factor", "two_factor.uid = `user`.id"). + Join("LEFT OUTER", "webauthn_credential", "webauthn_credential.user_id = `user`.id"). Where(cond) } diff --git a/models/user/user.go b/models/user/user.go index 423a26c8d3..f986ac5482 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -1043,22 +1043,6 @@ func GetUserByName(ctx context.Context, name string) (*User, error) { return u, nil } -// GetUserEmailsByNames returns a list of e-mails corresponds to names of users -// that have their email notifications set to enabled or onmention. -func GetUserEmailsByNames(ctx context.Context, names []string) []string { - mails := make([]string, 0, len(names)) - for _, name := range names { - u, err := GetUserByName(ctx, name) - if err != nil { - continue - } - if u.IsMailable() && u.EmailNotificationsPreference != EmailNotificationsDisabled { - mails = append(mails, u.Email) - } - } - return mails -} - // GetMaileableUsersByIDs gets users from ids, but only if they can receive mails func GetMaileableUsersByIDs(ctx context.Context, ids []int64, isMention bool) ([]*User, error) { if len(ids) == 0 { @@ -1085,17 +1069,6 @@ func GetMaileableUsersByIDs(ctx context.Context, ids []int64, isMention bool) ([ Find(&ous) } -// GetUserNamesByIDs returns usernames for all resolved users from a list of Ids. -func GetUserNamesByIDs(ctx context.Context, ids []int64) ([]string, error) { - unames := make([]string, 0, len(ids)) - err := db.GetEngine(ctx).In("id", ids). - Table("user"). - Asc("name"). - Cols("name"). - Find(&unames) - return unames, err -} - // GetUserNameByID returns username for the id func GetUserNameByID(ctx context.Context, id int64) (string, error) { var name string diff --git a/models/user/user_test.go b/models/user/user_test.go index 2c8c1609fd..263c38933a 100644 --- a/models/user/user_test.go +++ b/models/user/user_test.go @@ -102,16 +102,6 @@ func TestGetUserByName(t *testing.T) { } } -func TestGetUserEmailsByNames(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - - // ignore none active user email - assert.ElementsMatch(t, []string{"user8@example.com"}, user_model.GetUserEmailsByNames(db.DefaultContext, []string{"user8", "user9"})) - assert.ElementsMatch(t, []string{"user8@example.com", "user5@example.com"}, user_model.GetUserEmailsByNames(db.DefaultContext, []string{"user8", "user5"})) - - assert.ElementsMatch(t, []string{"user8@example.com"}, user_model.GetUserEmailsByNames(db.DefaultContext, []string{"user8", "org7"})) -} - func TestCanCreateOrganization(t *testing.T) { require.NoError(t, unittest.PrepareTestDatabase()) @@ -222,7 +212,7 @@ func TestSearchUsers(t *testing.T) { []int64{1041, 37}) testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsTwoFactorEnabled: optional.Some(true)}, - []int64{24}) + []int64{24, 32}) } func TestEmailNotificationPreferences(t *testing.T) { diff --git a/modules/markup/file_preview.go b/modules/markup/file_preview.go index 3caf08f7bb..2171f6097b 100644 --- a/modules/markup/file_preview.go +++ b/modules/markup/file_preview.go @@ -8,6 +8,7 @@ import ( "bytes" "html/template" "io" + "net/url" "regexp" "slices" "strconv" @@ -83,6 +84,10 @@ func newFilePreview(ctx *RenderContext, node *html.Node, locale translation.Loca } else if Type(filePath) != "" { urlFullSource = node.Data[m[0]:m[6]] + filePath + "?display=source#" + node.Data[m[8]:m[1]] } + filePath, err := url.QueryUnescape(filePath) + if err != nil { + return nil + } hash := node.Data[m[8]:m[9]] preview.start = m[0] diff --git a/modules/markup/html.go b/modules/markup/html.go index 3355c021ce..ca9857d2bf 100644 --- a/modules/markup/html.go +++ b/modules/markup/html.go @@ -759,9 +759,6 @@ func shortLinkProcessor(ctx *RenderContext, node *html.Node) { title = path.Base(name) } alt := props["alt"] - if alt == "" { - alt = name - } // make the childNode an image - if we can, we also place the alt childNode.Type = html.ElementNode @@ -772,9 +769,6 @@ func shortLinkProcessor(ctx *RenderContext, node *html.Node) { {Key: "title", Val: title}, {Key: "alt", Val: alt}, } - if alt == "" { - childNode.Attr = childNode.Attr[:2] - } } else { if !absoluteLink { if ctx.IsWiki { diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go index 702c5a716d..6c00c5e7c8 100644 --- a/modules/markup/html_test.go +++ b/modules/markup/html_test.go @@ -416,7 +416,7 @@ func TestRender_ShortLinks(t *testing.T) { otherImgurlWiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw", "Link+Other.jpg") encodedImgurlWiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw", "Link+%23.jpg") notencodedImgurlWiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw", "some", "path", "Link+#.jpg") - favicon := "http://google.com/favicon.ico" + favicon := "https://forgejo.org/favicon.ico" test( "[[Link]]", @@ -424,28 +424,28 @@ func TestRender_ShortLinks(t *testing.T) { `

Link

`) test( "[[Link.jpg]]", - `

Link.jpg

`, - `

Link.jpg

`) + `

`, + `

`) test( "[["+favicon+"]]", - `

`+favicon+`

`, - `

`+favicon+`

`) + `

`, + `

`) test( "[[Name|Link]]", `

Name

`, `

Name

`) test( "[[Name|Link.jpg]]", - `

Name

`, - `

Name

`) + `

`, + `

`) test( "[[Name|Link.jpg|alt=AltName]]", `

AltName

`, `

AltName

`) test( "[[Name|Link.jpg|title=Title]]", - `

Title

`, - `

Title

`) + `

`, + `

`) test( "[[Name|Link.jpg|alt=AltName|title=Title]]", `

AltName

`, @@ -472,16 +472,16 @@ func TestRender_ShortLinks(t *testing.T) { `

Link Other Link Link?

`) test( "[[Link #.jpg]]", - `

Link #.jpg

`, - `

Link #.jpg

`) + `

`, + `

`) test( "[[Name|Link #.jpg|alt=\"AltName\"|title='Title']]", `

AltName

`, `

AltName

`) test( "[[some/path/Link #.jpg]]", - `

some/path/Link #.jpg

`, - `

some/path/Link #.jpg

`) + `

`, + `

`) test( "

[[foobar]]

", `

[[foobar]]

`, @@ -1129,4 +1129,35 @@ func TestRender_FilePreview(t *testing.T) { localMetas, ) }) + + commitFileURL = util.URLJoin(markup.TestRepoURL, "src", "commit", "eeb243c3395e1921c5d90e73bd739827251fc99d", "path", "to", "file%20%23.txt") + + t.Run("file with strange characters in name", func(t *testing.T) { + testRender( + commitFileURL+"#L1", + `

`+ + `
`+ + `
`+ + `
`+ + `path/to/file #.txt`+ + `
`+ + ``+ + `Line 1 in eeb243c`+ + ``+ + `
`+ + `
`+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + `
A`+"\n"+`
`+ + `
`+ + `
`+ + `

`, + localMetas, + ) + }) } diff --git a/modules/markup/markdown/markdown_test.go b/modules/markup/markdown/markdown_test.go index 4c9ab42806..e64ab6a6f4 100644 --- a/modules/markup/markdown/markdown_test.go +++ b/modules/markup/markdown/markdown_test.go @@ -108,7 +108,7 @@ func TestRender_Images(t *testing.T) { test( "[["+title+"|"+url+"]]", - `

`+title+`

`) + `

`) test( "[!["+title+"]("+url+")]("+href+")", `

`+title+`

`) @@ -119,7 +119,7 @@ func TestRender_Images(t *testing.T) { test( "[["+title+"|"+url+"]]", - `

`+title+`

`) + `

`) test( "[!["+title+"]("+url+")]("+href+")", `

`+title+`

`) @@ -149,13 +149,13 @@ func testAnswers(baseURLContent, baseURLImages string) []string { - + - + @@ -164,9 +164,9 @@ func testAnswers(baseURLContent, baseURLImages string) []string { `

Excelsior JET allows you to create native executables for Windows, Linux and Mac OS X.

  1. Package your libGDX application
    -images/1.png
  2. +
  3. Perform a test run by hitting the Run! button.
    -images/2.png
  4. +

More tests

(from https://www.markdownguide.org/extended-syntax/)

@@ -849,8 +849,8 @@ mail@domain.com local image
local image
remote image
-local image
-remote link
+
+
https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb
@@ -876,8 +876,8 @@ space

local image
local image
remote image
-local image
-remote link
+
+
https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb
@@ -905,8 +905,8 @@ space

local image
local image
remote image
-local image
-remote link
+
+
https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb
@@ -934,8 +934,8 @@ space

local image
local image
remote image
-local image
-remote link
+
+
https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb
@@ -963,8 +963,8 @@ space

local image
local image
remote image
-local image
-remote link
+
+
https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb
@@ -992,8 +992,8 @@ space

local image
local image
remote image
-local image
-remote link
+
+
https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb
@@ -1022,8 +1022,8 @@ space

local image
local image
remote image
-local image
-remote link
+
+
https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb
@@ -1052,8 +1052,8 @@ space

local image
local image
remote image
-local image
-remote link
+
+
https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb
@@ -1082,8 +1082,8 @@ space

local image
local image
remote image
-local image
-remote link
+
+
https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb
@@ -1112,8 +1112,8 @@ space

local image
local image
remote image
-local image
-remote link
+
+
https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb
@@ -1143,8 +1143,8 @@ space

local image
local image
remote image
-local image
-remote link
+
+
https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb
@@ -1174,8 +1174,8 @@ space

local image
local image
remote image
-local image
-remote link
+
+
https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb
diff --git a/modules/markup/tests/repo/repo1_filepreview/objects/18/9739e1c2a6cdb8ee094ba1ef8a2e40cf65b2ec b/modules/markup/tests/repo/repo1_filepreview/objects/18/9739e1c2a6cdb8ee094ba1ef8a2e40cf65b2ec new file mode 100644 index 0000000000..c8b99f906b Binary files /dev/null and b/modules/markup/tests/repo/repo1_filepreview/objects/18/9739e1c2a6cdb8ee094ba1ef8a2e40cf65b2ec differ diff --git a/modules/markup/tests/repo/repo1_filepreview/objects/2a/4032b49cff56d6d4921133e087d9dc0341a2c5 b/modules/markup/tests/repo/repo1_filepreview/objects/2a/4032b49cff56d6d4921133e087d9dc0341a2c5 new file mode 100644 index 0000000000..f799e8a988 Binary files /dev/null and b/modules/markup/tests/repo/repo1_filepreview/objects/2a/4032b49cff56d6d4921133e087d9dc0341a2c5 differ diff --git a/modules/markup/tests/repo/repo1_filepreview/objects/2d/2f8eaa17b17359ee1c73222065575d50d9a157 b/modules/markup/tests/repo/repo1_filepreview/objects/2d/2f8eaa17b17359ee1c73222065575d50d9a157 new file mode 100644 index 0000000000..7f4c451d00 Binary files /dev/null and b/modules/markup/tests/repo/repo1_filepreview/objects/2d/2f8eaa17b17359ee1c73222065575d50d9a157 differ diff --git a/modules/markup/tests/repo/repo1_filepreview/objects/2f/b9577a8e940a0a84a789cdd4a45d0f172e3fdd b/modules/markup/tests/repo/repo1_filepreview/objects/2f/b9577a8e940a0a84a789cdd4a45d0f172e3fdd new file mode 100644 index 0000000000..fc97712911 Binary files /dev/null and b/modules/markup/tests/repo/repo1_filepreview/objects/2f/b9577a8e940a0a84a789cdd4a45d0f172e3fdd differ diff --git a/modules/markup/tests/repo/repo1_filepreview/objects/2f/f8eb63aad050c3f20e9cb27090ab7378267ab2 b/modules/markup/tests/repo/repo1_filepreview/objects/2f/f8eb63aad050c3f20e9cb27090ab7378267ab2 new file mode 100644 index 0000000000..e230df1343 Binary files /dev/null and b/modules/markup/tests/repo/repo1_filepreview/objects/2f/f8eb63aad050c3f20e9cb27090ab7378267ab2 differ diff --git a/modules/markup/tests/repo/repo1_filepreview/objects/3e/2a4f1b9a15ffa15ea7ffdc06acd302442b3eca b/modules/markup/tests/repo/repo1_filepreview/objects/3e/2a4f1b9a15ffa15ea7ffdc06acd302442b3eca new file mode 100644 index 0000000000..78189a52f6 --- /dev/null +++ b/modules/markup/tests/repo/repo1_filepreview/objects/3e/2a4f1b9a15ffa15ea7ffdc06acd302442b3eca @@ -0,0 +1 @@ +xAN0EYGB;a U=D9=&r}7ҌB^yY8:A X}RXks";uF9x EdВ%~**Z3\v9Й>n8fxk=[9K%L>{7s;av4hXOHԓՆ`K \ No newline at end of file diff --git a/modules/markup/tests/repo/repo1_filepreview/objects/8a/3b1881b5c4e7dc2be7ee1c0f37f93ffbb5ff77 b/modules/markup/tests/repo/repo1_filepreview/objects/8a/3b1881b5c4e7dc2be7ee1c0f37f93ffbb5ff77 new file mode 100644 index 0000000000..0ea93376dc Binary files /dev/null and b/modules/markup/tests/repo/repo1_filepreview/objects/8a/3b1881b5c4e7dc2be7ee1c0f37f93ffbb5ff77 differ diff --git a/modules/markup/tests/repo/repo1_filepreview/objects/95/31b649823095acf5d79ab9e4f8b8d86046352f b/modules/markup/tests/repo/repo1_filepreview/objects/95/31b649823095acf5d79ab9e4f8b8d86046352f new file mode 100644 index 0000000000..ab36311f6f Binary files /dev/null and b/modules/markup/tests/repo/repo1_filepreview/objects/95/31b649823095acf5d79ab9e4f8b8d86046352f differ diff --git a/modules/markup/tests/repo/repo1_filepreview/objects/ac/769ab4baa91060a4c2f828f53e6c3cc2f708f8 b/modules/markup/tests/repo/repo1_filepreview/objects/ac/769ab4baa91060a4c2f828f53e6c3cc2f708f8 new file mode 100644 index 0000000000..59afaebf4a Binary files /dev/null and b/modules/markup/tests/repo/repo1_filepreview/objects/ac/769ab4baa91060a4c2f828f53e6c3cc2f708f8 differ diff --git a/modules/markup/tests/repo/repo1_filepreview/objects/c5/3110b1957cefc56c4b2d879476ddbe905980bf b/modules/markup/tests/repo/repo1_filepreview/objects/c5/3110b1957cefc56c4b2d879476ddbe905980bf new file mode 100644 index 0000000000..3de089bf6a Binary files /dev/null and b/modules/markup/tests/repo/repo1_filepreview/objects/c5/3110b1957cefc56c4b2d879476ddbe905980bf differ diff --git a/modules/markup/tests/repo/repo1_filepreview/objects/e7/99b34ea867a0364d0df33f382562db9ff39084 b/modules/markup/tests/repo/repo1_filepreview/objects/e7/99b34ea867a0364d0df33f382562db9ff39084 new file mode 100644 index 0000000000..ef73ed1791 Binary files /dev/null and b/modules/markup/tests/repo/repo1_filepreview/objects/e7/99b34ea867a0364d0df33f382562db9ff39084 differ diff --git a/modules/markup/tests/repo/repo1_filepreview/objects/ee/b243c3395e1921c5d90e73bd739827251fc99d b/modules/markup/tests/repo/repo1_filepreview/objects/ee/b243c3395e1921c5d90e73bd739827251fc99d new file mode 100644 index 0000000000..5515b07d4a Binary files /dev/null and b/modules/markup/tests/repo/repo1_filepreview/objects/ee/b243c3395e1921c5d90e73bd739827251fc99d differ diff --git a/modules/markup/tests/repo/repo1_filepreview/objects/f7/0f10e4db19068f79bc43844b49f3eece45c4e8 b/modules/markup/tests/repo/repo1_filepreview/objects/f7/0f10e4db19068f79bc43844b49f3eece45c4e8 new file mode 100644 index 0000000000..2e15b4fb0a Binary files /dev/null and b/modules/markup/tests/repo/repo1_filepreview/objects/f7/0f10e4db19068f79bc43844b49f3eece45c4e8 differ diff --git a/modules/markup/tests/repo/repo1_filepreview/refs/heads/master b/modules/markup/tests/repo/repo1_filepreview/refs/heads/master index f3d5d39dd5..709cffca17 100644 --- a/modules/markup/tests/repo/repo1_filepreview/refs/heads/master +++ b/modules/markup/tests/repo/repo1_filepreview/refs/heads/master @@ -1 +1 @@ -c9913120ed2c1e27c1d7752ecdb7a504dc7cf6be +eeb243c3395e1921c5d90e73bd739827251fc99d diff --git a/modules/templates/util_avatar.go b/modules/templates/util_avatar.go index afc1091516..a468361101 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) diff --git a/modules/templates/util_render_test.go b/modules/templates/util_render_test.go index 3479d94039..028cd2ba76 100644 --- a/modules/templates/util_render_test.go +++ b/modules/templates/util_render_test.go @@ -194,8 +194,8 @@ func TestRenderMarkdownToHtml(t *testing.T) { remote linklocal imageremote image -local image -remote link + +88fc37a3c0...12fc37a3c0 (hash) com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare 88fc37a3c0 diff --git a/modules/translation/i18n/i18n_test.go b/modules/translation/i18n/i18n_test.go index 41f85931aa..66f5d5c402 100644 --- a/modules/translation/i18n/i18n_test.go +++ b/modules/translation/i18n/i18n_test.go @@ -158,6 +158,7 @@ commits = fallback value for commits found := lang1.HasKey("no-such") assert.False(t, found) + assert.EqualValues(t, "no-such", lang1.TrString("no-such")) require.NoError(t, ls.Close()) } diff --git a/modules/translation/i18n/localestore.go b/modules/translation/i18n/localestore.go index e80b2592ae..a38e967838 100644 --- a/modules/translation/i18n/localestore.go +++ b/modules/translation/i18n/localestore.go @@ -225,9 +225,9 @@ func (l *locale) TrString(trKey string, trArgs ...any) string { format = msg } else { // First fallback: old-style translation - idx, ok := l.store.trKeyToIdxMap[trKey] + idx, foundIndex := l.store.trKeyToIdxMap[trKey] found := false - if ok { + if foundIndex { if msg, ok := l.idxToMsgMap[idx]; ok { format = msg // use the found translation found = true @@ -239,7 +239,7 @@ func (l *locale) TrString(trKey string, trArgs ...any) string { if defaultLang, ok := l.store.localeMap[l.store.defaultLang]; ok { if msg := defaultLang.LookupNewStyleMessage(trKey); msg != "" { format = msg - } else { + } else if foundIndex { // Third fallback: old-style default language if msg, ok := defaultLang.idxToMsgMap[idx]; ok { format = msg diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini index 2c47093cea..f5b6540023 100644 --- a/options/locale/locale_cs-CZ.ini +++ b/options/locale/locale_cs-CZ.ini @@ -680,6 +680,8 @@ Location = Umístění To = Název větve Biography = Životopis AccessToken = Přístupový token +username_claiming_cooldown = Uživatelské jméno nelze získat, protože ještě neskončila doba jeho platnosti. Půjde jej získat %[1]s. +email_domain_is_not_allowed = Doména uživatelské e-mailové adresy %s je v rozporu se seznamem EMAIL_DOMAIN_ALLOWLIST nebo EMAIL_DOMAIN_BLOCKLIST. Ujistěte se, že je vaše adresa správně nastavena. [user] change_avatar=Změnit váš avatar… @@ -804,7 +806,7 @@ old_password=Stávající heslo new_password=Nové heslo retype_new_password=Potvrzení nového hesla password_incorrect=Zadané heslo není správné. -change_password_success=Vaše heslo bylo aktualizováno. Od teď se přihlašujte novým heslem. +change_password_success=Vaše heslo bylo aktualizováno. Od nynějška používejte kpřihlášení nové heslo. password_change_disabled=Externě ověřovaní uživatelé nemohou aktualizovat své heslo prostřednictvím webového rozhraní Forgejo. emails=E-mailové adresy @@ -1056,6 +1058,8 @@ language.description = Tento jazyk bude uložen do vašeho účtu a po přihlá language.localization_project = Pomozte nám s překladem Forgejo do vašeho jazyka! Více informací. user_block_yourself = Nemůžete zablokovat sami sebe. pronouns_custom_label = Vlastní zájmena +change_username_redirect_prompt.with_cooldown.few = Staré uživatelské jméno bude dostupné ostatním po %[1]d dnech. Do té doby budete moci své staré uživatelské jméno znovu získat. +change_username_redirect_prompt.with_cooldown.one = Staré uživatelské jméno bude dostupné ostatním po %[1]d dni. Do té doby budete moci své staré uživatelské jméno znovu získat. [repo] new_repo_helper=Repozitář obsahuje všechny soubory projektu, včetně historie revizí. Už jej hostujete jinde? Migrovat repozitář. @@ -3001,6 +3005,8 @@ teams.invite.by=Pozvání od %s teams.invite.description=Pro připojení k týmu klikněte na tlačítko níže. follow_blocked_user = Tuto organizaci nemůžete sledovat, protože jste v ní zablokováni. open_dashboard = Otevřít nástěnku +settings.change_orgname_redirect_prompt.with_cooldown.one = Staré uživatelské jméno bude dostupné ostatním po %[1]d dni. Do té doby budete moci své staré uživatelské jméno znovu získat. +settings.change_orgname_redirect_prompt.with_cooldown.few = Staré uživatelské jméno bude dostupné ostatním po %[1]d dnech. Do té doby budete moci své staré uživatelské jméno znovu získat. [admin] dashboard=Přehled diff --git a/options/locale/locale_da.ini b/options/locale/locale_da.ini index 2df385460b..a08fcaf8bc 100644 --- a/options/locale/locale_da.ini +++ b/options/locale/locale_da.ini @@ -629,6 +629,8 @@ password_special_one = Mindst ét specialtegn (tegnsætning, parenteser, anførs unsupported_login_type = Login typen understøttes ikke for at slette kontoen. cannot_add_org_to_team = En organisation kan ikke tilføjes som et holdmedlem. must_use_public_key = Nøglen du har angivet er en privat nøgle. Lad være med at uploade din private nøgle nogen steder. Brug din offentlige nøgle i stedet. +username_claiming_cooldown = Brugernavnet kan ikke gøres krav på, fordi dets nedkølingsperiode endnu ikke er forbi. Det kan gøres krav på %[1]s. +email_domain_is_not_allowed = Domænet for brugerens e-mailadresse %s er i konflikt med EMAIL_DOMAIN_ALLOWLIST eller EMAIL_DOMAIN_BLOCKLIST. Sørg for, at du har indstillet e-mailadressen korrekt. [user] change_avatar = Skift din avatar… @@ -782,7 +784,7 @@ uploaded_avatar_is_too_big = Den uploadede filstørrelse (%d KiB) overstiger den email_deletion_desc = E-mailadressen og relaterede oplysninger vil blive fjernet fra din konto. Git commits af denne e-mailadresse forbliver uændret. Fortsætte? choose_new_avatar = Vælg ny avatar update_avatar = Opdater avatar -change_password_success = Din adgangskode er blevet opdateret. Log ind med din nye adgangskode fra nu af. +change_password_success = Din adgangskode er blevet opdateret. Fra nu af skal du bruge din nye adgangskode til at logge ind. add_email = Tilføj e-mailadresse add_openid = Tilføj OpenID URI add_email_confirmation_sent = En bekræftelses-e-mail er blevet sendt til "%s". For at bekræfte din e-mailadresse, tjek venligst din indbakke og følg det medfølgende link inden for de næste %s. @@ -972,6 +974,8 @@ delete_with_all_comments = Din konto er yngre end %s. For at undgå spøgelsesko delete_account_title = Slet brugerkonto user_block_yourself = Du kan ikke blokere dig selv. pronouns_custom_label = Brugerdefinerede stedord +change_username_redirect_prompt.with_cooldown.one = Det gamle brugernavn vil være tilgængeligt for alle efter en nedkølingsperiode på %[1]d dag, du kan stadig kræve det gamle brugernavn tilbage i nedkølingsperioden. +change_username_redirect_prompt.with_cooldown.few = Det gamle brugernavn vil være tilgængeligt for alle efter en nedkølingsperiode på %[1]d dage, du kan stadig kræve det gamle brugernavn tilbage i nedkølingsperioden. [repo] rss.must_be_on_branch = Du skal være på en gren for at have et RSS-feed. @@ -1761,6 +1765,28 @@ pulls.compare_changes = Ny pull-anmodning issues.summary_card_alt = Oversigtskort over et problem med titlen "%s" i depotet %s pulls.edit.already_changed = Kunne ikke gemme ændringer af pull-anmodningen. Det ser ud til, at indholdet allerede er blevet ændret af en anden bruger. Opdater siden, og prøv at redigere igen for at undgå at overskrive deres ændringer pulls.sign_in_require = Log ind for at oprette en ny pull-anmodning. +pulls.allow_edits_from_maintainers = Tillad redigeringer fra vedligeholdere +pulls.allow_edits_from_maintainers_desc = Brugere med skriveadgang til basisgrenen kan også trykke til denne gren +pulls.allow_edits_from_maintainers_err = Opdatering mislykkedes +pulls.compare_base = flet ind i +pulls.compare_compare = pull fra +pulls.switch_comparison_type = Skift sammenligningstype +pulls.switch_head_and_base = Skift hoved og base +pulls.filter_branch = Filter gren +pulls.compare_changes_desc = Vælg den gren, der skal flettes ind i, og den gren, der skal trækkes fra. +pulls.has_viewed_file = Set +pulls.has_changed_since_last_review = Ændret siden din sidste gennemgang +pulls.no_results = Ingen resultater fundet. +pulls.show_all_commits = Vis alle commits +pulls.show_changes_since_your_last_review = Vis ændringer siden din sidste gennemgang +pulls.showing_only_single_commit = Viser kun ændringer af commit %[1]s +pulls.showing_specified_commit_range = Viser kun ændringer mellem %[1]s..%[2]s +pulls.select_commit_hold_shift_for_range = Vælg commit. Hold Shift + klik for at vælge et område +pulls.review_only_possible_for_full_diff = Gennemgang er kun mulig, når du ser den fulde diff +pulls.filter_changes_by_commit = Filtrer efter commit +pulls.expand_files = Udvid alle filer +pulls.collapse_files = Skjul alle filer +pulls.viewed_files_label = %[1]d / %[2]d filer set [notification] watching = Overvåger diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index f5e0d314ae..28e0016348 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -681,6 +681,8 @@ Website = Webseite Location = Ort To = Branchname AccessToken = Zugangstoken +username_claiming_cooldown = Der Benutzername kann nicht beansprucht werden, weil seine Schutzzeit noch nicht vorbei ist. Er kann am %[1]s beansprucht werden. +email_domain_is_not_allowed = Die Domain der E-Mail-Adresse des Benutzers %s steht in Konflikt mit EMAIL_DOMAIN_ALLOWLIST oder EMAIL_DOMAIN_BLOCKLIST. Bitte stelle sicher, dass du die E-Mail-Adresse richtig gesetzt hast. [user] @@ -806,7 +808,7 @@ old_password=Aktuelles Passwort new_password=Neues Passwort retype_new_password=Neues Passwort bestätigen password_incorrect=Das aktuelle Passwort ist falsch. -change_password_success=Dein Passwort wurde aktualisiert. Bitte verwende dieses beim nächsten Einloggen. +change_password_success=Dein Passwort wurde aktualisiert. Verwende ab jetzt dein neues Passwort zum Einloggen. password_change_disabled=Benutzer, die nicht von Forgejo verwaltet werden, können ihr Passwort im Web-Interface nicht ändern. emails=E-Mail-Adressen @@ -1058,6 +1060,8 @@ language.localization_project = Hilf uns, Forgejo in deine Sprache zu übersetze language.description = Diese Sprache wird in deinem Konto gespeichert und standardmäßig nach dem Anmelden benutzt. user_block_yourself = Du kannst dich nicht selbst blockieren. pronouns_custom_label = Individuelle Pronomen +change_username_redirect_prompt.with_cooldown.one = Der alte Benutzername ist nach einer Schutzzeit von einem Tag wieder für alle Verfügbar. Du kannst den alten Benutzername während dieser Schutzzeit erneut beanspruchen. +change_username_redirect_prompt.with_cooldown.few = Der alte Benutzername ist nach einer Schutzzeit von %[1]d Tagen wieder für alle Verfügbar. Du kannst den alten Benutzername während dieser Schutzzeit erneut beanspruchen. [repo] owner=Besitzer @@ -3004,6 +3008,8 @@ teams.invite.by=Von %s eingeladen teams.invite.description=Bitte klicke auf die folgende Schaltfläche, um dem Team beizutreten. follow_blocked_user = Du kannst dieser Organisation nicht folgen, weil diese Organisation dich blockiert hat. open_dashboard = Übersicht öffnen +settings.change_orgname_redirect_prompt.with_cooldown.one = Der alte Benutzername ist nach einer Schutzzeit von einem Tag wieder für alle Verfügbar. Du kannst den alten Benutzername während dieser Schutzzeit erneut beanspruchen. +settings.change_orgname_redirect_prompt.with_cooldown.few = Der alte Benutzername ist nach einer Schutzzeit von %[1]d Tagen wieder für alle Verfügbar. Du kannst den alten Benutzername während dieser Schutzzeit erneut beanspruchen. [admin] dashboard=Übersicht @@ -3109,7 +3115,7 @@ dashboard.sync_branch.started=Synchronisierung der Branches gestartet dashboard.rebuild_issue_indexer=Issue-Indexer neu bauen users.user_manage_panel=Benutzerkonten verwalten -users.new_account=Benutzerkonto erstellen +users.new_account=Benutzeraccount erstellen users.name=Benutzername users.full_name=Vollständiger Name users.activated=Aktiviert @@ -3579,7 +3585,7 @@ comment_pull=`hat den Pull-Request %[3]s#%[2]s kommentiert` merge_pull_request=`führte Pull-Request %[3]s#%[2]s zusammen` auto_merge_pull_request=`führte Pull-Request %[3]s#%[2]s automatisch zusammen` transfer_repo=hat Repository %s übertragen zu %s -push_tag=Tag %[3]s nach %[4]s wurde gepusht +push_tag=hat Tag %[3]s auf %[4]s gepusht delete_tag=hat Tag %[2]s in %[3]s gelöscht delete_branch=hat Branch %[2]s in %[3]s gelöscht compare_branch=Vergleichen diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index da2457ccb7..166616ba91 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1193,6 +1193,7 @@ archive.title = This repository is archived. You can view files and clone it, bu archive.title_date = This repository has been archived on %s. You can view files and clone it, but cannot push or open issues or pull requests. archive.issue.nocomment = This repository is archived. You cannot comment on issues. archive.pull.nocomment = This repository is archived. You cannot comment on pull requests. +archive.pull.noreview = This repository is archived. You cannot review pull requests. form.reach_limit_of_creation_1 = The owner has already reached the limit of %d repository. form.reach_limit_of_creation_n = The owner has already reached the limit of %d repositories. @@ -1431,6 +1432,7 @@ editor.user_no_push_to_branch = User cannot push to branch editor.require_signed_commit = Branch requires a signed commit editor.cherry_pick = Cherry-pick %s onto: editor.revert = Revert %s onto: +editor.commit_email = Commit email commits.desc = Browse source code change history. commits.commits = Commits diff --git a/options/locale/locale_es-ES.ini b/options/locale/locale_es-ES.ini index ec0239648c..abdadc6c66 100644 --- a/options/locale/locale_es-ES.ini +++ b/options/locale/locale_es-ES.ini @@ -56,7 +56,7 @@ mirror=Réplica new_repo=Nuevo repositorio new_migrate=Nueva migración new_mirror=Nueva réplica -new_fork=Nuevo fork de repositorio +new_fork=Nueva bifurcación del repositorio new_org=Nueva organización new_project=Nuevo proyecto new_project_column=Nueva columna @@ -75,7 +75,7 @@ collaborative=Colaborativo forks=Forks activities=Actividades -pull_requests=Pull requests +pull_requests=Solicitudes de incorporación de cambios issues=Incidencias milestones=Hitos @@ -156,8 +156,8 @@ invalid_data = Datos inválidos: %v confirm_delete_artifact = ¿Estás seguro de que deseas eliminar el artefacto "%s"? more_items = Mas cosas copy_generic = Copiar al portapapeles -filter.not_fork = No forks -filter.is_fork = Forks +filter.not_fork = No hay bifurcaciones +filter.is_fork = Bifurcaciones test = Test error413 = Has agotado tu cuota. new_repo.title = Nuevo repositorio @@ -397,8 +397,8 @@ code_search_results=Resultados de búsqueda para «%s» code_last_indexed_at=Indexado por última vez %s relevant_repositories_tooltip=Repositorios que son bifurcaciones o que no tienen ningún tema, ningún icono, y ninguna descripción están ocultos. relevant_repositories=Solo se muestran repositorios relevantes, mostrar resultados sin filtrar. -forks_few = %d forks -forks_one = %d fork +forks_few = %d bifurcaciones +forks_one = %d bifurcación stars_few = %d estrellas stars_one = %d estrella @@ -1070,10 +1070,10 @@ visibility=Visibilidad visibility_description=Sólo el propietario o los miembros de la organización -si tienen derechos- podrán verlo. visibility_helper=Hacer el repositorio privado visibility_helper_forced=El administrador de su sitio obliga a nuevos repositorios a ser privados. -visibility_fork_helper=(Cambiar esto afectará a la visibilidad de todos los forks.) +visibility_fork_helper=(Cambiar esto afectará a la visibilidad de todas las bifurcaciones.) clone_helper=¿Necesita ayuda para clonar? Visite Ayuda. -fork_repo=Hacer fork del repositorio -fork_from=Crear un fork desde +fork_repo=Hacer una bifurcación del repositorio +fork_from=Crear una bifurcación desde already_forked=Ya ha forkeado %s fork_to_different_account=Forkear a una cuenta diferente fork_visibility_helper=La visibilidad de un repositorio del cual se ha hecho fork no puede ser cambiada. @@ -3852,7 +3852,7 @@ search = Buscar... type_tooltip = Tipo de búsqueda project_kind = Buscar proyectos... branch_kind = Buscar ramas... -commit_kind = Buscar commits... +commit_kind = Buscar confirmaciones... repo_kind = Buscar repositorios... user_kind = Buscar usuarios... org_kind = Buscar organizaciones... diff --git a/options/locale/locale_et.ini b/options/locale/locale_et.ini index 26621dd16b..e54ceadbb5 100644 --- a/options/locale/locale_et.ini +++ b/options/locale/locale_et.ini @@ -108,7 +108,7 @@ never = Mitte kunagi unknown = Teadmata rss_feed = RSS infovoog confirm_delete_artifact = Kas oled kindel et soovite artefakti "%s" kustutada? -pin = +pin = artifacts = Artefaktid archived = Arhiveeritud concept_system_global = Ülemaailmne diff --git a/options/locale/locale_fi-FI.ini b/options/locale/locale_fi-FI.ini index dd1f97bd00..84940f5e45 100644 --- a/options/locale/locale_fi-FI.ini +++ b/options/locale/locale_fi-FI.ini @@ -1756,7 +1756,7 @@ issues.close = Sulje ongelma issues.no_content = Ei kuvausta. pulls.reject_count_1 = %d muutospyyntö pulls.update_branch_success = Haarapäivitys onnistui -milestones.completeness = %d%% valmiina +milestones.completeness = %d% % valmiina contributors.contribution_type.additions = Lisäykset contributors.contribution_type.deletions = Poistot settings.webhook_deletion_success = Webkoukku on poistettu. @@ -2088,6 +2088,9 @@ new_from_template_description = Voit valita olemassa olevan repon mallipohjan ja new_advanced = Lisäasetukset new_advanced_expand = Laajenna napsauttamalla template_description = Repojen mallipohjat mahdollistavat uusien repojen luomisen halutulla hakemistorakenteella, tiedostoilla ja valinnaisilla asetuksilla. +settings.enter_repo_name = Kirjoita omistajan ja repon nimi täsmälleen kuten esitetty: +settings.confirmation_string = Vahvistusteksti +settings.delete_notices_2 = - Tämä toiminto poistaa pysyvästi repon %s mukaan lukien koodin, ongelmat, kommentit, wikidatan ja avustaja-asetukset. @@ -2578,6 +2581,12 @@ commit_repo = työnsi haaraan %[3]s repossa create_issue = `avasi ongelman %[3]s#%[2]s` reopen_issue = `avasi uudelleen ongelman %[3]s#%[2]s` create_pull_request = `loi vetopyynnön %[3]s#%[2]s` +reopen_pull_request = `avasi uudelleen vetopyynnön %[3]s#%[2]s` +close_pull_request = `sulki vetopyynnön %[3]s#%[2]s` +comment_issue = `kommentoi ongelmaa %[3]s#%[2]s` +close_issue = `sulki ongelman %[3]s#%[2]s` +merge_pull_request = `yhdisti vetopyynnön %[3]s#%[2]s` +comment_pull = `kommentoi vetopyyntöä %[3]s#%[2]s` [tool] now=nyt @@ -2624,6 +2633,7 @@ error.not_signed_commit=Kommitti ei ole allekirjoitettu error.extract_sign = Allekirjoituksen purkaminen epäonnistui default_key = Allekirjoitettu oletusavaimella error.failed_retrieval_gpg_keys = Ei saatu yhtäkään kommitin tekijän tiliin liitettyä avainta +error.generate_hash = Tiivisteen luominen kommitista epäonnistui [units] unit = Yksikkö @@ -2765,6 +2775,11 @@ alpine.registry.info = Valitse $branch ja $repository alla olevasta listasta. container.images.title = Levykuvat owner.settings.cargo.initialize = Alusta indeksi owner.settings.cargo.initialize.description = Erityinen Git-repoindeksi vaaditaan Cargo-rekisterin käyttämiseksi. Tämän valinnan käyttäminen luo (tarvittaessa uudelleen) repon ja määrittää sen asetukset automaattisesti. +settings.link.error = Repositorion linkin päivittäminen epäonnistui. +alt.repository.multiple_groups = Tämä paketti on saatavilla useissa ryhmissä. +alt.repository.architectures = Arkkitehtuurit +alt.install = Asenna paketti +alt.registry.install = Asenna paketti suorittamalla komento: [secrets] creation.failed = Salaisuuden lisääminen epäonnistui. diff --git a/options/locale/locale_fil.ini b/options/locale/locale_fil.ini index 07372944bb..dea74a642c 100644 --- a/options/locale/locale_fil.ini +++ b/options/locale/locale_fil.ini @@ -640,6 +640,8 @@ AccessToken = Token ng pag-access Biography = Byograpya Location = Lokasyon visit_rate_limit = Natugunan ang limitasyon sa rate ng malayuang pagbisita. +username_claiming_cooldown = Hindi ma-claim ang username na ito, dahil hindi pa tapos ang panahon ng cooldown. Maari itong i-claim sa %[1]s. +email_domain_is_not_allowed = Sumasalungat ang domain ng email address ng user %s sa EMAIL_DOMAIN_ALLOWLIST o EMAIL_DOMAIN_BLOCKLIST. Siguraduhing natakda mo ang email address nang tama. [user] joined_on = Sumali noong %s @@ -784,7 +786,7 @@ old_password = Kasalukuyang password new_password = Bagong password retype_new_password = Kumpirmahin ang bagong password password_incorrect = Mali ang kasalukuyang password. -change_password_success = Na-update na ang iyong password. Mag-sign in gamit ng bagong password simula ngayon. +change_password_success = Na-update na ang iyong password. Simula ngayon, gamitin ang iyong bagong password para mag-sign in. password_change_disabled = Hindi mababago ng mga di-lokal na gumagamit ang kanilang password sa pamamagitan ng Forgejo web interface. emails = Mga email address manage_emails = Ipamahala ang mga email address @@ -999,6 +1001,8 @@ language.description = Mase-save ang wika sa iyong account at gagamitin bilang d language.localization_project = Tulungan kaming isalin ang Forgejo sa iyong wika! Matuto pa. pronouns_custom_label = Mga pasadyang pronoun user_block_yourself = Hindi mo maaring harangan ang sarili mo. +change_username_redirect_prompt.with_cooldown.one = Magiging available ang lumang username sa lahat pagkatapos ng panahon ng cooldown ng %[1]d araw, maari mo pa ring ma-claim muli ang lumang username sa panahon ng panahon ng cooldown. +change_username_redirect_prompt.with_cooldown.few = Magiging available ang lumang username sa lahat pagkatapos ng panahon ng cooldown ng %[1]d araw, maari mo pa ring ma-claim muli ang lumang username sa panahon ng panahon ng cooldown. [repo] template_description = Ang mga template na repositoryo ay pinapayagan ang mga gumagamit na mag-generate ng mga bagong repositoryo na may magkatulad na istraktura ng direktoryo, mga file, at opsyonal na mga setting. @@ -2911,7 +2915,7 @@ dashboard.delete_old_system_notices = Burahin ang lahat ng mga lumang paunawa ng dashboard.gc_lfs = I-garbage collect ang mga LFS meta object dashboard.stop_zombie_tasks = Itigil ang mga zombie action task users.user_manage_panel = Ipamahala ang mga user account -users.new_account = Gumawa ng User Account +users.new_account = Gumawa ng user account users.auth_login_name = Pangalan ng sign-in authentication users.password_helper = Iwanang walang laman ang password upang panatilihing hindi nabago. users.max_repo_creation = Pinakamataas na numero ng mga repositoryo @@ -3394,6 +3398,8 @@ teams.owners_permission_desc = Ang mga owner ay may punong access sa lah teams.add_nonexistent_repo = Hindi pa umiiral ang repositoryo na sinusubukan mong idagdag. Mangyaring gawin iyan muna. teams.all_repositories = Lahat ng mga repositoryo teams.all_repositories_helper = Ang koponan ay may access sa lahat ng mga repositoryo. Ang pagpili nito ay idadagdag ang lahat ng mga umiiral na repositoryo sa koponan. +settings.change_orgname_redirect_prompt.with_cooldown.few = Magiging available ang lumang username sa lahat pagkatapos ng panahon ng cooldown ng %[1]d araw, maari mo pa ring ma-claim muli ang lumang username sa panahon ng panahon ng cooldown. +settings.change_orgname_redirect_prompt.with_cooldown.one = Magiging available ang lumang username sa lahat pagkatapos ng panahon ng cooldown ng %[1]d araw, maari mo pa ring ma-claim muli ang lumang username sa panahon ng panahon ng cooldown. [packages] @@ -3571,7 +3577,7 @@ alt.registry = I-setup ang registry na ito mula sa command line: alt.registry.install = Para i-install ang package na ito, patakbuhin ang sumusunod na command: alt.install = I-install ang package alt.setup = Idagdag ang repositoryo sa listahan ng mga nakakonektang repositoryo (piliin ang kinakailangang architechture sa halip ng '_arch_'): -alt.repository = Info ng Repositoryo +alt.repository = Info ng repositoryo alt.repository.architectures = Mga architechture alt.repository.multiple_groups = Available ang package na ito sa iba't ibang grupo. @@ -3693,7 +3699,7 @@ approve_pull_request = `inaprubahan ang %[3]s#%[2]s` review_dismissed_reason = Dahilan: compare_branch = Ikumpara reject_pull_request = `nagmungkahi ng mga pagbabago para sa %[3]s#%[2]s` -rename_repo = pinalitan ang pangalan ng repositoryo mula %[1]s sa %[3]# +rename_repo = pinalitan ang pangalan ng repositoryo mula %[1]s sa %[3]s close_issue = `sinara ang isyu na %[3]s#%[2]s` review_dismissed = `na-dismiss ang pagsusuri mula %[4]s para sa %[3]s#%[2]s` close_pull_request = `sinara ang hiling sa paghila na %[3]s#%[2]s` diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini index f67b11dc04..7924c6eea4 100644 --- a/options/locale/locale_fr-FR.ini +++ b/options/locale/locale_fr-FR.ini @@ -680,6 +680,7 @@ Biography = Biographie Website = Site web Location = Emplacement To = Nom de la branche +email_domain_is_not_allowed = Le domaine %s du courriel utilisateur entre en conflit avec EMAIL_DOMAIN_ALLOWLIST ou EMAIL_DOMAIN_BLOCKLIST. Veuillez vous assurer le courriel est renseigné. [user] change_avatar=Changer votre avatar… diff --git a/options/locale/locale_lv-LV.ini b/options/locale/locale_lv-LV.ini index 829459f202..1b0001d44b 100644 --- a/options/locale/locale_lv-LV.ini +++ b/options/locale/locale_lv-LV.ini @@ -75,7 +75,7 @@ mirrors=Spoguļglabātavas collaborative=Līdzdarbošanās forks=Atzarojumi -activities=Aktivitāte +activities=Darbības pull_requests=Izmaiņu pieprasījumi issues=Pieteikumi milestones=Atskaites punkti @@ -678,6 +678,8 @@ Biography = Dzīves un darbības apraksts Website = Tīmekļvietne AccessToken = Piekļuves pilnvara To = Zara nosaukums +username_claiming_cooldown = Šo lietotājvārdu vēl nevar izmantot, jo tā noilgums vēl nav beidzies. To varēs izmantot %[1]s. +email_domain_is_not_allowed = Lietotāja e-pasta adreses %s domēna vārds ir pretrunāt ar EMAIL_DOMAIN_ALLOWLIST vai EMAIL_DOMAIN_BLOCKLIST. Jāpārliecinās, ka e-pasta adrese ir norādīta pareizi. [user] @@ -695,7 +697,7 @@ following_few=%d seko follow=Sekot unfollow=Nesekot user_bio=Biogrāfija -disabled_public_activity=Šis lietotājs ir atslēdzies iespēju aplūkot tā aktivitāti. +disabled_public_activity=Šis lietotājs ir atspējojis darbību redzamību visiem. email_visibility.limited=E-pasta adrese ir redzama visiem autentificētajiem lietotājiem email_visibility.private=E-pasta adrese ir redzama tikai administratoriem show_on_map=Rādīt šo vietu kartē @@ -803,7 +805,7 @@ old_password=Pašreizējā parole new_password=Jaunā parole retype_new_password=Apstiprināt jauno paroli password_incorrect=Ievadīta nepareiza pašreizējā parole. -change_password_success=Parole tika sekmīgi nomainīta. Turpmāk jāpiesakās ar savu jauno paroli. +change_password_success=Parole tika atjaunināta. Turpmāk jāizmanto sava jaunā parole, lai pieteiktos. password_change_disabled=Ārējie lietotāji nevar mainīt savu paroli Forgejo tīmekļa saskarnē. emails=E-pasta adreses @@ -904,7 +906,7 @@ added_on=Pievienots %s valid_until_date=Derīgs līdz %s valid_forever=Derīgs mūžīgi last_used=Pēdējo reizi izmantota -no_activity=Nav nesenas aktivitātes +no_activity=Nav nesenu darbību can_read_info=Lasīt can_write_info=Rakstīt key_state_desc=Šī atslēga ir izmantota pēdējo 7 dienu laikā @@ -1055,6 +1057,8 @@ additional_repo_units_hint_description = Attēlot norādi "Iespējot vēl" glab language.description = Šī valoda tiks saglabāta kontā un pēc pieteikšanās tiks izmantota kā noklusējuma. user_block_yourself = Nevar liegt sevi. pronouns_custom_label = Pielāgoti vietniekvārdi +change_username_redirect_prompt.with_cooldown.one = Vecais lietotājvārds būs pieejams visiem pēc noilguma, kas ir %[1]d diena. Šajā laikā ir iespējams to atkal sākt izmantot. +change_username_redirect_prompt.with_cooldown.few = Vecais lietotājvārds būs pieejams visiem pēc noilguma, kas ir %[1]d dienas. Šajā laikā ir iespējams to atkal sākt izmantot. [repo] new_repo_helper=Glabātava satur visas projekta datnes, tajā skaitā izmaiņu vēsturi. Jau tiek izmantota kaut kur citur? Pārcelt glabātavu. @@ -1652,7 +1656,7 @@ issues.save=Saglabāt issues.label_title=Nosaukums issues.label_description=Apraksts issues.label_color=Krāsa -issues.label_exclusive=Ekskluzīvs +issues.label_exclusive=Sevišķa issues.label_archive=Arhivēt iezīmi issues.label_archived_filter=Rādīt arhivētās iezīmes issues.label_archive_tooltip=Arhivētās iezīmes pēc noklusējuma netiek iekļautas ieteikumos, kad meklē pēc iezīmes. @@ -2033,7 +2037,7 @@ wiki.last_updated=Pēdējo reizi labota %s wiki.page_name_desc=Jāievada šīs vikivietnes lapas nosaukums. Daži īpašie nosaukumi ir: "Home", "_Sidebar" un "_Footer". wiki.original_git_entry_tooltip=Rādīt sākotnējo Git datni, nevis izmantot draudzīgo saiti. -activity=Aktivitāte +activity=Notikumi activity.period.filter_label=Laika periods: activity.period.daily=1 diena activity.period.halfweekly=3 dienas @@ -2999,6 +3003,8 @@ teams.invite.by=Uzaicināja %s teams.invite.description=Lūgums nospiest zemāk esošo pogu, lai pievienotos komandai. open_dashboard = Atvērt pārskata paneli follow_blocked_user = Tu nevari sekot šai apvienībai, jo tā ir liegusi Tevi. +settings.change_orgname_redirect_prompt.with_cooldown.one = Vecais lietotājvārds būs pieejams visiem pēc noilguma, kas ir %[1]d diena. Šajā laikā ir iespējams to atkal sākt izmantot. +settings.change_orgname_redirect_prompt.with_cooldown.few = Vecais lietotājvārds būs pieejams visiem pēc noilguma, kas ir %[1]d dienas. Šajā laikā ir iespējams to atkal sākt izmantot. [admin] dashboard=Pārskata panelis @@ -3761,7 +3767,7 @@ settings.link.select=Atlasīt glabātavu settings.link.button=Atjaunināt glabātavas saiti settings.link.success=Glabātavas saite tika sekmīgi atjaunināta. settings.link.error=Neizdevās atjaunināt glabātavas saiti. -settings.delete=Dzēst pakotni +settings.delete=Izdzēst pakotni settings.delete.description=Pakotne tiks neatgriezeniski izdzēsta. settings.delete.notice=Tiks izdzēsta pakotne %s (%s). Šī darbība ir neatgriezeniska. Tiešām turpināt? settings.delete.success=Pakotne tika izdzēsta. diff --git a/options/locale/locale_nb_NO.ini b/options/locale/locale_nb_NO.ini index 18c9835df0..2c8b5cfc64 100644 --- a/options/locale/locale_nb_NO.ini +++ b/options/locale/locale_nb_NO.ini @@ -1,5 +1,5 @@ [common] -enable_javascript = Denne nettsiden behøver JavaScript. +enable_javascript = Denne nettsiden krever JavaScript. toc = Innholdsfortegnelse licenses = Lisenser return_to_forgejo = Tilbake til Forgejo @@ -126,6 +126,13 @@ webauthn_sign_in = Trykk på knappen på sikkerhetsnøkkelen din. Dersom nøkkel copy_path = Kopier sti webauthn_error_unable_to_process = Tjeneren kunne ikke behandle forespørselen din. webauthn_error_empty = Du må gi nøkkelen et navn. +toggle_menu = Åpne/lukke meny +twofa_scratch = To-faktor skrapekode +webauthn_press_button = Vennligst trykk på knappen på sikkerhetsnøkkelen… +webauthn_error_duplicated = Sikkerhetsnøkkelen er ikke tillatt for denne forespørselen. Vennligst sørg for at nøkkelen ikke allerede er registrert. +webauthn_error_timeout = Et tidsavbrudd oppsto før nøkkelen din kunne leses. Vennligst last inn siden på nytt og prøv igjen. +new_fork = Ny fork av repository +collaborative = Samarbeidende [search] search = Søk... diff --git a/options/locale/locale_nl-NL.ini b/options/locale/locale_nl-NL.ini index 30d2c0ebdf..ac34b83f4e 100644 --- a/options/locale/locale_nl-NL.ini +++ b/options/locale/locale_nl-NL.ini @@ -681,6 +681,8 @@ To = Branch naam Website = Website AccessToken = Toegangstoken Pronouns = Voornaamwoorden +username_claiming_cooldown = De gebruikersnaam kan niet opgeëist worden, omdat de afkoelperiode nog niet voorbij is. Hij kan worden opgeëist op %[1]s. +email_domain_is_not_allowed = Het domein van het e-mailadres van de gebruiker %s is in strijd met EMAIL_DOMAIN_ALLOWLIST of EMAIL_DOMAIN_BLOCKLIST. Controleer of u het e-mailadres correct hebt ingesteld. [user] @@ -1058,6 +1060,8 @@ language.description = Deze taal wordt opgeslagen in uw account en wordt als sta language.localization_project = Help ons Forgejo in uw taal te vertalen! Leer meer. user_block_yourself = U kunt niet zichzelf blokkeren. pronouns_custom_label = Aangepaste voornaamwoorden +change_username_redirect_prompt.with_cooldown.few = De oude gebruikersnaam zal voor iedereen beschikbaar zijn na een afkoelperiode van %[1]d dagen. U kunt de oude gebruikersnaam nog steeds opeisen tijdens de afkoelperiode. +change_username_redirect_prompt.with_cooldown.one = De oude gebruikersnaam zal voor iedereen beschikbaar zijn na een afkoelperiode van %[1]d dag. U kunt de oude gebruikersnaam nog steeds opeisen tijdens de afkoelperiode. [repo] owner=Eigenaar @@ -2863,6 +2867,7 @@ summary_card_alt = Overzichtskaart van repository %s release.summary_card_alt = Samenvattende kaart van een release met de titel "%s" in repository %s issues.reaction.alt_remove = Verwijder %[1]s reactie van bericht. issues.reaction.alt_many = %[1]s en %[2]d meer gereageerd %[3]s. +editor.commit_email = Commit e-mail @@ -3001,6 +3006,8 @@ settings.visibility.limited = Beperkt (alleen zichtbaar voor ingelogde gebruiker teams.add_nonexistent_repo = De repository die u probeert toe te voegen bestaat niet, maak deze eerst aan alstublieft. teams.all_repositories_write_permission_desc = Dit team verleent Schrijf permissies tot alle repositories: leden kunnen lezen en pushen naar repositories. open_dashboard = Open dashboard +settings.change_orgname_redirect_prompt.with_cooldown.one = De oude gebruikersnaam zal voor iedereen beschikbaar zijn na een afkoelperiode van %[1]d dag. U kunt de oude gebruikersnaam nog steeds opeisen tijdens de afkoelperiode. +settings.change_orgname_redirect_prompt.with_cooldown.few = De oude gebruikersnaam zal voor iedereen beschikbaar zijn na een afkoelperiode van %[1]d dagen. U kunt de oude gebruikersnaam nog steeds opeisen tijdens de afkoelperiode. [admin] dashboard=Overzicht @@ -3083,7 +3090,7 @@ dashboard.gc_times=GC verwerkingen dashboard.delete_old_system_notices=Verwijder alle oude systeemmededelingen uit de database users.user_manage_panel=Gebruikersaccounts beheren -users.new_account=Nieuw account aanmaken +users.new_account=Gebruikersaccount aanmaken users.name=Gebruikersnaam users.full_name=Volledige naam users.activated=Geactiveerd diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini index f6cf73620c..5f4ebe389a 100644 --- a/options/locale/locale_pt-BR.ini +++ b/options/locale/locale_pt-BR.ini @@ -680,6 +680,8 @@ required_prefix = A entrada deve começar com "%s" FullName = Nome completo Description = Descrição unset_password = O usuário de login não definiu a senha. +username_claiming_cooldown = Este nome de usuário não pode ser registrado porque o período de espera ainda não acabou. Ele poderá ser registrado em %[1]s. +email_domain_is_not_allowed = O domínio do endereço de email da conta %s está em conflito com EMAIL_DOMAIN_ALLOWLIST ou EMAIL_DOMAIN_BLOCKLIST. Certifique-se de que você colocou o endereço de email correto. [user] @@ -805,7 +807,7 @@ old_password=Senha atual new_password=Nova senha retype_new_password=Confirme a nova senha password_incorrect=A senha atual está incorreta. -change_password_success=Sua senha foi atualizada. Acesse usando sua nova senha de agora em diante. +change_password_success=Sua senha foi atualizada. A partir de agora, use sua nova senha para acessar sua conta. password_change_disabled=Contas não-locais não podem alterar sua senha através da interface web do Forgejo. emails=Endereços de e-mail @@ -1057,6 +1059,8 @@ language.localization_project = Ajude-nos a traduzir Forgejo para o seu idioma! language.description = Essa língua será salva em sua conta e será usada como padrão após você iniciar a sessão. user_block_yourself = Você não pode se bloquear. pronouns_custom_label = Pronomes personalizados +change_username_redirect_prompt.with_cooldown.one = O nome de usuário antigo ficará disponível para qualquer pessoa após um período de espera de %[1]d dia, você ainda pode recuperar o nome de usuário antigo durante este período de espera. +change_username_redirect_prompt.with_cooldown.few = O nome de usuário antigo ficará disponível para qualquer pessoa após um período de espera de %[1]d dias, você ainda pode recuperar o nome de usuário antigo durante este período de espera. [repo] owner=Proprietário @@ -2619,7 +2623,7 @@ tag.create_tag_from=`Criar nova tag a partir de "%s"` tag.create_success=Tag "%s" criada. -topic.manage_topics=Gerenciar Tópicos +topic.manage_topics=Gerenciar tópicos topic.done=Feito topic.count_prompt=Você não pode selecionar mais de 25 tópicos @@ -3000,6 +3004,8 @@ open_dashboard = Abrir painel settings.change_orgname_prompt = Obs.: Alterar o nome de uma organização resultará na alteração do URL dela e disponibilizará o nome antigo para uso. follow_blocked_user = Não foi possível seguir esta organização porque ela bloqueou-o(a). form.name_pattern_not_allowed = O padrão "%s" não é permitido no nome de uma organização. +settings.change_orgname_redirect_prompt.with_cooldown.one = O nome de usuário antigo ficará disponível para qualquer pessoa após um período de espera de %[1]d dia, você ainda pode recuperar o nome de usuário antigo durante este período de espera. +settings.change_orgname_redirect_prompt.with_cooldown.few = O nome de usuário antigo ficará disponível para qualquer pessoa após um período de espera de %[1]d dias, você ainda pode recuperar o nome de usuário antigo durante este período de espera. [admin] dashboard=Painel @@ -3820,6 +3826,13 @@ owner.settings.cargo.initialize.description = É necessário um repositório Git owner.settings.chef.keypair.description = É necessário um par de chaves para autenticar no registro Chef. Se você já gerou um par de chaves, gere um novo par e descarte o antigo. container.images.title = Imagens search_in_external_registry = Buscar em %s +alt.registry.install = Para instalar o pacote, execute o seguinte comando: +alt.registry = Configurar este registro da linha de comando: +alt.install = Instalar pacote +alt.repository = Informação do repositório +alt.repository.architectures = Arquiteturas +alt.repository.multiple_groups = Este pacote está disponível em múltiplos grupos. +alt.setup = Adicionar um repositório à lista de repositórios conectados (escolha a arquitetura necessária em vez de '_arch_'): [secrets] secrets=Segredos diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini index ca1199dd54..d00c5810d4 100644 --- a/options/locale/locale_pt-PT.ini +++ b/options/locale/locale_pt-PT.ini @@ -681,6 +681,8 @@ AccessToken = Código de acesso FullName = Nome completo Description = Descrição Pronouns = Pronomes +username_claiming_cooldown = O nome de utilizador não pode ser reivindicado, porque o período de espera do mesmo ainda não terminou. Pode ser reivindicado em %[1]s. +email_domain_is_not_allowed = O domínio do endereço de email %s do utilizador entra em conflito com EMAIL_DOMAIN_ALLOWLIST ou EMAIL_DOMAIN_BLOCKLIST. Certifique-se de que definiu corretamente o endereço de email. [user] change_avatar=Mude o seu avatar… @@ -805,7 +807,7 @@ old_password=Senha corrente new_password=Nova senha retype_new_password=Confirme a nova senha password_incorrect=A senha corrente está errada. -change_password_success=A sua senha foi substituída. Inicie a sessão com a nova senha a partir de agora. +change_password_success=A sua senha foi atualizada. A partir de agora, utilize a sua nova senha para iniciar sessão. password_change_disabled=Os utilizadores não-locais não podem alterar a sua senha através da interface web do Forgejo. emails=Endereços de email @@ -1057,6 +1059,8 @@ language.description = Este idioma vai ser guardado na sua conta e ser usado com language.localization_project = Ajude-nos a traduzir o Forgejo para o seu idioma! Saiba mais. pronouns_custom_label = Pronomes personalizados user_block_yourself = Não se pode bloquear a si próprio. +change_username_redirect_prompt.with_cooldown.one = O nome de utilizador antigo estará disponível para todos após um período de espera de %[1]d dia, podendo ainda reivindicar o nome de utilizador antigo durante o período de espera. +change_username_redirect_prompt.with_cooldown.few = O nome de utilizador antigo ficará disponível para todos após um período de espera de %[1]d dias, podendo ainda reivindicar o nome de utilizador antigo durante o período de espera. [repo] new_repo_helper=Um repositório contém todos os ficheiros do trabalho, incluindo o histórico das revisões. Já tem um hospedado noutro sítio? Migre o repositório. @@ -3001,6 +3005,8 @@ teams.invite.by=Convidado(a) por %s teams.invite.description=Clique no botão abaixo para se juntar à equipa. follow_blocked_user = Não pode seguir esta organização porque esta organização bloqueou-o/a. open_dashboard = Abrir painel de controlo +settings.change_orgname_redirect_prompt.with_cooldown.one = O nome de utilizador antigo estará disponível para todos após um período de espera de %[1]d dia, podendo ainda reivindicar o nome de utilizador antigo durante o período de espera. +settings.change_orgname_redirect_prompt.with_cooldown.few = O nome de utilizador antigo estará disponível para todos após um período de espera de %[1]d dias, podendo ainda reivindicar o nome de utilizador antigo durante o período de espera. [admin] dashboard=Painel de controlo @@ -3821,6 +3827,13 @@ arch.version.backup = Cópia de segurança arch.version.replaces = Substitui container.images.title = Imagens search_in_external_registry = Procurar em %s +alt.registry = Configure este registo a partir da linha de comandos: +alt.registry.install = Para instalar o pacote, execute o seguinte comando: +alt.install = Instalar pacote +alt.repository = Informação do repositório +alt.repository.architectures = Arquiteturas +alt.repository.multiple_groups = Este pacote está disponível em vários grupos. +alt.setup = Adicionar um repositório à lista de repositórios ligados (escolha a arquitetura necessária em vez de '_arch_'): [secrets] secrets=Segredos diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini index 5ed8dae67b..de6d9292af 100644 --- a/options/locale/locale_ru-RU.ini +++ b/options/locale/locale_ru-RU.ini @@ -680,6 +680,8 @@ Biography = О себе Website = Веб-сайт Location = Местоположение To = Название ветви +email_domain_is_not_allowed = Домен адреса эл. почты %s не разрешён к использованию. Убедитесь, что он введён правильно или попробуйте другой адрес. +username_claiming_cooldown = Это имя пока не может быть занято, т.к. срок его защиты ещё не вышел. Его получится занять после %[1]s. [user] @@ -761,7 +763,7 @@ update_language_success=Язык обновлён. update_profile_success=Ваш профиль успешно обновлён. change_username=Ваше имя пользователя было изменено. change_username_prompt=Обратите внимание: изменение имени пользователя также меняет URL вашей учётной записи. -change_username_redirect_prompt=Старое имя пользователя будет перенаправлять на новое до тех пор, пока его не займут. +change_username_redirect_prompt=Старое имя будет перенаправлять на новое до тех пор, пока оно не будет занято. continue=Далее cancel=Отмена language=Язык @@ -1057,6 +1059,8 @@ language.description = Выбранный язык будет сохранён language.localization_project = Помогите с переводом Forgejo на свой язык! Подробнее. user_block_yourself = Нельзя заблокировать себя. pronouns_custom_label = Другие местоимения +change_username_redirect_prompt.with_cooldown.one = Прежнее имя будет доступно для использования другим пользователям после истечения защиты в %[1]d день. Вы сможете вернуть его себе во время срока защиты. +change_username_redirect_prompt.with_cooldown.few = Прежнее имя будет доступно для использования другим пользователям после истечения защиты в %[1]d дней. Вы сможете вернуть его себе во время срока защиты. [repo] owner=Владелец @@ -2650,7 +2654,7 @@ tag.create_tag_from=Создать новый тег из «%s» tag.create_success=Тег «%s» создан. -topic.manage_topics=Редактировать тематические метки +topic.manage_topics=Изменить темы topic.done=Сохранить topic.count_prompt=Нельзя выбрать более 25 тем topic.format_prompt=Темы должны начинаться с буквы или цифры и могут содержать дефисы («-») и точки («.»). Длина темы не должна превышать 35 символов. Все буквы должны быть строчными. @@ -3003,6 +3007,8 @@ teams.invite.description=Нажмите на кнопку ниже, чтобы follow_blocked_user = Вы не можете подписаться на эту организацию, т.к. вы в ней заблокированы. teams.general_access = Настраиваемый доступ open_dashboard = Открыть панель +settings.change_orgname_redirect_prompt.with_cooldown.few = Прежнее название будет доступно для использования другим пользователям после истечения защиты в %[1]d дней. Вы сможете вернуть его во время срока защиты. +settings.change_orgname_redirect_prompt.with_cooldown.one = Прежнее название будет доступно для использования другим пользователям после истечения защиты в %[1]d день. Вы сможете вернуть его во время срока защиты. [admin] dashboard=Панель управления @@ -3708,7 +3714,7 @@ composer.install=Чтобы установить пакет с помощью Co composer.dependencies=Зависимости composer.dependencies.development=Зависимости для разработки conan.details.repository=Репозиторий -conan.registry=Настроить реестр из командной строки: +conan.registry=Добавьте реестр командой: conan.install=Чтобы установить пакет с помощью Conan, выполните следующую команду: conda.registry=Пропишите этот реестр в качестве репозитория Conda в своём файле .condarc: conda.install=Чтобы установить пакет с помощью Conda, выполните следующую команду: @@ -3723,7 +3729,7 @@ container.labels.key=Ключ container.labels.value=Значение cran.registry=Настройте этот реестр в файле Rprofile.site: cran.install=Чтобы установить пакет, выполните следующую команду: -debian.registry=Настроить реестр из командной строки: +debian.registry=Добавьте реестр командой: debian.registry.info=Выберите $distribution и $component из списка ниже. debian.install=Чтобы установить пакет, выполните следующую команду: debian.repository=О репозитории @@ -3732,13 +3738,13 @@ debian.repository.components=Компоненты debian.repository.architectures=Архитектуры generic.download=Скачать пакет из командной строки: go.install=Установите пакет из командной строки: -helm.registry=Настроить реестр из командной строки: +helm.registry=Добавьте реестр командой: helm.install=Чтобы установить пакет, выполните следующую команду: maven.registry=Настройте реестр в файле pom.xml вашего проекта: maven.install=Чтобы использовать пакет, включите в блок dependencies в файле pom.xml следующее: maven.install2=Выполнить через командную строку: maven.download=Чтобы скачать зависимость, запустите в командной строке: -nuget.registry=Настроить реестр из командной строки: +nuget.registry=Добавьте реестр командой: nuget.install=Чтобы установить пакет с помощью NuGet, выполните следующую команду: nuget.dependency.framework=Целевой фреймворк npm.registry=Настройте реестр в файле .npmrc вашего проекта: @@ -3752,7 +3758,7 @@ npm.details.tag=Тег pub.install=Чтобы установить пакет с помощью Dart, выполните следующую команду: pypi.requires=Требуется Python pypi.install=Чтобы установить пакет с помощью pip, выполните следующую команду: -rpm.registry=Настроить реестр из командной строки: +rpm.registry=Добавьте реестр командой: rpm.distros.redhat=на дистрибутивах семейства RedHat rpm.distros.suse=на дистрибутивах семейства SUSE rpm.install=Чтобы установить пакет, выполните следующую команду: @@ -3764,7 +3770,7 @@ rubygems.dependencies.runtime=Зависимости времени выполн rubygems.dependencies.development=Зависимости для разработки rubygems.required.ruby=Требуется версия Ruby rubygems.required.rubygems=Требуется версия RubyGem -swift.registry=Настроить реестр из командной строки: +swift.registry=Добавьте реестр командой: swift.install=Добавьте пакет в свой файл Package.swift: swift.install2=и запустите следующую команду: vagrant.install=Чтобы добавить бокс Vagrant, выполните следующую команду: @@ -3834,6 +3840,9 @@ container.images.title = Образы search_in_external_registry = Найти в %s alt.repository = О репозитории alt.repository.architectures = Архитектуры +alt.registry = Добавьте реестр командой: +alt.repository.multiple_groups = Этот пакет доступен в нескольких группах. +alt.setup = Добавьте репозиторий в свой список репозиториев (выберите подходящую архитектуру вместо «_arch_»): [secrets] secrets=Секреты diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini index 472ce499f1..a9aee2c373 100644 --- a/options/locale/locale_uk-UA.ini +++ b/options/locale/locale_uk-UA.ini @@ -666,6 +666,8 @@ invalid_group_team_map_error = ` призначення недійсне: %s` unsupported_login_type = Цей тип входу не підтримує видалення облікового запису. admin_cannot_delete_self = Ви не можете видалити себе, якщо ви є адміністратором. Спочатку зніміть із себе права адміністратора. unset_password = Для користувача не встановлено пароль. +username_claiming_cooldown = Це ім'я користувача не можна присвоїти, оскільки його період захисту ще не закінчився. Ім'я можна буде присвоїти %[1]s. +email_domain_is_not_allowed = Домен адреси електронної пошти %s конфліктує з EMAIL_DOMAIN_ALLOWLIST або EMAIL_DOMAIN_BLOCKLIST. Перевірте, чи ви правильно вказали адресу електронної пошти. [user] @@ -761,7 +763,7 @@ update_password=Оновити пароль old_password=Поточний пароль new_password=Новий пароль password_incorrect=Поточний пароль неправильний. -change_password_success=Ваш пароль був оновлений. Тепер увійдіть в систему, використовуючи новий пароль. +change_password_success=Ваш пароль оновлено. Відтепер входьте в систему, використовуючи новий пароль. password_change_disabled=Нелокальні акаунти не можуть змінити пароль через Forgejo. emails=Адреса електронної пошти @@ -1006,6 +1008,9 @@ key_signature_ssh_placeholder = Починається з «-----BEGIN SSH SIGNA user_block_yourself = Ви не можете заблокувати себе. pronouns_custom_label = Інші займенники repo_and_org_access = Доступ до репозиторію та організації +change_username_redirect_prompt.with_cooldown.few = Старе ім'я користувача буде доступне всім після періоду захисту, який триватиме %[1]d днів. Протягом періоду захисту ви ще можете повернути собі старе ім'я. +change_username_redirect_prompt.with_cooldown.one = Старе ім'я користувача буде доступне всім після періоду захисту, який триватиме %[1]d день. Протягом періоду захисту ви ще можете повернути собі старе ім'я. +change_username_redirect_prompt = Старе ім'я користувача буде перенаправленням, поки хтось не присвоїть ім'я собі. [repo] owner=Власник @@ -2595,6 +2600,8 @@ follow_blocked_user = Ви не можете стежити за цією орг teams.invite.description = Щоб приєднатися до команди, натисніть кнопку нижче. teams.invite.title = Вас запрошено приєднатися до команди %s в організації %s. form.name_reserved = Назву організації «%s» зарезервовано. +settings.change_orgname_redirect_prompt.with_cooldown.one = Стара назва буде доступна всім після періоду захисту, який триватиме %[1]d день. Протягом періоду захисту ви ще можете повернути стару назву. +settings.change_orgname_redirect_prompt.with_cooldown.few = Стара назва буде доступна всім після періоду захисту, який триватиме %[1]d днів. Протягом періоду захисту ви ще можете повернути стару назву. [admin] dashboard=Панель управління diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index b17d85cffa..a527197ce3 100644 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -680,6 +680,8 @@ AccessToken = 访问令牌 Description = 描述 Pronouns = 代称 Biography = 简历 +username_claiming_cooldown = 用户名不能被认领,因为其仍处于冷却期间。其可以在%[1]s后被认领。 +email_domain_is_not_allowed = 用户电子邮箱的域名%s与EMAIL_DOMAIN_ALLOWLIST或EMAIL_DOMAIN_BLOCKLIST冲突。请确保您正确设置了电子邮件地址。 [user] change_avatar=修改头像… @@ -804,7 +806,7 @@ old_password=当前密码 new_password=新的密码 retype_new_password=确认新密码 password_incorrect=当前密码不正确。 -change_password_success=您的密码已更新。从现在开始使用您的新密码登录。 +change_password_success=您的密码已更新。从现在开始请使用您的新密码登录。 password_change_disabled=非本地帐户不能通过 Forgejo 的 web 界面更改密码。 emails=邮箱地址 @@ -1056,6 +1058,8 @@ language.description = 此语言将保存到您的账号中,并在您登录后 language.localization_project = 帮助我们将 Forgejo 翻译成您的语言!了解更多。 user_block_yourself = 您不能屏蔽自己。 pronouns_custom_label = 自定义代词 +change_username_redirect_prompt.with_cooldown.one = 旧的用户名将在%[1]d天的冷却期后对所有人可用,您仍可以在此期间重新认领旧的用户名。 +change_username_redirect_prompt.with_cooldown.few = 旧的用户名将在%[1]d天的冷却期后对所有人可用,您仍可以在此期间重新认领旧的用户名。 [repo] new_repo_helper=代码仓库包含了所有的项目文件,包括版本历史记录。已经在其他地方托管了?迁移仓库。 @@ -2866,6 +2870,7 @@ issues.context.menu = 评论菜单 issues.reaction.alt_add = 对评论添加 %[1]s 回应。 release.summary_card_alt = 仓库 %[2]s 中标题为 %[1]s 的版本发布的摘要卡片 summary_card_alt = 仓库 %s 的摘要卡片 +editor.commit_email = 提交电子邮件 [graphs] component_loading=正在加载 %s… @@ -3001,6 +3006,8 @@ teams.invite.by=邀请人 %s teams.invite.description=请点击下面的按钮加入团队。 follow_blocked_user = 你无法关注此组织,因为此组织已屏蔽你。 open_dashboard = 打开仪表盘 +settings.change_orgname_redirect_prompt.with_cooldown.one = 旧的用户名将在%[1]d天的冷却期后对所有人可用,您仍可以在此期间重新认领旧的用户名。 +settings.change_orgname_redirect_prompt.with_cooldown.few = 旧的用户名将在%[1]d天的冷却期后对所有人可用,您仍可以在此期间重新认领旧的用户名。 [admin] dashboard=管理面板 diff --git a/package-lock.json b/package-lock.json index 33ff9157ca..21596d1f45 100644 --- a/package-lock.json +++ b/package-lock.json @@ -62,7 +62,7 @@ "devDependencies": { "@axe-core/playwright": "4.10.1", "@eslint-community/eslint-plugin-eslint-comments": "4.4.1", - "@playwright/test": "1.49.1", + "@playwright/test": "1.50.1", "@stoplight/spectral-cli": "6.14.2", "@stylistic/eslint-plugin-js": "2.12.1", "@stylistic/stylelint-plugin": "3.1.1", @@ -86,9 +86,9 @@ "eslint-plugin-vue-scoped-css": "2.9.0", "eslint-plugin-wc": "2.2.0", "globals": "15.14.0", - "happy-dom": "16.3.0", + "happy-dom": "16.8.1", "license-checker-rseidelsohn": "4.4.2", - "markdownlint-cli": "0.43.0", + "markdownlint-cli": "0.44.0", "postcss-html": "1.8.0", "stylelint": "16.12.0", "stylelint-declaration-block-no-ignored-properties": "2.8.0", @@ -3350,13 +3350,13 @@ } }, "node_modules/@playwright/test": { - "version": "1.49.1", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.49.1.tgz", - "integrity": "sha512-Ky+BVzPz8pL6PQxHqNRW1k3mIyv933LML7HktS8uik0bUXNCdPhoS/kLihiO1tMf/egaJb4IutXd7UywvXEW+g==", + "version": "1.50.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.50.1.tgz", + "integrity": "sha512-Jii3aBg+CEDpgnuDxEp/h7BimHcUTDlpEtce89xEumlJ5ef2hqepZ+PWp1DDpYC/VO9fmWVI1IlEaoI5fK9FXQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright": "1.49.1" + "playwright": "1.50.1" }, "bin": { "playwright": "cli.js" @@ -4533,6 +4533,16 @@ "@types/d3-selection": "*" } }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, "node_modules/@types/doctrine": { "version": "0.0.9", "resolved": "https://registry.npmjs.org/@types/doctrine/-/doctrine-0.0.9.tgz", @@ -4594,6 +4604,13 @@ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "license": "MIT" }, + "node_modules/@types/katex": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.7.tgz", + "integrity": "sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/markdown-escape": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@types/markdown-escape/-/markdown-escape-1.1.3.tgz", @@ -4607,6 +4624,13 @@ "integrity": "sha512-a79Yc3TOk6dGdituy8hmTTJXjOkZ7zsFYV10L337ttq/rec8lRMDBpV7fL3uLx6TgbFCa5DU/h8FmIBQPSbU0w==", "license": "MIT" }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/node": { "version": "22.10.7", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.7.tgz", @@ -4646,6 +4670,13 @@ "license": "MIT", "optional": true }, + "node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/urijs": { "version": "1.19.25", "resolved": "https://registry.npmjs.org/@types/urijs/-/urijs-1.19.25.tgz", @@ -6143,6 +6174,39 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/chart.js": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.5.tgz", @@ -7271,6 +7335,20 @@ } } }, + "node_modules/decode-named-character-reference": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", + "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/decode-uri-component": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", @@ -7363,6 +7441,30 @@ "node": ">= 0.6.0" } }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", @@ -9155,9 +9257,9 @@ } }, "node_modules/happy-dom": { - "version": "16.3.0", - "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-16.3.0.tgz", - "integrity": "sha512-Q71RaIhyS21vhW17Tpa5W36yqQXIlE1TZ0A0Gguts8PShUSQE/7fBgxYGxgm3+5y0gF6afdlAVHLQqgrIcfRzg==", + "version": "16.8.1", + "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-16.8.1.tgz", + "integrity": "sha512-n0QrmT9lD81rbpKsyhnlz3DgnMZlaOkJPpgi746doA+HvaMC79bdWkwjrNnGJRvDrWTI8iOcJiVTJ5CdT/AZRw==", "dev": true, "license": "MIT", "dependencies": { @@ -9527,6 +9629,32 @@ "node": ">=10.13.0" } }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-array-buffer": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", @@ -9704,6 +9832,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -9783,6 +9922,17 @@ "node": ">=0.10.0" } }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-js-type": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-js-type/-/is-js-type-3.0.0.tgz", @@ -10796,14 +10946,21 @@ } }, "node_modules/markdownlint": { - "version": "0.36.1", - "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.36.1.tgz", - "integrity": "sha512-s73fU2CQN7WCgjhaQUQ8wYESQNzGRNOKDd+3xgVqu8kuTEhmwepd/mxOv1LR2oV046ONrTLBFsM7IoKWNvmy5g==", + "version": "0.37.4", + "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.37.4.tgz", + "integrity": "sha512-u00joA/syf3VhWh6/ybVFkib5Zpj2e5KB/cfCei8fkSRuums6nyisTWGqjTWIOFoFwuXoTBQQiqlB4qFKp8ncQ==", "dev": true, "license": "MIT", "dependencies": { "markdown-it": "14.1.0", - "markdownlint-micromark": "0.1.12" + "micromark": "4.0.1", + "micromark-core-commonmark": "2.0.2", + "micromark-extension-directive": "3.0.2", + "micromark-extension-gfm-autolink-literal": "2.1.0", + "micromark-extension-gfm-footnote": "2.1.0", + "micromark-extension-gfm-table": "2.1.0", + "micromark-extension-math": "3.1.0", + "micromark-util-types": "2.0.1" }, "engines": { "node": ">=18" @@ -10813,20 +10970,20 @@ } }, "node_modules/markdownlint-cli": { - "version": "0.43.0", - "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.43.0.tgz", - "integrity": "sha512-6vwurKK4B21eyYzwgX6ph13cZS7hE6LZfcS8QyD722CyxVD2RtAvbZK2p7k+FZbbKORulEuwl+hJaEq1l6/hoQ==", + "version": "0.44.0", + "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.44.0.tgz", + "integrity": "sha512-ZJTAONlvF9NkrIBltCdW15DxN9UTbPiKMEqAh2EU2gwIFlrCMavyCEPPO121cqfYOrLUJWW8/XKWongstmmTeQ==", "dev": true, "license": "MIT", "dependencies": { - "commander": "~12.1.0", - "glob": "~11.0.0", - "ignore": "~6.0.2", - "js-yaml": "^4.1.0", + "commander": "~13.1.0", + "glob": "~10.4.5", + "ignore": "~7.0.3", + "js-yaml": "~4.1.0", "jsonc-parser": "~3.3.1", - "jsonpointer": "5.0.1", - "markdownlint": "~0.36.1", - "minimatch": "~10.0.1", + "jsonpointer": "~5.0.1", + "markdownlint": "~0.37.4", + "minimatch": "~9.0.5", "run-con": "~1.3.2", "smol-toml": "~1.3.1" }, @@ -10838,9 +10995,9 @@ } }, "node_modules/markdownlint-cli/node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", "dev": true, "license": "MIT", "engines": { @@ -10848,55 +11005,36 @@ } }, "node_modules/markdownlint-cli/node_modules/glob": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz", - "integrity": "sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^4.0.1", - "minimatch": "^10.0.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", - "path-scurry": "^2.0.0" + "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" }, - "engines": { - "node": "20 || >=22" - }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/markdownlint-cli/node_modules/ignore": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-6.0.2.tgz", - "integrity": "sha512-InwqeHHN2XpumIkMvpl/DCJVrAHgCsG5+cn1XlnLWGwtZBm8QJfSusItfrwx81CTp5agNZqpKU2J/ccC5nGT4A==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.3.tgz", + "integrity": "sha512-bAH5jbK/F3T3Jls4I0SO1hmPR0dKU0a7+SY6n1yzRtG54FLO8d6w/nxLFX2Nb7dBu6cCWXPaAME6cYqFUMmuCA==", "dev": true, "license": "MIT", "engines": { "node": ">= 4" } }, - "node_modules/markdownlint-cli/node_modules/jackspeak": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz", - "integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/markdownlint-cli/node_modules/jsonc-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", @@ -10904,46 +11042,22 @@ "dev": true, "license": "MIT" }, - "node_modules/markdownlint-cli/node_modules/lru-cache": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.2.tgz", - "integrity": "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==", + "node_modules/markdownlint-cli/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "license": "ISC", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/markdownlint-cli/node_modules/path-scurry": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", - "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", - "dev": true, - "license": "BlueOak-1.0.0", "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "20 || >=22" + "node": ">=16 || 14 >=14.17" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/markdownlint-micromark": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/markdownlint-micromark/-/markdownlint-micromark-0.1.12.tgz", - "integrity": "sha512-RlB6EwMGgc0sxcIhOQ2+aq7Zw1V2fBnzbXKGgYK/mVWdT7cz34fteKSwfYeo4rL6+L/q2tyC9QtD/PgZbkdyJQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/DavidAnson" - } - }, "node_modules/marked": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", @@ -11059,6 +11173,542 @@ "node": ">= 18" } }, + "node_modules/micromark": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.1.tgz", + "integrity": "sha512-eBPdkcoCNvYcxQOAKAlceo5SNdzZWfF+FcSupREAzdAh9rRmE239CEQAiTwIgblwnoM8zzj35sZ5ZwvSEOF6Kw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.2.tgz", + "integrity": "sha512-FKjQKbxd1cibWMM1P9N+H8TwlgGgSkWZMmfuVucLCHaYqeSvJ0hFeHsIa65pA2nYbes0f8LDHPMrd9X7Ujxg9w==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-directive": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-3.0.2.tgz", + "integrity": "sha512-wjcXHgk+PPdmvR58Le9d7zQYWy+vKEU9Se44p2CrCDPiLr2FMyiT4Fyb5UFKFC66wGB3kPlgD7q3TnoqPS7SZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "parse-entities": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "dev": true, + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.0.tgz", + "integrity": "sha512-Ub2ncQv+fwD70/l4ou27b4YzfNaCJOvyX4HxXU15m7mpYY+rjuWzsLIPZHJL253Z643RpbcP1oeIJlQ/SKW67g==", + "dev": true, + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-math": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-math/-/micromark-extension-math-3.1.0.tgz", + "integrity": "sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/katex": "^0.16.0", + "devlop": "^1.0.0", + "katex": "^0.16.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.4.tgz", + "integrity": "sha512-N6hXjrin2GTJDe3MVjf5FuXpm12PGm80BrUAeub9XFXca8JZbP+oIwY4LJSVwFUCL1IPm/WwSVUN7goFHmSGGQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.1.tgz", + "integrity": "sha512-534m2WhVTddrcKVepwmVEVnUAmtrx9bfIjNoQHRqfnvdaHQiFytEhJoTgpWJvDEXCO5gLTQh3wYC1PgOJA4NSQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", @@ -11581,6 +12231,26 @@ "node": ">=6" } }, + "node_modules/parse-entities": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -11817,13 +12487,13 @@ } }, "node_modules/playwright": { - "version": "1.49.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.1.tgz", - "integrity": "sha512-VYL8zLoNTBxVOrJBbDuRgDWa3i+mfQgDTrL8Ah9QXZ7ax4Dsj0MSq5bYgytRnDVVe+njoKnfsYkH3HzqVj5UZA==", + "version": "1.50.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.50.1.tgz", + "integrity": "sha512-G8rwsOQJ63XG6BbKj2w5rHeavFjy5zynBA9zsJMMtBoe/Uf757oG12NXz6e6OirF7RCrTVAKFXbLmn1RbL7Qaw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.49.1" + "playwright-core": "1.50.1" }, "bin": { "playwright": "cli.js" @@ -11836,9 +12506,9 @@ } }, "node_modules/playwright-core": { - "version": "1.49.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.1.tgz", - "integrity": "sha512-BzmpVcs4kE2CH15rWfzpjzVGhWERJfmnXmniSyKeRZUs9Ws65m+RGIi7mjJK/euCegfn3i7jvqWeWyHe9y3Vgg==", + "version": "1.50.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.50.1.tgz", + "integrity": "sha512-ra9fsNWayuYumt+NiM069M6OkcRb1FZSK8bgi66AtpFoWkg2+y0bJSNmkFrWhMbEBbVKC/EruAHH3g0zmtwGmQ==", "dev": true, "license": "Apache-2.0", "bin": { diff --git a/package.json b/package.json index 95129444fb..0736be5026 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "devDependencies": { "@axe-core/playwright": "4.10.1", "@eslint-community/eslint-plugin-eslint-comments": "4.4.1", - "@playwright/test": "1.49.1", + "@playwright/test": "1.50.1", "@stoplight/spectral-cli": "6.14.2", "@stylistic/eslint-plugin-js": "2.12.1", "@stylistic/stylelint-plugin": "3.1.1", @@ -85,9 +85,9 @@ "eslint-plugin-vue-scoped-css": "2.9.0", "eslint-plugin-wc": "2.2.0", "globals": "15.14.0", - "happy-dom": "16.3.0", + "happy-dom": "16.8.1", "license-checker-rseidelsohn": "4.4.2", - "markdownlint-cli": "0.43.0", + "markdownlint-cli": "0.44.0", "postcss-html": "1.8.0", "stylelint": "16.12.0", "stylelint-declaration-block-no-ignored-properties": "2.8.0", diff --git a/renovate.json b/renovate.json index c34ae1aaba..7efe4123b0 100644 --- a/renovate.json +++ b/renovate.json @@ -62,6 +62,7 @@ "description": "Group nodejs packages", "matchPackageNames": [ "code.forgejo.org/oci/node", + "data.forgejo.org/oci/node", "docker.io/library/node", "docker.io/node", "node" diff --git a/routers/api/v1/misc/markup_test.go b/routers/api/v1/misc/markup_test.go index df70ee49ef..32568b805f 100644 --- a/routers/api/v1/misc/markup_test.go +++ b/routers/api/v1/misc/markup_test.go @@ -112,7 +112,7 @@ Here are some links to the most important topics. You can find the full list of

Here are some links to the most important topics. You can find the full list of pages at the sidebar.

Configuration -images/icon-bug.png

+

`, } diff --git a/routers/api/v1/user/gpg_key.go b/routers/api/v1/user/gpg_key.go index 2fe4eb8e78..652007a78b 100644 --- a/routers/api/v1/user/gpg_key.go +++ b/routers/api/v1/user/gpg_key.go @@ -303,11 +303,7 @@ func DeleteGPGKey(ctx *context.APIContext) { } if err := asymkey_model.DeleteGPGKey(ctx, ctx.Doer, ctx.ParamsInt64(":id")); err != nil { - if asymkey_model.IsErrGPGKeyAccessDenied(err) { - ctx.Error(http.StatusForbidden, "", "You do not have access to this key") - } else { - ctx.Error(http.StatusInternalServerError, "DeleteGPGKey", err) - } + ctx.Error(http.StatusInternalServerError, "DeleteGPGKey", err) return } @@ -317,8 +313,6 @@ func DeleteGPGKey(ctx *context.APIContext) { // HandleAddGPGKeyError handle add GPGKey error func HandleAddGPGKeyError(ctx *context.APIContext, err error, token string) { switch { - case asymkey_model.IsErrGPGKeyAccessDenied(err): - ctx.Error(http.StatusUnprocessableEntity, "GPGKeyAccessDenied", "You do not have access to this GPG key") case asymkey_model.IsErrGPGKeyIDAlreadyUsed(err): ctx.Error(http.StatusUnprocessableEntity, "GPGKeyIDAlreadyUsed", "A key with the same id already exists") case asymkey_model.IsErrGPGKeyParsing(err): diff --git a/routers/web/admin/users.go b/routers/web/admin/users.go index 6bfc35cb99..36ce8d286c 100644 --- a/routers/web/admin/users.go +++ b/routers/web/admin/users.go @@ -83,6 +83,7 @@ func Users(ctx *context.Context) { IsTwoFactorEnabled: util.OptionalBoolParse(statusFilterMap["is_2fa_enabled"]), IsProhibitLogin: util.OptionalBoolParse(statusFilterMap["is_prohibit_login"]), IncludeReserved: true, // administrator needs to list all accounts include reserved, bot, remote ones + Load2FAStatus: true, ExtraParamStrings: extraParamStrings, }, tplUsers) } diff --git a/routers/web/auth/auth.go b/routers/web/auth/auth.go index e956dcf496..8487351e5d 100644 --- a/routers/web/auth/auth.go +++ b/routers/web/auth/auth.go @@ -228,15 +228,6 @@ func SignInPost(ctx *context.Context) { log.Warn("Failed authentication attempt for %s from %s: %v", form.UserName, ctx.RemoteAddr(), err) ctx.Data["Title"] = ctx.Tr("auth.prohibit_login") ctx.HTML(http.StatusOK, "user/auth/prohibit_login") - } else if user_model.IsErrUserInactive(err) { - if setting.Service.RegisterEmailConfirm { - ctx.Data["Title"] = ctx.Tr("auth.active_your_account") - ctx.HTML(http.StatusOK, TplActivate) - } else { - log.Warn("Failed authentication attempt for %s from %s: %v", form.UserName, ctx.RemoteAddr(), err) - ctx.Data["Title"] = ctx.Tr("auth.prohibit_login") - ctx.HTML(http.StatusOK, "user/auth/prohibit_login") - } } else { ctx.ServerError("UserSignIn", err) } diff --git a/routers/web/auth/linkaccount.go b/routers/web/auth/linkaccount.go index 9b0141c14e..8dce0a30a4 100644 --- a/routers/web/auth/linkaccount.go +++ b/routers/web/auth/linkaccount.go @@ -99,16 +99,6 @@ func handleSignInError(ctx *context.Context, userName string, ptrForm any, tmpl log.Info("Failed authentication attempt for %s from %s: %v", userName, ctx.RemoteAddr(), err) ctx.Data["Title"] = ctx.Tr("auth.prohibit_login") ctx.HTML(http.StatusOK, "user/auth/prohibit_login") - } else if user_model.IsErrUserInactive(err) { - ctx.Data["user_exists"] = true - if setting.Service.RegisterEmailConfirm { - ctx.Data["Title"] = ctx.Tr("auth.active_your_account") - ctx.HTML(http.StatusOK, TplActivate) - } else { - log.Info("Failed authentication attempt for %s from %s: %v", userName, ctx.RemoteAddr(), err) - ctx.Data["Title"] = ctx.Tr("auth.prohibit_login") - ctx.HTML(http.StatusOK, "user/auth/prohibit_login") - } } else { ctx.ServerError(invoker, err) } diff --git a/routers/web/explore/org.go b/routers/web/explore/org.go index 7178630b64..d13271ae53 100644 --- a/routers/web/explore/org.go +++ b/routers/web/explore/org.go @@ -39,7 +39,11 @@ func Organizations(ctx *context.Context) { ) sortOrder := ctx.FormString("sort") if sortOrder == "" { - sortOrder = "newest" + if supportedSortOrders.Contains(setting.UI.ExploreDefaultSort) { + sortOrder = setting.UI.ExploreDefaultSort + } else { + sortOrder = "newest" + } ctx.SetFormString("sort", sortOrder) } diff --git a/routers/web/explore/user.go b/routers/web/explore/user.go index 15c60f546f..241e5f61a1 100644 --- a/routers/web/explore/user.go +++ b/routers/web/explore/user.go @@ -114,7 +114,9 @@ func RenderUserSearch(ctx *context.Context, opts *user_model.SearchUserOptions, ctx.Data["Keyword"] = opts.Keyword ctx.Data["Total"] = count ctx.Data["Users"] = users - ctx.Data["UsersTwoFaStatus"] = user_model.UserList(users).GetTwoFaStatus(ctx) + if opts.Load2FAStatus { + ctx.Data["UsersTwoFaStatus"] = user_model.UserList(users).GetTwoFaStatus(ctx) + } ctx.Data["ShowUserEmail"] = setting.UI.ShowUserEmail ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled @@ -149,7 +151,11 @@ func Users(ctx *context.Context) { ) sortOrder := ctx.FormString("sort") if sortOrder == "" { - sortOrder = "newest" + if supportedSortOrders.Contains(setting.UI.ExploreDefaultSort) { + sortOrder = setting.UI.ExploreDefaultSort + } else { + sortOrder = "newest" + } ctx.SetFormString("sort", sortOrder) } diff --git a/routers/web/repo/blame.go b/routers/web/repo/blame.go index 4f962d4c19..c7fbaaefcb 100644 --- a/routers/web/repo/blame.go +++ b/routers/web/repo/blame.go @@ -56,6 +56,11 @@ func RefBlame(ctx *context.Context) { HandleGitError(ctx, "Repo.Commit.GetTreeEntryByPath", err) return } + if entry.IsDir() { + ctx.NotFound("Cannot blame directory", nil) + return + } + blob := entry.Blob() ctx.Data["PageIsViewCode"] = true diff --git a/services/context/repo.go b/services/context/repo.go index d294c00455..ff03844c03 100644 --- a/services/context/repo.go +++ b/services/context/repo.go @@ -937,6 +937,9 @@ func getRefName(ctx *Base, repo *Repository, pathType RepoRefType) string { // of repository reference func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context) context.CancelFunc { return func(ctx *Context) (cancel context.CancelFunc) { + if ctx.Repo.Repository.IsBeingCreated() { + return nil // no git repo, so do nothing + } // Empty repository does not have reference information. if ctx.Repo.Repository.IsEmpty { // assume the user is viewing the (non-existent) default branch diff --git a/templates/install.tmpl b/templates/install.tmpl index ae800df130..7a9b40826f 100644 --- a/templates/install.tmpl +++ b/templates/install.tmpl @@ -363,5 +363,5 @@ - +{{ctx.Locale.Tr {{template "base/footer" .}} diff --git a/templates/repo/diff/image_diff.tmpl b/templates/repo/diff/image_diff.tmpl index 0612854609..a793f54da1 100644 --- a/templates/repo/diff/image_diff.tmpl +++ b/templates/repo/diff/image_diff.tmpl @@ -22,7 +22,7 @@ {{if .blobBase}}

{{ctx.Locale.Tr "repo.diff.file_before"}}

- +

{{ctx.Locale.Tr "repo.diff.file_image_width"}}: @@ -37,7 +37,7 @@ {{if .blobHead}}

{{ctx.Locale.Tr "repo.diff.file_after"}}

- +

{{ctx.Locale.Tr "repo.diff.file_image_width"}}: @@ -55,9 +55,9 @@

- + {{ctx.Locale.Tr - + {{ctx.Locale.Tr @@ -70,8 +70,8 @@
- - + {{ctx.Locale.Tr + {{ctx.Locale.Tr
diff --git a/templates/repo/diff/new_review.tmpl b/templates/repo/diff/new_review.tmpl index a2eae007a5..13d09babe1 100644 --- a/templates/repo/diff/new_review.tmpl +++ b/templates/repo/diff/new_review.tmpl @@ -1,9 +1,16 @@
- +
+ +
{{if $.IsShowingAllCommits}}
diff --git a/templates/repo/editor/commit_form.tmpl b/templates/repo/editor/commit_form.tmpl index fc04289b70..c42eed69a5 100644 --- a/templates/repo/editor/commit_form.tmpl +++ b/templates/repo/editor/commit_form.tmpl @@ -67,7 +67,7 @@ {{end}}
- + - + {{ctx.Locale.Tr
diff --git a/templates/user/dashboard/feeds.tmpl b/templates/user/dashboard/feeds.tmpl index bd2a3800a2..8ae50bf385 100644 --- a/templates/user/dashboard/feeds.tmpl +++ b/templates/user/dashboard/feeds.tmpl @@ -91,7 +91,7 @@ {{range $push.Commits}} {{$commitLink := printf "%s/commit/%s" $repoLink .Sha1}}
- + {{ShortSha .Sha1}} {{RenderCommitMessage $.Context .Message ($repo.ComposeMetas ctx)}} @@ -103,11 +103,11 @@ {{ctx.Locale.Tr "action.compare_commits" $push.Len}} » {{end}} {{else if .GetOpType.InActions "create_issue"}} - {{index .GetIssueInfos 1 | RenderEmoji $.Context | RenderCodeBlock}} + {{RenderIssueTitle ctx (index .GetIssueInfos 1) (.Repo.ComposeMetas ctx)}} {{else if .GetOpType.InActions "create_pull_request"}} - {{index .GetIssueInfos 1 | RenderEmoji $.Context | RenderCodeBlock}} + {{RenderIssueTitle ctx (index .GetIssueInfos 1) (.Repo.ComposeMetas ctx)}} {{else if .GetOpType.InActions "comment_issue" "approve_pull_request" "reject_pull_request" "comment_pull"}} - {{(.GetIssueTitle ctx) | RenderEmoji $.Context | RenderCodeBlock}} + {{RenderIssueTitle ctx (.GetIssueTitle ctx) (.Repo.ComposeMetas ctx)}} {{$comment := index .GetIssueInfos 1}} {{if $comment}}
{{RenderMarkdownToHtml ctx $comment}}
@@ -115,7 +115,7 @@ {{else if .GetOpType.InActions "merge_pull_request"}}
{{index .GetIssueInfos 1}}
{{else if .GetOpType.InActions "close_issue" "reopen_issue" "close_pull_request" "reopen_pull_request"}} - {{(.GetIssueTitle ctx) | RenderEmoji $.Context | RenderCodeBlock}} + {{RenderIssueTitle ctx (.GetIssueTitle ctx) (.Repo.ComposeMetas ctx)}} {{else if .GetOpType.InActions "pull_review_dismissed"}}
{{ctx.Locale.Tr "action.review_dismissed_reason"}}
{{index .GetIssueInfos 2 | RenderEmoji $.Context}}
diff --git a/tests/e2e/repo-home.e2e.ts b/tests/e2e/repo-home.e2e.ts new file mode 100644 index 0000000000..fbcfe17226 --- /dev/null +++ b/tests/e2e/repo-home.e2e.ts @@ -0,0 +1,19 @@ +// @watch start +// web_src/js/features/common-global.js +// web_src/css/repo.css +// @watch end + +import {expect} from '@playwright/test'; +import {save_visual, test} from './utils_e2e.ts'; + +test('Language stats bar', async ({page}) => { + const response = await page.goto('/user2/repo1'); + expect(response?.status()).toBe(200); + + await expect(page.locator('#language-stats-legend')).toBeVisible(); + await save_visual(page); + + await page.click('#language-stats-bar'); + await expect(page.locator('#language-stats-legend')).toBeHidden(); + await save_visual(page); +}); diff --git a/tests/integration/admin_user_test.go b/tests/integration/admin_user_test.go index 93499e9139..6e0499d949 100644 --- a/tests/integration/admin_user_test.go +++ b/tests/integration/admin_user_test.go @@ -26,13 +26,36 @@ import ( func TestAdminViewUsers(t *testing.T) { defer tests.PrepareTestEnv(t)() - session := loginUser(t, "user1") - req := NewRequest(t, "GET", "/admin/users") - session.MakeRequest(t, req, http.StatusOK) + t.Run("Admin user", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() - session = loginUser(t, "user2") - req = NewRequest(t, "GET", "/admin/users") - session.MakeRequest(t, req, http.StatusForbidden) + session := loginUser(t, "user1") + req := NewRequest(t, "GET", "/admin/users") + session.MakeRequest(t, req, http.StatusOK) + + req = NewRequest(t, "GET", "/admin/users?status_filter[is_2fa_enabled]=1") + resp := session.MakeRequest(t, req, http.StatusOK) + htmlDoc := NewHTMLParser(t, resp.Body) + + // 6th column is the 2FA column. + // One user that has TOTP and another user that has WebAuthn. + assert.EqualValues(t, 2, htmlDoc.Find(".admin-setting-content table tbody tr td:nth-child(6) .octicon-check").Length()) + }) + + t.Run("Normal user", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + session := loginUser(t, "user2") + req := NewRequest(t, "GET", "/admin/users") + session.MakeRequest(t, req, http.StatusForbidden) + }) + + t.Run("Anonymous user", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req := NewRequest(t, "GET", "/admin/users") + MakeRequest(t, req, http.StatusSeeOther) + }) } func TestAdminViewUser(t *testing.T) { diff --git a/tests/integration/explore_org_test.go b/tests/integration/explore_org_test.go new file mode 100644 index 0000000000..e0c48ccf0d --- /dev/null +++ b/tests/integration/explore_org_test.go @@ -0,0 +1,49 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package integration + +import ( + "net/http" + "testing" + + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/test" + "code.gitea.io/gitea/tests" + + "github.com/stretchr/testify/assert" +) + +func TestExploreOrg(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + // Set the default sort order + defer test.MockVariableValue(&setting.UI.ExploreDefaultSort, "alphabetically")() + + cases := []struct{ sortOrder, expected string }{ + {"", "?sort=" + setting.UI.ExploreDefaultSort + "&q="}, + {"newest", "?sort=newest&q="}, + {"oldest", "?sort=oldest&q="}, + {"alphabetically", "?sort=alphabetically&q="}, + {"reversealphabetically", "?sort=reversealphabetically&q="}, + } + for _, c := range cases { + req := NewRequest(t, "GET", "/explore/organizations?sort="+c.sortOrder) + resp := MakeRequest(t, req, http.StatusOK) + h := NewHTMLParser(t, resp.Body) + href, _ := h.Find(`.ui.dropdown .menu a.active.item[href^="?sort="]`).Attr("href") + assert.Equal(t, c.expected, href) + } + + // these sort orders shouldn't be supported, to avoid leaking user activity + cases404 := []string{ + "/explore/organizations?sort=mostMembers", + "/explore/organizations?sort=leastGroups", + "/explore/organizations?sort=leastupdate", + "/explore/organizations?sort=reverseleastupdate", + } + for _, c := range cases404 { + req := NewRequest(t, "GET", c).SetHeader("Accept", "text/html") + MakeRequest(t, req, http.StatusNotFound) + } +} diff --git a/tests/integration/explore_user_test.go b/tests/integration/explore_user_test.go index 441d89cea5..d1e3fd85af 100644 --- a/tests/integration/explore_user_test.go +++ b/tests/integration/explore_user_test.go @@ -7,6 +7,8 @@ import ( "net/http" "testing" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/tests" "github.com/stretchr/testify/assert" @@ -15,8 +17,11 @@ import ( func TestExploreUser(t *testing.T) { defer tests.PrepareTestEnv(t)() + // Set the default sort order + defer test.MockVariableValue(&setting.UI.ExploreDefaultSort, "reversealphabetically")() + cases := []struct{ sortOrder, expected string }{ - {"", "?sort=newest&q="}, + {"", "?sort=" + setting.UI.ExploreDefaultSort + "&q="}, {"newest", "?sort=newest&q="}, {"oldest", "?sort=oldest&q="}, {"alphabetically", "?sort=alphabetically&q="}, diff --git a/tests/integration/pull_icon_test.go b/tests/integration/pull_icon_test.go index 8fde547ce9..b678550c30 100644 --- a/tests/integration/pull_icon_test.go +++ b/tests/integration/pull_icon_test.go @@ -133,7 +133,7 @@ func testPullRequestListIcon(t *testing.T, doc *HTMLDoc, name, expectedColor, ex } func createOpenPullRequest(ctx context.Context, t *testing.T, user *user_model.User, repo *repo_model.Repository) *issues_model.PullRequest { - pull := createPullRequest(t, user, repo, "open") + pull := createPullRequest(t, user, repo, "branch-open", "open") assert.False(t, pull.Issue.IsClosed) assert.False(t, pull.HasMerged) @@ -143,7 +143,7 @@ func createOpenPullRequest(ctx context.Context, t *testing.T, user *user_model.U } func createOpenWipPullRequest(ctx context.Context, t *testing.T, user *user_model.User, repo *repo_model.Repository) *issues_model.PullRequest { - pull := createPullRequest(t, user, repo, "open-wip") + pull := createPullRequest(t, user, repo, "branch-open-wip", "open-wip") err := issue_service.ChangeTitle(ctx, pull.Issue, user, "WIP: "+pull.Issue.Title) require.NoError(t, err) @@ -156,7 +156,7 @@ func createOpenWipPullRequest(ctx context.Context, t *testing.T, user *user_mode } func createClosedPullRequest(ctx context.Context, t *testing.T, user *user_model.User, repo *repo_model.Repository) *issues_model.PullRequest { - pull := createPullRequest(t, user, repo, "closed") + pull := createPullRequest(t, user, repo, "branch-closed", "closed") err := issue_service.ChangeStatus(ctx, pull.Issue, user, "", true) require.NoError(t, err) @@ -169,7 +169,7 @@ func createClosedPullRequest(ctx context.Context, t *testing.T, user *user_model } func createClosedWipPullRequest(ctx context.Context, t *testing.T, user *user_model.User, repo *repo_model.Repository) *issues_model.PullRequest { - pull := createPullRequest(t, user, repo, "closed-wip") + pull := createPullRequest(t, user, repo, "branch-closed-wip", "closed-wip") err := issue_service.ChangeTitle(ctx, pull.Issue, user, "WIP: "+pull.Issue.Title) require.NoError(t, err) @@ -185,7 +185,7 @@ func createClosedWipPullRequest(ctx context.Context, t *testing.T, user *user_mo } func createMergedPullRequest(ctx context.Context, t *testing.T, user *user_model.User, repo *repo_model.Repository) *issues_model.PullRequest { - pull := createPullRequest(t, user, repo, "merged") + pull := createPullRequest(t, user, repo, "branch-merged", "merged") gitRepo, err := git.OpenRepository(ctx, repo.RepoPath()) defer gitRepo.Close() @@ -202,10 +202,7 @@ func createMergedPullRequest(ctx context.Context, t *testing.T, user *user_model return pull } -func createPullRequest(t *testing.T, user *user_model.User, repo *repo_model.Repository, name string) *issues_model.PullRequest { - branch := "branch-" + name - title := "Testing " + name - +func createPullRequest(t *testing.T, user *user_model.User, repo *repo_model.Repository, branch, title string) *issues_model.PullRequest { _, err := files_service.ChangeRepoFiles(git.DefaultContext, repo, user, &files_service.ChangeRepoFilesOptions{ Files: []*files_service.ChangeRepoFile{ { diff --git a/tests/integration/pull_review_test.go b/tests/integration/pull_review_test.go index 1319db29bf..6dd58102eb 100644 --- a/tests/integration/pull_review_test.go +++ b/tests/integration/pull_review_test.go @@ -1,4 +1,5 @@ // Copyright 2019 The Gitea Authors. All rights reserved. +// Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package integration @@ -362,6 +363,8 @@ func TestPullView_CodeOwner(t *testing.T) { defer f() t.Run("First Pull Request", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + // create a new branch to prepare for pull request _, err := files_service.ChangeRepoFiles(db.DefaultContext, repo, user2, &files_service.ChangeRepoFilesOptions{ NewBranch: "codeowner-basebranch", @@ -409,6 +412,8 @@ func TestPullView_CodeOwner(t *testing.T) { require.NoError(t, err) t.Run("Second Pull Request", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + // create a new branch to prepare for pull request _, err = files_service.ChangeRepoFiles(db.DefaultContext, repo, user2, &files_service.ChangeRepoFilesOptions{ NewBranch: "codeowner-basebranch2", @@ -431,6 +436,8 @@ func TestPullView_CodeOwner(t *testing.T) { }) t.Run("Forked Repo Pull Request", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + user5 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}) forkedRepo, err := repo_service.ForkRepositoryAndUpdates(db.DefaultContext, user2, user5, repo_service.ForkRepoOptions{ BaseRepo: repo, @@ -483,6 +490,8 @@ func TestPullView_GivenApproveOrRejectReviewOnClosedPR(t *testing.T) { defer baseGitRepo.Close() t.Run("Submit approve/reject review on merged PR", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + // Create a merged PR (made by user1) in the upstream repo1. testEditFile(t, user1Session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") resp := testPullCreate(t, user1Session, "user1", "repo1", false, "master", "master", "This is a pull title") @@ -513,12 +522,14 @@ func TestPullView_GivenApproveOrRejectReviewOnClosedPR(t *testing.T) { }) t.Run("Submit approve/reject review on closed PR", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + // Created a closed PR (made by user1) in the upstream repo1. testEditFileToNewBranch(t, user1Session, "user1", "repo1", "master", "a-test-branch", "README.md", "Hello, World (Edited...again)\n") resp := testPullCreate(t, user1Session, "user1", "repo1", false, "master", "a-test-branch", "This is a pull title") elem := strings.Split(test.RedirectURL(resp), "/") assert.EqualValues(t, "pulls", elem[3]) - testIssueClose(t, user1Session, elem[1], elem[2], elem[4]) + testIssueClose(t, user1Session, elem[1], elem[2], elem[4], true) // Get the commit SHA pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ @@ -544,6 +555,41 @@ func TestPullView_GivenApproveOrRejectReviewOnClosedPR(t *testing.T) { }) } +func TestPullReviewInArchivedRepo(t *testing.T) { + onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { + session := loginUser(t, "user2") + + // Open a PR + testEditFileToNewBranch(t, session, "user2", "repo1", "master", "for-pr", "README.md", "Hi!\n") + resp := testPullCreate(t, session, "user2", "repo1", true, "master", "for-pr", "PR title") + elem := strings.Split(test.RedirectURL(resp), "/") + + t.Run("Review box normally", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + // The "Finish review button" must be available + resp = session.MakeRequest(t, NewRequest(t, "GET", path.Join(elem[1], elem[2], "pulls", elem[4], "files")), http.StatusOK) + button := NewHTMLParser(t, resp.Body).Find("#review-box button") + assert.False(t, button.HasClass("disabled")) + }) + + t.Run("Review box in archived repo", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + // Archive the repo + resp = session.MakeRequest(t, NewRequestWithValues(t, "POST", path.Join(elem[1], elem[2], "settings"), map[string]string{ + "_csrf": GetCSRF(t, session, path.Join(elem[1], elem[2], "settings")), + "action": "archive", + }), http.StatusSeeOther) + + // The "Finish review button" must be disabled + resp = session.MakeRequest(t, NewRequest(t, "GET", path.Join(elem[1], elem[2], "pulls", elem[4], "files")), http.StatusOK) + button := NewHTMLParser(t, resp.Body).Find("#review-box button") + assert.True(t, button.HasClass("disabled")) + }) + }) +} + func testNofiticationCount(t *testing.T, session *TestSession, csrf string, expectedSubmitStatus int) *httptest.ResponseRecorder { options := map[string]string{ "_csrf": csrf, @@ -579,8 +625,12 @@ func testSubmitReview(t *testing.T, session *TestSession, csrf, owner, repo, pul return session.MakeRequest(t, req, expectedSubmitStatus) } -func testIssueClose(t *testing.T, session *TestSession, owner, repo, issueNumber string) *httptest.ResponseRecorder { - req := NewRequest(t, "GET", path.Join(owner, repo, "pulls", issueNumber)) +func testIssueClose(t *testing.T, session *TestSession, owner, repo, issueNumber string, isPull bool) *httptest.ResponseRecorder { + issueType := "issues" + if isPull { + issueType = "pulls" + } + req := NewRequest(t, "GET", path.Join(owner, repo, issueType, issueNumber)) resp := session.MakeRequest(t, req, http.StatusOK) htmlDoc := NewHTMLParser(t, resp.Body) diff --git a/tests/integration/repo_test.go b/tests/integration/repo_test.go index 90fc19c193..01d905895a 100644 --- a/tests/integration/repo_test.go +++ b/tests/integration/repo_test.go @@ -1462,3 +1462,15 @@ func TestRepoSubmoduleView(t *testing.T) { htmlDoc.AssertElement(t, fmt.Sprintf(`tr[data-entryname="repo1"] a[href="%s"]`, u.JoinPath("/user2/repo1").String()), true) }) } + +func TestBlameDirectory(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + // Ensure directory exists. + req := NewRequest(t, "GET", "/user2/repo59/src/branch/master/deep") + MakeRequest(t, req, http.StatusOK) + + // Blame is not allowed + req = NewRequest(t, "GET", "/user2/repo59/blame/branch/master/deep") + MakeRequest(t, req, http.StatusNotFound) +} diff --git a/tests/integration/user_dashboard_test.go b/tests/integration/user_dashboard_test.go index abc3e065d9..0ed5193c48 100644 --- a/tests/integration/user_dashboard_test.go +++ b/tests/integration/user_dashboard_test.go @@ -5,12 +5,21 @@ package integration import ( "net/http" + "net/url" + "strconv" "strings" "testing" + "code.gitea.io/gitea/models/db" + unit_model "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/translation" + issue_service "code.gitea.io/gitea/services/issue" + files_service "code.gitea.io/gitea/services/repository/files" + "code.gitea.io/gitea/tests" + "github.com/PuerkitoBio/goquery" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -28,3 +37,45 @@ func TestUserDashboardActionLinks(t *testing.T) { assert.EqualValues(t, locale.TrString("new_migrate.link"), strings.TrimSpace(links.Find("a[href='/repo/migrate']").Text())) assert.EqualValues(t, locale.TrString("new_org.link"), strings.TrimSpace(links.Find("a[href='/org/create']").Text())) } + +func TestDashboardTitleRendering(t *testing.T) { + onGiteaRun(t, func(t *testing.T, u *url.URL) { + user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) + sess := loginUser(t, user4.Name) + + repo, _, f := tests.CreateDeclarativeRepo(t, user4, "", + []unit_model.Type{unit_model.TypePullRequests, unit_model.TypeIssues}, nil, + []*files_service.ChangeRepoFile{ + { + Operation: "create", + TreePath: "test.txt", + ContentReader: strings.NewReader("Just some text here"), + }, + }, + ) + defer f() + + issue := createIssue(t, user4, repo, "`:exclamation:` not rendered", "Hi there!") + pr := createPullRequest(t, user4, repo, "testing", "`:exclamation:` not rendered") + + _, err := issue_service.CreateIssueComment(db.DefaultContext, user4, repo, issue, "hi", nil) + require.NoError(t, err) + + _, err = issue_service.CreateIssueComment(db.DefaultContext, user4, repo, pr.Issue, "hi", nil) + require.NoError(t, err) + + testIssueClose(t, sess, repo.OwnerName, repo.Name, strconv.Itoa(int(issue.Index)), false) + testIssueClose(t, sess, repo.OwnerName, repo.Name, strconv.Itoa(int(pr.Issue.Index)), true) + + response := sess.MakeRequest(t, NewRequest(t, "GET", "/"), http.StatusOK) + htmlDoc := NewHTMLParser(t, response.Body) + + count := 0 + htmlDoc.doc.Find("#activity-feed .flex-item-main .title").Each(func(i int, s *goquery.Selection) { + count++ + assert.EqualValues(t, ":exclamation: not rendered", s.Text()) + }) + + assert.EqualValues(t, 6, count) + }) +} diff --git a/web_src/css/repo.css b/web_src/css/repo.css index b8cd3c08bb..bdb09eb776 100644 --- a/web_src/css/repo.css +++ b/web_src/css/repo.css @@ -2022,13 +2022,14 @@ details.repo-search-result summary::marker { font-weight: var(--font-weight-medium); } -.repository .repository-summary .segment.language-stats { +.repository .repository-summary #language-stats-bar { display: flex; gap: 2px; padding: 0; height: 10px; white-space: nowrap; - border-radius: 0 0 3px 3px !important; + border-top-left-radius: 0 !important; + border-top-right-radius: 0 !important; overflow: hidden; } diff --git a/web_src/js/components/DashboardRepoList.vue b/web_src/js/components/DashboardRepoList.vue index 007891a39f..0d8396b6f3 100644 --- a/web_src/js/components/DashboardRepoList.vue +++ b/web_src/js/components/DashboardRepoList.vue @@ -1,5 +1,5 @@
images/icon-install.png Installation
images/icon-usage.png Usage