From 97e2a4382bfd4fe4233994444d7982aa8667525f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 5 Feb 2025 18:27:34 +0100 Subject: [PATCH 001/164] Bump the github-actions-updates group across 1 directory with 7 updates (#30090) Bumps the github-actions-updates group with 7 updates in the / directory: | Package | From | To | | --- | --- | --- | | [actions/setup-node](https://github.com/actions/setup-node) | `4.1.0` | `4.2.0` | | [docker/build-push-action](https://github.com/docker/build-push-action) | `6.10.0` | `6.13.0` | | [github/codeql-action](https://github.com/github/codeql-action) | `3.27.9` | `3.28.8` | | [tj-actions/changed-files](https://github.com/tj-actions/changed-files) | `45.0.5` | `45.0.6` | | [getsentry/action-release](https://github.com/getsentry/action-release) | `1.7.0` | `1.9.0` | | [aws-actions/configure-aws-credentials](https://github.com/aws-actions/configure-aws-credentials) | `4.0.2` | `4.0.3` | | [mikepenz/action-junit-report](https://github.com/mikepenz/action-junit-report) | `5.2.0` | `5.3.0` | Updates `actions/setup-node` from 4.1.0 to 4.2.0 - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](https://github.com/actions/setup-node/compare/39370e3970a6d050c480ffad4ff0ed4d3fdee5af...1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a) Updates `docker/build-push-action` from 6.10.0 to 6.13.0 - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/48aba3b46d1b1fec4febb7c5d0c644b249a11355...ca877d9245402d1537745e0e356eab47c3520991) Updates `github/codeql-action` from 3.27.9 to 3.28.8 - [Release notes](https://github.com/github/codeql-action/releases) - [Commits](https://github.com/github/codeql-action/compare/v3.27.9...v3.28.8) Updates `tj-actions/changed-files` from 45.0.5 to 45.0.6 - [Release notes](https://github.com/tj-actions/changed-files/releases) - [Changelog](https://github.com/tj-actions/changed-files/blob/main/HISTORY.md) - [Commits](https://github.com/tj-actions/changed-files/compare/bab30c2299617f6615ec02a68b9a40d10bd21366...d6e91a2266cdb9d62096cebf1e8546899c6aa18f) Updates `getsentry/action-release` from 1.7.0 to 1.9.0 - [Release notes](https://github.com/getsentry/action-release/releases) - [Changelog](https://github.com/getsentry/action-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/getsentry/action-release/compare/e769183448303de84c5a06aaaddf9da7be26d6c7...f6dfa3d84a1c740b94aa45255c5e032b744a095d) Updates `aws-actions/configure-aws-credentials` from 4.0.2 to 4.0.3 - [Release notes](https://github.com/aws-actions/configure-aws-credentials/releases) - [Changelog](https://github.com/aws-actions/configure-aws-credentials/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws-actions/configure-aws-credentials/compare/e3dd6a429d7300a6a4c196c26e071d42e0343502...4fc4975a852c8cd99761e2de1f4ba73402e44dd9) Updates `mikepenz/action-junit-report` from 5.2.0 to 5.3.0 - [Release notes](https://github.com/mikepenz/action-junit-report/releases) - [Commits](https://github.com/mikepenz/action-junit-report/compare/62516aa379bff6370c95fd5894d5a27fb6619d9b...ee6b445351cd81e2f73a16a0e52d598aeac2197f) --- updated-dependencies: - dependency-name: actions/setup-node dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions-updates - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions-updates - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions-updates - dependency-name: tj-actions/changed-files dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions-updates - dependency-name: getsentry/action-release dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions-updates - dependency-name: aws-actions/configure-aws-credentials dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions-updates - dependency-name: mikepenz/action-junit-report dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions-updates ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/api.yml | 2 +- .github/workflows/build-opensearch-image.yml | 2 +- .github/workflows/build-server-image.yml | 2 +- .github/workflows/codeql-analysis.yml | 6 +++--- .github/workflows/e2e-tests-ci-template.yml | 10 +++++----- .github/workflows/i18n-ci-template.yml | 2 +- .github/workflows/scorecards-analysis.yml | 2 +- .github/workflows/sentry.yaml | 2 +- .github/workflows/server-ci-artifacts.yml | 2 +- .github/workflows/server-ci-report.yml | 2 +- .github/workflows/server-ci-template.yml | 2 +- 11 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/workflows/api.yml b/.github/workflows/api.yml index cadb2a939850..97895a8f50e0 100644 --- a/.github/workflows/api.yml +++ b/.github/workflows/api.yml @@ -20,7 +20,7 @@ jobs: - name: Checkout code uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0 + - uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 with: node-version-file: .nvmrc cache: "npm" diff --git a/.github/workflows/build-opensearch-image.yml b/.github/workflows/build-opensearch-image.yml index d20f89679804..6609175f0f84 100644 --- a/.github/workflows/build-opensearch-image.yml +++ b/.github/workflows/build-opensearch-image.yml @@ -22,7 +22,7 @@ jobs: password: ${{ secrets.DOCKERHUB_DEV_TOKEN }} - name: opensearch/build-and-push - uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 # v6.10.0 + uses: docker/build-push-action@ca877d9245402d1537745e0e356eab47c3520991 # v6.13.0 with: provenance: false file: server/build/Dockerfile.opensearch diff --git a/.github/workflows/build-server-image.yml b/.github/workflows/build-server-image.yml index 34ee19a4695a..843413b20b13 100644 --- a/.github/workflows/build-server-image.yml +++ b/.github/workflows/build-server-image.yml @@ -27,7 +27,7 @@ jobs: password: ${{ secrets.DOCKERHUB_DEV_TOKEN }} - name: buildenv/build-and-push - uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 # v6.10.0 + uses: docker/build-push-action@ca877d9245402d1537745e0e356eab47c3520991 # v6.13.0 with: provenance: false file: server/build/Dockerfile.buildenv diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 0050d08e5d75..de898fb87fe0 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -29,14 +29,14 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v3.27.9 + uses: github/codeql-action/init@v3.28.8 with: languages: ${{ matrix.language }} debug: false config-file: ./.github/codeql/codeql-config.yml - name: Build JavaScript - uses: github/codeql-action/autobuild@v3.27.9 + uses: github/codeql-action/autobuild@v3.28.8 if: ${{ matrix.language == 'javascript' }} - name: Setup go @@ -55,4 +55,4 @@ jobs: # Perform Analysis - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3.27.9 + uses: github/codeql-action/analyze@v3.28.8 diff --git a/.github/workflows/e2e-tests-ci-template.yml b/.github/workflows/e2e-tests-ci-template.yml index d8989cf16705..9187081509f4 100644 --- a/.github/workflows/e2e-tests-ci-template.yml +++ b/.github/workflows/e2e-tests-ci-template.yml @@ -124,7 +124,7 @@ jobs: fetch-depth: 0 - name: ci/setup-node if: "${{ inputs.run_preflight_checks }}" - uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0 + uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 id: setup_node with: node-version-file: ".nvmrc" @@ -155,7 +155,7 @@ jobs: fetch-depth: 0 - name: ci/setup-node if: "${{ inputs.run_preflight_checks }}" - uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0 + uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 id: setup_node with: node-version-file: ".nvmrc" @@ -237,7 +237,7 @@ jobs: ref: ${{ inputs.commit_sha }} fetch-depth: 0 - name: ci/setup-node - uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0 + uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 id: setup_node with: node-version-file: ".nvmrc" @@ -324,7 +324,7 @@ jobs: ln -sfn /usr/local/opt/docker-compose/bin/docker-compose ~/.docker/cli-plugins/docker-compose sudo ln -sf $HOME/.colima/default/docker.sock /var/run/docker.sock - name: ci/setup-node - uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0 + uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 id: setup_node with: node-version-file: ".nvmrc" @@ -402,7 +402,7 @@ jobs: e2e-tests/${{ inputs.TEST }}/results/ - name: ci/setup-node if: "${{ inputs.enable_reporting }}" - uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0 + uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 id: setup_node with: node-version-file: ".nvmrc" diff --git a/.github/workflows/i18n-ci-template.yml b/.github/workflows/i18n-ci-template.yml index 326bebfcb5bf..f456d0a46b38 100644 --- a/.github/workflows/i18n-ci-template.yml +++ b/.github/workflows/i18n-ci-template.yml @@ -15,7 +15,7 @@ jobs: - name: Get changed files id: changed-files - uses: tj-actions/changed-files@bab30c2299617f6615ec02a68b9a40d10bd21366 # v45.0.5 + uses: tj-actions/changed-files@d6e91a2266cdb9d62096cebf1e8546899c6aa18f # v45.0.6 with: files: | server/i18n/*.json diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml index e29671022d1b..00c3bf236d1c 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -56,6 +56,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@dd7559424621a6dd0b32ababe9e4b271a87f78d2 # v2.27.0 + uses: github/codeql-action/upload-sarif@0701025a8b1600e416be4f3bb5a830b1aa6af01e # v2.27.0 with: sarif_file: results.sarif diff --git a/.github/workflows/sentry.yaml b/.github/workflows/sentry.yaml index 0a7b0d5493be..b9c6beb8fbd8 100644 --- a/.github/workflows/sentry.yaml +++ b/.github/workflows/sentry.yaml @@ -20,5 +20,5 @@ jobs: - name: cd/Checkout mattermost project uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: cd/Create Sentry release - uses: getsentry/action-release@e769183448303de84c5a06aaaddf9da7be26d6c7 # v1.7.0 + uses: getsentry/action-release@f6dfa3d84a1c740b94aa45255c5e032b744a095d # v1.9.0 diff --git a/.github/workflows/server-ci-artifacts.yml b/.github/workflows/server-ci-artifacts.yml index ffac29839e6b..5506be6d0a10 100644 --- a/.github/workflows/server-ci-artifacts.yml +++ b/.github/workflows/server-ci-artifacts.yml @@ -33,7 +33,7 @@ jobs: - update-initial-status steps: - name: cd/configure-aws-credentials - uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v4.0.2 + uses: aws-actions/configure-aws-credentials@4fc4975a852c8cd99761e2de1f4ba73402e44dd9 # v4.0.3 with: aws-region: us-east-1 aws-access-key-id: ${{ secrets.PR_BUILDS_BUCKET_AWS_ACCESS_KEY_ID }} diff --git a/.github/workflows/server-ci-report.yml b/.github/workflows/server-ci-report.yml index 93f12f731807..956726f5e1ee 100644 --- a/.github/workflows/server-ci-report.yml +++ b/.github/workflows/server-ci-report.yml @@ -57,7 +57,7 @@ jobs: run: echo "NUMBER=$(cat ${{ matrix.test.artifact }}/pr-number)" >> ${GITHUB_OUTPUT} - name: Publish test report id: report - uses: mikepenz/action-junit-report@62516aa379bff6370c95fd5894d5a27fb6619d9b # v5.2.0 + uses: mikepenz/action-junit-report@ee6b445351cd81e2f73a16a0e52d598aeac2197f # v5.3.0 with: report_paths: ${{ matrix.test.artifact }}/report.xml check_name: ${{ matrix.test.name }} (Results) diff --git a/.github/workflows/server-ci-template.yml b/.github/workflows/server-ci-template.yml index 843f7360b3af..002a513a637c 100644 --- a/.github/workflows/server-ci-template.yml +++ b/.github/workflows/server-ci-template.yml @@ -295,7 +295,7 @@ jobs: server/go.sum server/public/go.sum - name: ci/setup-node - uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0 + uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 id: setup_node with: node-version-file: ".nvmrc" From 1ce29f8466aa6d8dd2884e9bad4c8cea7919c1de Mon Sep 17 00:00:00 2001 From: Christopher Poile Date: Wed, 5 Feb 2025 16:06:43 -0500 Subject: [PATCH 002/164] [MM-60989] Add SelectBuilderCtx to simplify db code (#29988) * add SelectBuilderCtx; change the few calls where it's appropriate to use * tests (wip) * tests improved * use SelectCtx's own WithTimeout * improve tests based on PR feedback --------- Co-authored-by: Mattermost Build --- .../store/sqlstore/compliance_store.go | 7 +- .../outgoing_oauth_connection_store.go | 2 +- .../channels/store/sqlstore/sqlx_wrapper.go | 6 +- .../store/sqlstore/sqlx_wrapper_test.go | 70 +++++++++++++++++++ server/channels/store/sqlstore/user_store.go | 2 +- 5 files changed, 78 insertions(+), 9 deletions(-) diff --git a/server/channels/store/sqlstore/compliance_store.go b/server/channels/store/sqlstore/compliance_store.go index bd902a292ef7..cd82f50c6c2f 100644 --- a/server/channels/store/sqlstore/compliance_store.go +++ b/server/channels/store/sqlstore/compliance_store.go @@ -330,13 +330,8 @@ func (s SqlComplianceStore) MessageExport(c request.CTX, cursor model.MessageExp builder = builder.Where(sq.LtOrEq{"Posts.UpdateAt": cursor.UntilUpdateAt}) } - query, args, err := builder.ToSql() - if err != nil { - return nil, cursor, errors.Wrap(err, "unable to construct query to export messages") - } - cposts := []*model.MessageExport{} - if err := s.GetReplica().SelectCtx(c.Context(), &cposts, query, args...); err != nil { + if err := s.GetReplica().SelectBuilderCtx(c.Context(), &cposts, builder); err != nil { return nil, cursor, errors.Wrap(err, "unable to export messages") } if len(cposts) > 0 { diff --git a/server/channels/store/sqlstore/outgoing_oauth_connection_store.go b/server/channels/store/sqlstore/outgoing_oauth_connection_store.go index 398f6ad60c9a..ba625214d4a5 100644 --- a/server/channels/store/sqlstore/outgoing_oauth_connection_store.go +++ b/server/channels/store/sqlstore/outgoing_oauth_connection_store.go @@ -121,7 +121,7 @@ func (s *SqlOutgoingOAuthConnectionStore) GetConnections(c request.CTX, filters query = query.Where(sq.Like{"Audiences": fmt.Sprint("%", filters.Audience, "%")}) } - if err := s.GetReplica().SelectBuilder(&conns, query); err != nil { + if err := s.GetReplica().SelectBuilderCtx(c.Context(), &conns, query); err != nil { return nil, errors.Wrap(err, "failed to get OutgoingOAuthConnections") } diff --git a/server/channels/store/sqlstore/sqlx_wrapper.go b/server/channels/store/sqlstore/sqlx_wrapper.go index 3ea0d28dad3b..bd294ba628af 100644 --- a/server/channels/store/sqlstore/sqlx_wrapper.go +++ b/server/channels/store/sqlstore/sqlx_wrapper.go @@ -249,12 +249,16 @@ func (w *sqlxDBWrapper) SelectCtx(ctx context.Context, dest any, query string, a } func (w *sqlxDBWrapper) SelectBuilder(dest any, builder Builder) error { + return w.SelectBuilderCtx(context.Background(), dest, builder) +} + +func (w *sqlxDBWrapper) SelectBuilderCtx(ctx context.Context, dest any, builder Builder) error { query, args, err := builder.ToSql() if err != nil { return err } - return w.Select(dest, query, args...) + return w.SelectCtx(ctx, dest, query, args...) } type sqlxTxWrapper struct { diff --git a/server/channels/store/sqlstore/sqlx_wrapper_test.go b/server/channels/store/sqlstore/sqlx_wrapper_test.go index 264c60844b70..35dab5bbedc1 100644 --- a/server/channels/store/sqlstore/sqlx_wrapper_test.go +++ b/server/channels/store/sqlstore/sqlx_wrapper_test.go @@ -90,3 +90,73 @@ func TestSqlX(t *testing.T) { } }) } + +func TestSqlxSelect(t *testing.T) { + testDrivers := []string{ + model.DatabaseDriverPostgres, + model.DatabaseDriverMysql, + } + for _, driver := range testDrivers { + t.Run(driver, func(t *testing.T) { + settings, err := makeSqlSettings(driver) + if err != nil { + t.Skip(err) + } + *settings.QueryTimeout = 1 + store := &SqlStore{ + rrCounter: 0, + srCounter: 0, + settings: settings, + logger: mlog.CreateConsoleTestLogger(t), + quitMonitor: make(chan struct{}), + wgMonitor: &sync.WaitGroup{}, + } + + require.NoError(t, store.initConnection()) + defer store.Close() + + t.Run("SelectCtx", func(t *testing.T) { + var result []string + err := store.GetMaster().SelectCtx(context.Background(), &result, "SELECT 'test' AS col") + require.NoError(t, err) + require.Equal(t, []string{"test"}, result) + + // Test timeout + ctx, cancel := context.WithTimeout(context.Background(), 1) + defer cancel() + var query string + if driver == model.DatabaseDriverMysql { + query = "SELECT SLEEP(2)" + } else { + query = "SELECT pg_sleep(2)" + } + err = store.GetMaster().SelectCtx(ctx, &result, query) + require.Error(t, err) + require.Equal(t, context.DeadlineExceeded, err) + }) + + t.Run("SelectBuilderCtx", func(t *testing.T) { + var result []string + builder := store.getQueryBuilder(). + Select("'test' AS col") + err := store.GetMaster().SelectBuilderCtx(context.Background(), &result, builder) + require.NoError(t, err) + require.Equal(t, []string{"test"}, result) + + // Test timeout + ctx, cancel := context.WithTimeout(context.Background(), 1) + defer cancel() + if driver == model.DatabaseDriverMysql { + builder = store.getQueryBuilder(). + Select("SLEEP(2)") + } else { + builder = store.getQueryBuilder(). + Select("pg_sleep(2)") + } + err = store.GetMaster().SelectBuilderCtx(ctx, &result, builder) + require.Error(t, err) + require.Equal(t, context.DeadlineExceeded, err) + }) + }) + } +} diff --git a/server/channels/store/sqlstore/user_store.go b/server/channels/store/sqlstore/user_store.go index 4bc5388d8343..fda085fc4668 100644 --- a/server/channels/store/sqlstore/user_store.go +++ b/server/channels/store/sqlstore/user_store.go @@ -501,7 +501,7 @@ func (us SqlUserStore) GetMfaUsedTimestamps(userId string) ([]int, error) { func (us SqlUserStore) GetMany(ctx context.Context, ids []string) ([]*model.User, error) { query := us.usersQuery.Where(sq.Eq{"Id": ids}) users := []*model.User{} - if err := us.SqlStore.DBXFromContext(ctx).SelectBuilder(&users, query); err != nil { + if err := us.SqlStore.DBXFromContext(ctx).SelectBuilderCtx(ctx, &users, query); err != nil { return nil, errors.Wrap(err, "users_get_many_select") } From 8b65771a3154a651478361ac71cb8d9a1ac4e911 Mon Sep 17 00:00:00 2001 From: Christopher Poile Date: Wed, 5 Feb 2025 16:20:28 -0500 Subject: [PATCH 003/164] [MM-62897] Compliance export backwards compatibility (#30120) * add backwards compatibility for <10.5 * test: backwards compatibility for <10.5 * spelling --- server/enterprise/message_export/worker.go | 6 ++ .../enterprise/message_export/worker_test.go | 60 +++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/server/enterprise/message_export/worker.go b/server/enterprise/message_export/worker.go index 95e1f40ffa9d..851af7e0885f 100644 --- a/server/enterprise/message_export/worker.go +++ b/server/enterprise/message_export/worker.go @@ -327,6 +327,12 @@ func (w *MessageExportWorker) initJobData(logger mlog.LoggerIFace, job *model.Jo if previousJob.Data == nil { previousJob.Data = make(map[string]string) } + + // Backwards compatibility for <10.5: + if batchStartTimestamp, prevExists := previousJob.Data["batch_start_timestamp"]; prevExists { + previousJob.Data[shared.JobDataBatchStartTime] = batchStartTimestamp + } + if _, prevExists := previousJob.Data[shared.JobDataBatchStartTime]; !prevExists { exportFromTimestamp := strconv.FormatInt(*w.jobServer.Config().MessageExportSettings.ExportFromTimestamp, 10) logger.Info("Worker: Previously successful job lacks job data, falling back to configured MessageExportSettings.ExportFromTimestamp", mlog.String("export_from_timestamp", exportFromTimestamp)) diff --git a/server/enterprise/message_export/worker_test.go b/server/enterprise/message_export/worker_test.go index 08baa5cf598f..348c83fe0383 100644 --- a/server/enterprise/message_export/worker_test.go +++ b/server/enterprise/message_export/worker_test.go @@ -188,6 +188,66 @@ func TestInitJobDataPreviousJobWithJobData(t *testing.T) { assert.Equal(t, expectedDir, job.Data[shared.JobDataExportDir]) } +func TestInitJobDataPreviousJobWithJobDataPre105(t *testing.T) { + logger := mlog.CreateConsoleTestLogger(t) + mockStore := &storetest.Store{} + defer mockStore.AssertExpectations(t) + + previousJob := &model.Job{ + Id: st.NewTestID(), + CreateAt: model.GetMillis(), + Status: model.JobStatusSuccess, + Type: model.JobTypeMessageExport, + StartAt: model.GetMillis() - 1000, + LastActivityAt: model.GetMillis() - 1000, + Data: map[string]string{"batch_start_timestamp": "123"}, + } + + job := &model.Job{ + Id: st.NewTestID(), + CreateAt: model.GetMillis(), + Status: model.JobStatusPending, + Type: model.JobTypeMessageExport, + Data: map[string]string{shared.JobDataExportDir: "this-is-the-export-dir"}, + } + + // mock job store returns a previously successful job that has the config that we're looking for, so we use it + mockStore.JobStore.On("GetNewestJobByStatusesAndType", []string{model.JobStatusWarning, model.JobStatusSuccess}, model.JobTypeMessageExport).Return(previousJob, nil) + + worker := &MessageExportWorker{ + jobServer: &jobs.JobServer{ + Store: mockStore, + ConfigService: &testutils.StaticConfigService{ + Cfg: &model.Config{ + // mock config + MessageExportSettings: model.MessageExportSettings{ + EnableExport: model.NewPointer(true), + ExportFormat: model.NewPointer(model.ComplianceExportTypeActiance), + DailyRunTime: model.NewPointer("01:00"), + ExportFromTimestamp: model.NewPointer(int64(0)), + BatchSize: model.NewPointer(10000), + ChannelBatchSize: model.NewPointer(100), + ChannelHistoryBatchSize: model.NewPointer(100), + }, + }, + }, + }, + logger: logger, + } + + now := time.Now() + worker.initJobData(logger, job, now) + + assert.Equal(t, model.ComplianceExportTypeActiance, job.Data[shared.JobDataExportType]) + assert.Equal(t, strconv.Itoa(*worker.jobServer.Config().MessageExportSettings.BatchSize), job.Data[shared.JobDataBatchSize]) + + // Assert the new job picks up the <10.5 job start time: + assert.Equal(t, previousJob.Data[shared.JobDataBatchStartTime], job.Data[shared.JobDataBatchStartTime]) + + expectedDir := "this-is-the-export-dir" + assert.Equal(t, expectedDir, job.Data[shared.JobDataExportDir]) +} + func TestDoJobNoPostsToExport(t *testing.T) { logger := mlog.CreateConsoleTestLogger(t) From 35e776d805e8384b6e7d010b079ec05465b6ea29 Mon Sep 17 00:00:00 2001 From: Harshil Sharma <18575143+harshilsharma63@users.noreply.github.com> Date: Thu, 6 Feb 2025 11:50:37 +0530 Subject: [PATCH 004/164] Removed Elasticsearch and Opensearch channel index schedma check (#30102) --- server/channels/app/elasticsearch.go | 153 ------------------ server/channels/app/server.go | 2 - server/channels/app/user.go | 10 -- .../elasticsearch/elasticsearch.go | 112 +------------ .../elasticsearch/opensearch/opensearch.go | 117 +------------- server/i18n/en.json | 4 - .../searchengine/bleveengine/bleve.go | 4 - .../services/searchengine/interface.go | 1 - .../mocks/SearchEngineInterface.go | 18 --- 9 files changed, 2 insertions(+), 419 deletions(-) delete mode 100644 server/channels/app/elasticsearch.go diff --git a/server/channels/app/elasticsearch.go b/server/channels/app/elasticsearch.go deleted file mode 100644 index 06cf04e06e99..000000000000 --- a/server/channels/app/elasticsearch.go +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package app - -import ( - "errors" - "net/url" - - "github.com/mattermost/mattermost/server/public/model" - "github.com/mattermost/mattermost/server/public/shared/i18n" - "github.com/mattermost/mattermost/server/public/shared/mlog" - "github.com/mattermost/mattermost/server/public/shared/request" - "github.com/mattermost/mattermost/server/v8/channels/utils" -) - -func (a *App) initElasticsearchChannelIndexCheck() { - // the logic of when to perform the check has been derived from platform/searchengine.StartSearchEngine() - // Wherever we're starting the engine, we're checking the index mapping here. - - a.Log().Debug("initElasticsearchChannelIndexCheck: calling elasticsearchChannelIndexCheckWithRetry before setting up config and license change listeners...") - a.elasticsearchChannelIndexCheckWithRetry() - - a.AddConfigListener(func(oldConfig, newConfig *model.Config) { - if a.SearchEngine().ElasticsearchEngine == nil { - return - } - - oldESConfig := oldConfig.ElasticsearchSettings - newESConfig := newConfig.ElasticsearchSettings - - // if indexing is turned on, check. - if !*oldESConfig.EnableIndexing && *newESConfig.EnableIndexing { - a.Log().Debug("initElasticsearchChannelIndexCheck: calling elasticsearchChannelIndexCheckWithRetry from config change listener as ES indexing was turned from 'off' to 'on'") - a.elasticsearchChannelIndexCheckWithRetry() - } else if *newESConfig.EnableIndexing && (*oldESConfig.Password != *newESConfig.Password || *oldESConfig.Username != *newESConfig.Username || *oldESConfig.ConnectionURL != *newESConfig.ConnectionURL || *oldESConfig.Sniff != *newESConfig.Sniff) { - // ES client reconnects if credentials or address changes - a.Log().Debug("initElasticsearchChannelIndexCheck: calling elasticsearchChannelIndexCheckWithRetry from config change listener one of the Elasticsearch config param changed") - a.elasticsearchChannelIndexCheckWithRetry() - } - }) - - a.AddLicenseListener(func(oldLicense, newLicense *model.License) { - if a.SearchEngine() == nil { - return - } - - // if a license was added, and it has ES enabled- - if oldLicense == nil && newLicense != nil { - if a.SearchEngine().ElasticsearchEngine != nil { - a.Log().Debug("initElasticsearchChannelIndexCheck: calling elasticsearchChannelIndexCheckWithRetry from license change listener") - a.elasticsearchChannelIndexCheckWithRetry() - } - } - }) -} - -func (a *App) elasticsearchChannelIndexCheckWithRetry() { - // this is being done async to not block license application and config - // processes as the listeners for those are called synchronously. - go func() { - // using progressive retry because ES client may take some time to connect and be ready. - _ = utils.LongProgressiveRetry(func() error { - a.Log().Debug("elasticsearchChannelIndexCheckWithRetry: attempting to check channel index state...") - - if !*a.Config().ElasticsearchSettings.EnableIndexing { - a.Log().Debug("elasticsearchChannelIndexCheckWithRetry: skipping because elasticsearch indexing is disabled") - return nil - } - - elastic := a.SearchEngine().ElasticsearchEngine - if elastic == nil { - a.Log().Debug("elasticsearchChannelIndexCheckWithRetry: skipping because elastic engine is nil") - return errors.New("retry") - } - - if !elastic.IsActive() { - a.Log().Debug("elasticsearchChannelIndexCheckWithRetry: skipping because elastic.IsActive is false") - return errors.New("retry") - } - - a.Log().Debug("elasticsearchChannelIndexCheckWithRetry: checking channel index state...") - a.elasticsearchChannelIndexCheck() - return nil - }) - }() -} - -func (a *App) elasticsearchChannelIndexCheck() { - a.Log().Debug("elasticsearchChannelIndexCheck: checking if there is a need to notify the admins...") - if needNotify := a.elasticChannelsIndexNeedNotifyAdmins(); !needNotify { - a.Log().Debug("elasticsearchChannelIndexCheck: index is verified, no need to notify admins.") - return - } - - a.Log().Debug("elasticsearchChannelIndexCheck: index is not verified, need to notify admins.") - - // notify all system admins - systemBot, appErr := a.GetSystemBot(request.EmptyContext(a.Log())) - if appErr != nil { - a.Log().Error("elasticsearchChannelIndexCheck: couldn't get system bot", mlog.Err(appErr)) - return - } - - sysAdmins, appErr := a.getAllSystemAdmins() - if appErr != nil { - a.Log().Error("elasticsearchChannelIndexCheck: error occurred fetching all system admins", mlog.Err(appErr)) - } - - elasticsearchSettingsSectionLink, err := url.JoinPath(*a.Config().ServiceSettings.SiteURL, "admin_console/environment/elasticsearch") - if err != nil { - a.Log().Error("elasticsearchChannelIndexCheck: error occurred constructing Elasticsearch system console section path") - return - } - - // TODO include a link to changelog - postMessage := i18n.T("app.channel.elasticsearch_channel_index.notify_admin.message", map[string]any{"ElasticsearchSection": elasticsearchSettingsSectionLink}) - - for _, sysAdmin := range sysAdmins { - var channel *model.Channel - channel, appErr = a.GetOrCreateDirectChannel(request.EmptyContext(a.Log()), sysAdmin.Id, systemBot.UserId) - if appErr != nil { - a.Log().Error("elasticsearchChannelIndexCheck: error occurred ensuring DM channel between system bot and sys admin", mlog.Err(appErr)) - continue - } - - post := &model.Post{ - Message: postMessage, - UserId: systemBot.UserId, - ChannelId: channel.Id, - } - _, appErr = a.CreatePost(request.EmptyContext(a.Log()), post, channel, model.CreatePostFlags{TriggerWebhooks: true}) - if appErr != nil { - a.Log().Error("elasticsearchChannelIndexCheck: error occurred creating post", mlog.Err(appErr)) - continue - } - } -} - -func (a *App) elasticChannelsIndexNeedNotifyAdmins() bool { - elastic := a.SearchEngine().ElasticsearchEngine - if elastic == nil { - a.Log().Debug("elasticChannelsIndexNeedNotifyAdmins: skipping because elastic engine is nil") - return false - } - - if elastic.IsChannelsIndexVerified() { - a.Log().Debug("elasticChannelsIndexNeedNotifyAdmins: skipping because channels index is verified") - return false - } - - return true -} diff --git a/server/channels/app/server.go b/server/channels/app/server.go index aef479a7b17d..5a8b815279ef 100644 --- a/server/channels/app/server.go +++ b/server/channels/app/server.go @@ -502,8 +502,6 @@ func NewServer(options ...Option) (*Server, error) { } }) - app.initElasticsearchChannelIndexCheck() - return s, nil } diff --git a/server/channels/app/user.go b/server/channels/app/user.go index 15789f785766..c429df5ddef0 100644 --- a/server/channels/app/user.go +++ b/server/channels/app/user.go @@ -2922,13 +2922,3 @@ func (a *App) UserIsFirstAdmin(rctx request.CTX, user *model.User) bool { return true } - -func (a *App) getAllSystemAdmins() ([]*model.User, *model.AppError) { - userOptions := &model.UserGetOptions{ - Page: 0, - PerPage: 500, - Role: model.SystemAdminRoleId, - Inactive: false, - } - return a.GetUsersFromProfiles(userOptions) -} diff --git a/server/enterprise/elasticsearch/elasticsearch/elasticsearch.go b/server/enterprise/elasticsearch/elasticsearch/elasticsearch.go index b6e6506fe966..ece15b8128b0 100644 --- a/server/enterprise/elasticsearch/elasticsearch/elasticsearch.go +++ b/server/enterprise/elasticsearch/elasticsearch/elasticsearch.go @@ -44,14 +44,6 @@ type ElasticsearchInterfaceImpl struct { bulkProcessor *Bulk Platform *platform.PlatformService - - // This flag is for indicating if channel index's mappings - // has been verified, and if so, what was the result. - // - // value = 0 indicates it has NOT BEEN CHECKED - // value = 1 indicates index has been checked and has CORRECT mappings - // value = 2 indicates index has been checked and it has INCORRECT mappings - channelIndexVerified int32 } func getJSONOrErrorStr(obj any) string { @@ -87,25 +79,7 @@ func (es *ElasticsearchInterfaceImpl) IsSearchEnabled() bool { } func (es *ElasticsearchInterfaceImpl) IsAutocompletionEnabled() bool { - // if we encounter the index mappings haven't been checked, we check it once and store result. - // While in most cases the flag would have been set in the `Start` function, - // There's a case if you call the update config API and enable ES and autocomplete at the same time, it's not set - // so we're checking if it's unset here and trying to check the index. - if atomic.LoadInt32(&es.channelIndexVerified) == 0 { - es.Platform.Log().Debug("Elasticsearch.IsAutocompletionEnabled: channel index has not been verified yet, checking index now") - es.checkChannelIndex() - } - - return *es.Platform.Config().ElasticsearchSettings.EnableAutocomplete && atomic.LoadInt32(&es.channelIndexVerified) == 1 -} - -func (es *ElasticsearchInterfaceImpl) IsChannelsIndexVerified() bool { - if atomic.LoadInt32(&es.channelIndexVerified) == 0 { - es.Platform.Log().Debug("Elasticsearch.IsChannelsIndexVerified: channel index has not been verified yet, checking index now") - es.checkChannelIndex() - } - - return atomic.LoadInt32(&es.channelIndexVerified) == 1 + return *es.Platform.Config().ElasticsearchSettings.EnableAutocomplete } func (es *ElasticsearchInterfaceImpl) IsIndexingSync() bool { @@ -191,10 +165,6 @@ func (es *ElasticsearchInterfaceImpl) Start() *model.AppError { return model.NewAppError("Elasticsearch.start", "ent.elasticsearch.create_template_file_info_if_not_exists.template_create_failed", map[string]any{"Backend": model.ElasticsearchSettingsESBackend}, "", http.StatusInternalServerError).Wrap(err) } - if atomic.LoadInt32(&es.channelIndexVerified) == 0 { - es.checkChannelIndex() - } - atomic.StoreInt32(&es.ready, 1) return nil @@ -1876,83 +1846,3 @@ func checkMaxVersion(client *elastic.TypedClient, cfg *model.Config) (string, in } return resp.Version.Int, major, nil } - -// checkChannelIndex checks if channel index's mapping is correct. -// See Jira issue https://mattermost.atlassian.net/browse/MM-49257 -func (es *ElasticsearchInterfaceImpl) checkChannelIndex() { - es.Platform.Log().Debug("Elasticsearch.checkChannelIndex: checking if channel index field is of correct type") - isCorrect, err := es.isFieldCorrect() - if err != nil { - return - } - - if isCorrect { - es.Platform.Log().Debug("Elasticsearch.checkChannelIndex: channel index field is correct") - atomic.StoreInt32(&es.channelIndexVerified, 1) - } else { - es.Platform.Log().Debug("Elasticsearch.checkChannelIndex: channel index field is incorrect") - atomic.StoreInt32(&es.channelIndexVerified, 2) - } -} - -func (es *ElasticsearchInterfaceImpl) isFieldCorrect() (bool, error) { - // We want to check if channel index's "type" field is of type "keyword". - // If the index is in incorrect state, the field would be of type "text". - - es.Platform.Log().Debug("Elasticsearch.isFieldCorrect: querying ES to check if field is correct") - - ctx, cancel := context.WithTimeout( - context.Background(), - time.Duration(*es.Platform.Config().ElasticsearchSettings.RequestTimeoutSeconds)*time.Second, - ) - defer cancel() - - indexName := *es.Platform.Config().ElasticsearchSettings.IndexPrefix + common.IndexBaseChannels - indexMappingInterface, err := es.client.Indices.GetFieldMapping("type").Index(indexName).Do(ctx) - if err != nil { - // The case of channels index not existing is fine, - // as whenever the index will be created, it will be created - // with the correct mappings. - elasticErr, ok := err.(*types.ElasticsearchError) - if ok && elasticErr.Status == http.StatusNotFound { - es.Platform.Logger().Debug("Elasticsearch isFieldCorrect: channel index doesn't exist", mlog.Err(err)) - return true, nil - } - - es.Platform.Logger().Error("Elasticsearch: Failed to fetch channels index template", mlog.Err(err)) - return false, err - } - - // this struct is declared here because - // it's not used anywhere outside this function - type channelsTypeFieldMapping struct { - Mappings struct { - Type struct { - Mapping struct { - Type struct { - Type string - } - } - } - } - } - - mappingInterface := indexMappingInterface[indexName] - mappingBytes, err := json.Marshal(mappingInterface) - if err != nil { - es.Platform.Logger().Error("Elasticsearch: Failed to marshal Elasticsearch index field mapping", mlog.Err(err)) - return false, err - } - - es.Platform.Log().Debug("Elasticsearch.isFieldCorrect: channel index type field mapping queried successfully", mlog.String("mapping", string(mappingBytes))) - - var mapping channelsTypeFieldMapping - err = json.Unmarshal(mappingBytes, &mapping) - if err != nil { - es.Platform.Logger().Error("Elasticsearch: Failed to unmarshal Elasticsearch index field mapping", mlog.Err(err)) - return false, err - } - - es.Platform.Logger().Debug("Elasticsearch: Found type of type field as", mlog.String("type", mapping.Mappings.Type.Mapping.Type.Type)) - return mapping.Mappings.Type.Mapping.Type.Type == "keyword", nil -} diff --git a/server/enterprise/elasticsearch/opensearch/opensearch.go b/server/enterprise/elasticsearch/opensearch/opensearch.go index 329f447faedd..69f85f9f4d0d 100644 --- a/server/enterprise/elasticsearch/opensearch/opensearch.go +++ b/server/enterprise/elasticsearch/opensearch/opensearch.go @@ -46,14 +46,6 @@ type OpensearchInterfaceImpl struct { bulkProcessor *Bulk Platform *platform.PlatformService - - // This flag is for indicating if channel index's mappings - // has been verified, and if so, what was the result. - // - // value = 0 indicates it has NOT BEEN CHECKED - // value = 1 indicates index has been checked and has CORRECT mappings - // value = 2 indicates index has been checked and it has INCORRECT mappings - channelIndexVerified int32 } func getJSONOrErrorStr(obj any) string { @@ -89,25 +81,7 @@ func (os *OpensearchInterfaceImpl) IsSearchEnabled() bool { } func (os *OpensearchInterfaceImpl) IsAutocompletionEnabled() bool { - // if we encounter the index mappings haven't been checked, we check it once and store result. - // While in most cases the flag would have been set in the `Start` function, - // There's a case if you call the update config API and enable ES and autocomplete at the same time, it's not set - // so we're checking if its unset here and trying to check the index. - if atomic.LoadInt32(&os.channelIndexVerified) == 0 { - os.Platform.Log().Debug("IsAutocompletionEnabled: channel index has not been verified yet, checking index now") - os.checkChannelIndex() - } - - return *os.Platform.Config().ElasticsearchSettings.EnableAutocomplete && atomic.LoadInt32(&os.channelIndexVerified) == 1 -} - -func (os *OpensearchInterfaceImpl) IsChannelsIndexVerified() bool { - if atomic.LoadInt32(&os.channelIndexVerified) == 0 { - os.Platform.Log().Debug("OpenSearch.IsChannelsIndexVerified: channel index has not been verified yet, checking index now") - os.checkChannelIndex() - } - - return atomic.LoadInt32(&os.channelIndexVerified) == 1 + return *os.Platform.Config().ElasticsearchSettings.EnableAutocomplete } func (os *OpensearchInterfaceImpl) IsIndexingSync() bool { @@ -213,10 +187,6 @@ func (os *OpensearchInterfaceImpl) Start() *model.AppError { return model.NewAppError("Opensearch.start", "ent.elasticsearch.create_template_file_info_if_not_exists.template_create_failed", map[string]any{"Backend": model.ElasticsearchSettingsOSBackend}, "", http.StatusInternalServerError).Wrap(err) } - if atomic.LoadInt32(&os.channelIndexVerified) == 0 { - os.checkChannelIndex() - } - atomic.StoreInt32(&os.ready, 1) return nil @@ -2025,88 +1995,3 @@ func checkMaxVersion(client *opensearchapi.Client) (string, int, *model.AppError } return resp.Version.Number, major, nil } - -// checkChannelIndex checks if channel index's mapping is correct. -// See Jira issue https://mattermost.atlassian.net/browse/MM-49257 -func (os *OpensearchInterfaceImpl) checkChannelIndex() { - os.Platform.Log().Debug("Opensearch.checkChannelIndex: checking if channel index field is of correct type") - isCorrect, err := os.isFieldCorrect() - if err != nil { - return - } - - if isCorrect { - os.Platform.Log().Debug("Opensearch.checkChannelIndex: channel index field is correct") - atomic.StoreInt32(&os.channelIndexVerified, 1) - } else { - os.Platform.Log().Debug("Opensearch.checkChannelIndex: channel index field is incorrect") - atomic.StoreInt32(&os.channelIndexVerified, 2) - } -} - -func (os *OpensearchInterfaceImpl) isFieldCorrect() (bool, error) { - // We want to check if channel index's "type" field is of type "keyword". - // If the index is in incorrect state, the field would be of type "text". - - os.Platform.Log().Debug("Opensearch.isFieldCorrect: querying ES to check if field is correct") - - ctx, cancel := context.WithTimeout( - context.Background(), - time.Duration(*os.Platform.Config().ElasticsearchSettings.RequestTimeoutSeconds)*time.Second, - ) - defer cancel() - - var mappingFieldResp map[string]struct { - Mappings json.RawMessage `json:"mappings"` - } - - indexName := *os.Platform.Config().ElasticsearchSettings.IndexPrefix + common.IndexBaseChannels - httpResp, err := os.client.Client.Do(ctx, &opensearchapi.MappingFieldReq{ - Fields: []string{"type"}, - Indices: []string{indexName}, - }, &mappingFieldResp) - if err != nil { - os.Platform.Logger().Error("Opensearch: Failed to fetch channels index template", mlog.Err(err)) - return false, err - } - // The case of channels index not existing is fine, - // as whenever the index will be created, it will be created - // with the correct mappings. - if httpResp != nil && httpResp.StatusCode == http.StatusNotFound { - os.Platform.Logger().Debug("Opensearch isFieldCorrect: channel index doesn't exist", mlog.Err(err)) - return true, nil - } - - // this struct is declared here because - // it's not used anywhere outside this function - type channelsTypeFieldMapping struct { - Mappings struct { - Type struct { - Mapping struct { - Type struct { - Type string - } - } - } - } - } - - mappingInterface := mappingFieldResp[indexName] - mappingBytes, err := json.Marshal(mappingInterface) - if err != nil { - os.Platform.Logger().Error("Opensearch: Failed to marshal Opensearch index field mapping", mlog.Err(err)) - return false, err - } - - os.Platform.Log().Debug("Opensearch.isFieldCorrect: channel index type field mapping queried successfully", mlog.String("mapping", string(mappingBytes))) - - var mapping channelsTypeFieldMapping - err = json.Unmarshal(mappingBytes, &mapping) - if err != nil { - os.Platform.Logger().Error("Opensearch: Failed to unmarshal Opensearch index field mapping", mlog.Err(err)) - return false, err - } - - os.Platform.Logger().Debug("Opensearch: Found type of type field as", mlog.String("type", mapping.Mappings.Type.Mapping.Type.Type)) - return mapping.Mappings.Type.Mapping.Type.Type == "keyword", nil -} diff --git a/server/i18n/en.json b/server/i18n/en.json index 64a46d6a292c..ae5b7c51ed9a 100644 --- a/server/i18n/en.json +++ b/server/i18n/en.json @@ -4610,10 +4610,6 @@ "id": "app.channel.delete.app_error", "translation": "Unable to delete the channel." }, - { - "id": "app.channel.elasticsearch_channel_index.notify_admin.message", - "translation": "Your search channel index schema is out of date. We recommend regenerating your channel index.\nClick the `Rebuild Channels Index` button in the [Elasticsearch page via the System Console]({{.ElasticsearchSection}}) to fix this issue.\nSee the Mattermost changelog for more information." - }, { "id": "app.channel.get.existing.app_error", "translation": "Unable to find the existing channel {{.channel_id}}." diff --git a/server/platform/services/searchengine/bleveengine/bleve.go b/server/platform/services/searchengine/bleveengine/bleve.go index a6c4803ded48..8733055833f2 100644 --- a/server/platform/services/searchengine/bleveengine/bleve.go +++ b/server/platform/services/searchengine/bleveengine/bleve.go @@ -335,7 +335,3 @@ func (b *BleveEngine) UpdateConfig(cfg *model.Config) { } b.cfg = cfg } - -func (b *BleveEngine) IsChannelsIndexVerified() bool { - return true -} diff --git a/server/platform/services/searchengine/interface.go b/server/platform/services/searchengine/interface.go index 8d8ea1d59446..69740171d556 100644 --- a/server/platform/services/searchengine/interface.go +++ b/server/platform/services/searchengine/interface.go @@ -50,5 +50,4 @@ type SearchEngineInterface interface { PurgeIndexList(rctx request.CTX, indexes []string) *model.AppError RefreshIndexes(rctx request.CTX) *model.AppError DataRetentionDeleteIndexes(rctx request.CTX, cutoff time.Time) *model.AppError - IsChannelsIndexVerified() bool } diff --git a/server/platform/services/searchengine/mocks/SearchEngineInterface.go b/server/platform/services/searchengine/mocks/SearchEngineInterface.go index 9baa6eaece51..7aefd0cc9321 100644 --- a/server/platform/services/searchengine/mocks/SearchEngineInterface.go +++ b/server/platform/services/searchengine/mocks/SearchEngineInterface.go @@ -407,24 +407,6 @@ func (_m *SearchEngineInterface) IsAutocompletionEnabled() bool { return r0 } -// IsChannelsIndexVerified provides a mock function with given fields: -func (_m *SearchEngineInterface) IsChannelsIndexVerified() bool { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for IsChannelsIndexVerified") - } - - var r0 bool - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - return r0 -} - // IsEnabled provides a mock function with given fields: func (_m *SearchEngineInterface) IsEnabled() bool { ret := _m.Called() From 50c7f1df1279c8e060b08673909c87cc1d42399a Mon Sep 17 00:00:00 2001 From: Christopher Poile Date: Thu, 6 Feb 2025 10:34:34 -0500 Subject: [PATCH 005/164] [MM-62876] Bulk export warnings (#30105) * err -> appErr * record warnings in job.Data, and output them to warnings.txt * tests --- server/channels/app/export.go | 76 +++++++++++++++++++----------- server/channels/app/export_test.go | 37 ++++++++++++++- 2 files changed, 83 insertions(+), 30 deletions(-) diff --git a/server/channels/app/export.go b/server/channels/app/export.go index 0258a5171eb6..0c3df35b5ae8 100644 --- a/server/channels/app/export.go +++ b/server/channels/app/export.go @@ -27,6 +27,8 @@ import ( "github.com/mattermost/mattermost/server/v8/platform/shared/filestore" ) +const warningsFilename = "warnings.txt" + // We use this map to identify the exportable preferences. // Here we link the preference category and name, to the name of the relevant field in the import struct. var exportablePreferences = map[imports.ComparablePreference]string{ @@ -145,62 +147,65 @@ func (a *App) BulkExport(ctx request.CTX, writer io.Writer, outPath string, job } ctx.Logger().Info("Bulk export: exporting teams") - teamNames, err := a.exportAllTeams(ctx, job, writer) - if err != nil { - return err + teamNames, appErr := a.exportAllTeams(ctx, job, writer) + if appErr != nil { + return appErr } ctx.Logger().Info("Bulk export: exporting channels") - if err = a.exportAllChannels(ctx, job, writer, teamNames, opts.IncludeArchivedChannels); err != nil { - return err + if appErr = a.exportAllChannels(ctx, job, writer, teamNames, opts.IncludeArchivedChannels); appErr != nil { + return appErr } ctx.Logger().Info("Bulk export: exporting users") - profilePictures, err := a.exportAllUsers(ctx, job, writer, opts.IncludeArchivedChannels, opts.IncludeProfilePictures) - if err != nil { - return err + profilePictures, appErr := a.exportAllUsers(ctx, job, writer, opts.IncludeArchivedChannels, opts.IncludeProfilePictures) + if appErr != nil { + return appErr } ctx.Logger().Info("Bulk export: exporting bots") - botPPs, err := a.exportAllBots(ctx, job, writer, opts.IncludeProfilePictures) - if err != nil { - return err + botPPs, appErr := a.exportAllBots(ctx, job, writer, opts.IncludeProfilePictures) + if appErr != nil { + return appErr } profilePictures = append(profilePictures, botPPs...) ctx.Logger().Info("Bulk export: exporting posts") - attachments, err := a.exportAllPosts(ctx, job, writer, opts.IncludeAttachments, opts.IncludeArchivedChannels) - if err != nil { - return err + attachments, appErr := a.exportAllPosts(ctx, job, writer, opts.IncludeAttachments, opts.IncludeArchivedChannels) + if appErr != nil { + return appErr } ctx.Logger().Info("Bulk export: exporting emoji") - emojiPaths, err := a.exportCustomEmoji(ctx, job, writer, outPath, "exported_emoji", !opts.CreateArchive) - if err != nil { - return err + emojiPaths, appErr := a.exportCustomEmoji(ctx, job, writer, outPath, "exported_emoji", !opts.CreateArchive) + if appErr != nil { + return appErr } ctx.Logger().Info("Bulk export: exporting direct channels") - if err = a.exportAllDirectChannels(ctx, job, writer, opts.IncludeArchivedChannels); err != nil { - return err + if appErr = a.exportAllDirectChannels(ctx, job, writer, opts.IncludeArchivedChannels); appErr != nil { + return appErr } ctx.Logger().Info("Bulk export: exporting direct posts") - directAttachments, err := a.exportAllDirectPosts(ctx, job, writer, opts.IncludeAttachments, opts.IncludeArchivedChannels) - if err != nil { - return err + directAttachments, appErr := a.exportAllDirectPosts(ctx, job, writer, opts.IncludeAttachments, opts.IncludeArchivedChannels) + if appErr != nil { + return appErr } if opts.IncludeAttachments { ctx.Logger().Info("Bulk export: exporting file attachments") - if err = a.exportAttachments(ctx, attachments, outPath, zipWr); err != nil { - return err + warnings, appErr := a.exportAttachments(ctx, attachments, outPath, zipWr) + if appErr != nil { + return appErr } ctx.Logger().Info("Bulk export: exporting direct file attachments") - if err = a.exportAttachments(ctx, directAttachments, outPath, zipWr); err != nil { - return err + newWarnings, appErr := a.exportAttachments(ctx, directAttachments, outPath, zipWr) + if appErr != nil { + return appErr } + warnings = append(warnings, newWarnings...) totalExportedEmojis := 0 emojisLen := len(emojiPaths) @@ -216,6 +221,18 @@ func (a *App) BulkExport(ctx request.CTX, writer io.Writer, outPath string, job } } + if len(warnings) > 0 { + warningsFile, _ := zipWr.Create(warningsFilename) + for _, warning := range warnings { + _, err := warningsFile.Write([]byte(warning + "\n")) + if err != nil { + return model.NewAppError("BulkExport", "app.export.zip_create.error", + nil, "err="+err.Error(), http.StatusInternalServerError) + } + } + updateJobProgress(ctx.Logger(), a.Srv().Store(), job, "num_warnings", len(warnings)) + } + updateJobProgress(ctx.Logger(), a.Srv().Store(), job, "attachments_exported", len(attachments)+len(directAttachments)+len(emojiPaths)) } @@ -232,12 +249,15 @@ func (a *App) BulkExport(ctx request.CTX, writer io.Writer, outPath string, job return nil } -func (a *App) exportAttachments(ctx request.CTX, attachments []imports.AttachmentImportData, outPath string, zipWr *zip.Writer) *model.AppError { +func (a *App) exportAttachments(ctx request.CTX, attachments []imports.AttachmentImportData, outPath string, + zipWr *zip.Writer) ([]string, *model.AppError) { totalExportedFiles := 0 attachmentsLen := len(attachments) + var warnings []string for _, attachment := range attachments { if err := a.exportFile(ctx, outPath, *attachment.Path, zipWr); err != nil { ctx.Logger().Warn("Unable to export file attachment", mlog.String("attachment_path", *attachment.Path), mlog.Err(err)) + warnings = append(warnings, fmt.Sprintf("Unable to export file attachment, attachment path: %s , error: %s", *attachment.Path, err.Error())) } else { totalExportedFiles++ } @@ -245,7 +265,7 @@ func (a *App) exportAttachments(ctx request.CTX, attachments []imports.Attachmen ctx.Logger().Info("Bulk export: exporting file attachments progress", mlog.Int("total_successfully_exported_files", totalExportedFiles), mlog.Int("total_files_to_export", attachmentsLen)) } } - return nil + return warnings, nil } func (a *App) exportWriteLine(w io.Writer, line *imports.LineImportData) *model.AppError { diff --git a/server/channels/app/export_test.go b/server/channels/app/export_test.go index e746ed6fe2d2..9bd58a0120f8 100644 --- a/server/channels/app/export_test.go +++ b/server/channels/app/export_test.go @@ -938,18 +938,51 @@ func TestExportFileWarnings(t *testing.T) { exportFile, err := os.Create(filepath.Join(dir, "export.zip")) require.NoError(t, err) - defer exportFile.Close() + + job, appErr := th.App.Srv().Jobs.CreateJob(th.Context, model.JobTypeExportProcess, nil) + require.Nil(t, appErr) + ok, appErr := th.App.Srv().Jobs.ClaimJob(job) + require.Nil(t, appErr) + require.True(t, ok) + job, appErr = th.App.Srv().Jobs.GetJob(th.Context, job.Id) + require.Nil(t, appErr) opts := model.BulkExportOpts{ IncludeAttachments: true, CreateArchive: true, } - appErr = th.App.BulkExport(th.Context, exportFile, dir, nil, opts) + appErr = th.App.BulkExport(th.Context, exportFile, dir, job, opts) // should not get an error for the missing file require.Nil(t, appErr) // should get a warning instead: testlib.AssertLog(t, buffer, mlog.LvlWarn.Name, "Unable to export file attachment") + + // should get info in the job data: + job, appErr = th.App.Srv().Jobs.GetJob(th.Context, job.Id) + require.Nil(t, appErr) + warnings, ok := job.Data["num_warnings"] + require.True(t, ok) + require.Equal(t, "1", warnings) + + exportFile.Close() + + // Verify warnings.txt exists in the zip and contains expected content + exportZipPath := filepath.Join(dir, "export.zip") + exportZipFile, err := os.Open(exportZipPath) + require.NoError(t, err) + defer exportZipFile.Close() + + info, err := exportZipFile.Stat() + require.NoError(t, err) + + paths, err := utils.UnzipToPath(exportZipFile, info.Size(), dir) + require.NoError(t, err) + require.Contains(t, paths, filepath.Join(dir, warningsFilename)) + + warningsContent, err := os.ReadFile(filepath.Join(dir, warningsFilename)) + require.NoError(t, err) + require.Contains(t, string(warningsContent), "Unable to export file attachment, attachment path:") }) } } From d34dcc2dda2863041b78d58ea015fd5bc4a17e59 Mon Sep 17 00:00:00 2001 From: Agniva De Sarker Date: Thu, 6 Feb 2025 21:45:57 +0530 Subject: [PATCH 006/164] MM-62901: Apply constLabels on all metrics (#30125) When the client side metrics were added constLabels got missed out. Without this, we cannot do the high level grouping by rings. https://mattermost.atlassian.net/browse/MM-62901 ```release-note NONE ``` --- server/enterprise/metrics/metrics.go | 283 +++++++++++++++------------ 1 file changed, 156 insertions(+), 127 deletions(-) diff --git a/server/enterprise/metrics/metrics.go b/server/enterprise/metrics/metrics.go index 52156a7a0ecb..c5491366e31b 100644 --- a/server/enterprise/metrics/metrics.go +++ b/server/enterprise/metrics/metrics.go @@ -1190,10 +1190,11 @@ func New(ps *platform.PlatformService, driver, dataSource string) *MetricsInterf m.ClientTimeToFirstByte = NewHistogramVec( prometheus.HistogramOpts{ - Namespace: MetricsNamespace, - Subsystem: MetricsSubsystemClientsWeb, - Name: "time_to_first_byte", - Help: "Duration from when a browser starts to request a page from a server until when it starts to receive data in response (seconds)", + Namespace: MetricsNamespace, + Subsystem: MetricsSubsystemClientsWeb, + Name: "time_to_first_byte", + Help: "Duration from when a browser starts to request a page from a server until when it starts to receive data in response (seconds)", + ConstLabels: additionalLabels, }, []string{"platform", "agent"}, m.Platform.Log(), @@ -1202,10 +1203,11 @@ func New(ps *platform.PlatformService, driver, dataSource string) *MetricsInterf m.ClientTimeToLastByte = NewHistogramVec( prometheus.HistogramOpts{ - Namespace: MetricsNamespace, - Subsystem: MetricsSubsystemClientsWeb, - Name: "time_to_last_byte", - Help: "Duration from when a browser starts to request a page from a server until when it receives the last byte of the resource or immediately before the transport connection is closed, whichever comes first. (seconds)", + Namespace: MetricsNamespace, + Subsystem: MetricsSubsystemClientsWeb, + Name: "time_to_last_byte", + Help: "Duration from when a browser starts to request a page from a server until when it receives the last byte of the resource or immediately before the transport connection is closed, whichever comes first. (seconds)", + ConstLabels: additionalLabels, }, []string{"platform", "agent"}, m.Platform.Log(), @@ -1214,11 +1216,12 @@ func New(ps *platform.PlatformService, driver, dataSource string) *MetricsInterf m.ClientTimeToDOMInteractive = NewHistogramVec( prometheus.HistogramOpts{ - Namespace: MetricsNamespace, - Subsystem: MetricsSubsystemClientsWeb, - Name: "dom_interactive", - Help: "Duration from when a browser starts to request a page from a server until when it sets the document's readyState to interactive. (seconds)", - Buckets: []float64{.1, .25, .5, 1, 2.5, 5, 7.5, 10, 12.5, 15}, + Namespace: MetricsNamespace, + Subsystem: MetricsSubsystemClientsWeb, + Name: "dom_interactive", + Help: "Duration from when a browser starts to request a page from a server until when it sets the document's readyState to interactive. (seconds)", + Buckets: []float64{.1, .25, .5, 1, 2.5, 5, 7.5, 10, 12.5, 15}, + ConstLabels: additionalLabels, }, []string{"platform", "agent"}, m.Platform.Log(), @@ -1227,11 +1230,12 @@ func New(ps *platform.PlatformService, driver, dataSource string) *MetricsInterf m.ClientSplashScreenEnd = NewHistogramVec( prometheus.HistogramOpts{ - Namespace: MetricsNamespace, - Subsystem: MetricsSubsystemClientsWeb, - Name: "splash_screen", - Help: "Duration from when a browser starts to request a page from a server until when the splash screen ends. (seconds)", - Buckets: []float64{.1, .25, .5, 1, 2.5, 5, 7.5, 10, 12.5, 15}, + Namespace: MetricsNamespace, + Subsystem: MetricsSubsystemClientsWeb, + Name: "splash_screen", + Help: "Duration from when a browser starts to request a page from a server until when the splash screen ends. (seconds)", + Buckets: []float64{.1, .25, .5, 1, 2.5, 5, 7.5, 10, 12.5, 15}, + ConstLabels: additionalLabels, }, []string{"platform", "agent", "page_type"}, m.Platform.Log(), @@ -1246,7 +1250,8 @@ func New(ps *platform.PlatformService, driver, dataSource string) *MetricsInterf Help: "Duration of how long it takes for any content to be displayed on screen to a user (seconds)", // Extend the range of buckets for this while we get a better idea of the expected range of this metric is - Buckets: []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10, 15, 20}, + Buckets: []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10, 15, 20}, + ConstLabels: additionalLabels, }, []string{"platform", "agent"}, ) @@ -1260,7 +1265,8 @@ func New(ps *platform.PlatformService, driver, dataSource string) *MetricsInterf Help: "Duration of how long it takes for large content to be displayed on screen to a user (seconds)", // Extend the range of buckets for this while we get a better idea of the expected range of this metric is - Buckets: []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10, 15, 20}, + Buckets: []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10, 15, 20}, + ConstLabels: additionalLabels, }, []string{"platform", "agent", "region"}, ) @@ -1268,10 +1274,11 @@ func New(ps *platform.PlatformService, driver, dataSource string) *MetricsInterf m.ClientInteractionToNextPaint = prometheus.NewHistogramVec( prometheus.HistogramOpts{ - Namespace: MetricsNamespace, - Subsystem: MetricsSubsystemClientsWeb, - Name: "interaction_to_next_paint", - Help: "Measure of how long it takes for a user to see the effects of clicking with a mouse, tapping with a touchscreen, or pressing a key on the keyboard (seconds)", + Namespace: MetricsNamespace, + Subsystem: MetricsSubsystemClientsWeb, + Name: "interaction_to_next_paint", + Help: "Measure of how long it takes for a user to see the effects of clicking with a mouse, tapping with a touchscreen, or pressing a key on the keyboard (seconds)", + ConstLabels: additionalLabels, }, []string{"platform", "agent", "interaction"}, ) @@ -1279,10 +1286,11 @@ func New(ps *platform.PlatformService, driver, dataSource string) *MetricsInterf m.ClientCumulativeLayoutShift = prometheus.NewHistogramVec( prometheus.HistogramOpts{ - Namespace: MetricsNamespace, - Subsystem: MetricsSubsystemClientsWeb, - Name: "cumulative_layout_shift", - Help: "Measure of how much a page's content shifts unexpectedly", + Namespace: MetricsNamespace, + Subsystem: MetricsSubsystemClientsWeb, + Name: "cumulative_layout_shift", + Help: "Measure of how much a page's content shifts unexpectedly", + ConstLabels: additionalLabels, }, []string{"platform", "agent"}, ) @@ -1290,10 +1298,11 @@ func New(ps *platform.PlatformService, driver, dataSource string) *MetricsInterf m.ClientLongTasks = prometheus.NewCounterVec( prometheus.CounterOpts{ - Namespace: MetricsNamespace, - Subsystem: MetricsSubsystemClientsWeb, - Name: "long_tasks", - Help: "Counter of the number of times that the browser's main UI thread is blocked for more than 50ms by a single task", + Namespace: MetricsNamespace, + Subsystem: MetricsSubsystemClientsWeb, + Name: "long_tasks", + Help: "Counter of the number of times that the browser's main UI thread is blocked for more than 50ms by a single task", + ConstLabels: additionalLabels, }, []string{"platform", "agent"}, ) @@ -1301,11 +1310,12 @@ func New(ps *platform.PlatformService, driver, dataSource string) *MetricsInterf m.ClientPageLoadDuration = NewHistogramVec( prometheus.HistogramOpts{ - Namespace: MetricsNamespace, - Subsystem: MetricsSubsystemClientsWeb, - Name: "page_load", - Help: "The amount of time from when the browser starts loading the web app until when the web app's load event has finished (seconds)", - Buckets: []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10, 20, 40}, + Namespace: MetricsNamespace, + Subsystem: MetricsSubsystemClientsWeb, + Name: "page_load", + Help: "The amount of time from when the browser starts loading the web app until when the web app's load event has finished (seconds)", + Buckets: []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10, 20, 40}, + ConstLabels: additionalLabels, }, []string{"platform", "agent"}, m.Platform.Log(), @@ -1314,10 +1324,11 @@ func New(ps *platform.PlatformService, driver, dataSource string) *MetricsInterf m.ClientChannelSwitchDuration = prometheus.NewHistogramVec( prometheus.HistogramOpts{ - Namespace: MetricsNamespace, - Subsystem: MetricsSubsystemClientsWeb, - Name: "channel_switch", - Help: "Duration of the time taken from when a user clicks on a channel in the LHS to when posts in that channel become visible (seconds)", + Namespace: MetricsNamespace, + Subsystem: MetricsSubsystemClientsWeb, + Name: "channel_switch", + Help: "Duration of the time taken from when a user clicks on a channel in the LHS to when posts in that channel become visible (seconds)", + ConstLabels: additionalLabels, }, []string{"platform", "agent", "fresh"}, ) @@ -1325,10 +1336,11 @@ func New(ps *platform.PlatformService, driver, dataSource string) *MetricsInterf m.ClientTeamSwitchDuration = prometheus.NewHistogramVec( prometheus.HistogramOpts{ - Namespace: MetricsNamespace, - Subsystem: MetricsSubsystemClientsWeb, - Name: "team_switch", - Help: "Duration of the time taken from when a user clicks on a team in the LHS to when posts in that team become visible (seconds)", + Namespace: MetricsNamespace, + Subsystem: MetricsSubsystemClientsWeb, + Name: "team_switch", + Help: "Duration of the time taken from when a user clicks on a team in the LHS to when posts in that team become visible (seconds)", + ConstLabels: additionalLabels, }, []string{"platform", "agent", "fresh"}, ) @@ -1336,10 +1348,11 @@ func New(ps *platform.PlatformService, driver, dataSource string) *MetricsInterf m.ClientRHSLoadDuration = prometheus.NewHistogramVec( prometheus.HistogramOpts{ - Namespace: MetricsNamespace, - Subsystem: MetricsSubsystemClientsWeb, - Name: "rhs_load", - Help: "Duration of the time taken from when a user clicks to open a thread in the RHS until when posts in that thread become visible (seconds)", + Namespace: MetricsNamespace, + Subsystem: MetricsSubsystemClientsWeb, + Name: "rhs_load", + Help: "Duration of the time taken from when a user clicks to open a thread in the RHS until when posts in that thread become visible (seconds)", + ConstLabels: additionalLabels, }, []string{"platform", "agent"}, ) @@ -1347,10 +1360,11 @@ func New(ps *platform.PlatformService, driver, dataSource string) *MetricsInterf m.ClientGlobalThreadsLoadDuration = prometheus.NewHistogramVec( prometheus.HistogramOpts{ - Namespace: MetricsNamespace, - Subsystem: MetricsSubsystemClientsWeb, - Name: "global_threads_load", - Help: "Duration of the time taken from when a user clicks to open Threads in the LHS until when the global threads view becomes visible (milliseconds)", + Namespace: MetricsNamespace, + Subsystem: MetricsSubsystemClientsWeb, + Name: "global_threads_load", + Help: "Duration of the time taken from when a user clicks to open Threads in the LHS until when the global threads view becomes visible (milliseconds)", + ConstLabels: additionalLabels, }, []string{"platform", "agent"}, ) @@ -1358,110 +1372,120 @@ func New(ps *platform.PlatformService, driver, dataSource string) *MetricsInterf m.MobileClientLoadDuration = prometheus.NewHistogramVec( prometheus.HistogramOpts{ - Namespace: MetricsNamespace, - Subsystem: MetricsSubsystemClientsMobileApp, - Name: "mobile_load", - Help: "Duration of the time taken from when a user opens the app and the app finally loads all relevant information (seconds)", - Buckets: []float64{1, 1.5, 2, 3, 4, 4.5, 5, 5.5, 6, 7.5, 10, 20, 25, 30}, + Namespace: MetricsNamespace, + Subsystem: MetricsSubsystemClientsMobileApp, + Name: "mobile_load", + Help: "Duration of the time taken from when a user opens the app and the app finally loads all relevant information (seconds)", + Buckets: []float64{1, 1.5, 2, 3, 4, 4.5, 5, 5.5, 6, 7.5, 10, 20, 25, 30}, + ConstLabels: additionalLabels, }, []string{"platform"}, ) m.MobileClientNetworkRequestsAverageSpeed = prometheus.NewHistogramVec( prometheus.HistogramOpts{ - Namespace: MetricsNamespace, - Subsystem: MetricsSubsystemClientsMobileApp, - Name: "mobile_network_requests_average_speed", - Help: "Average speed of network requests in megabytes per second (MBps)", - Buckets: []float64{1000, 10000, 50000, 100000, 500000, 1000000, 5000000}, + Namespace: MetricsNamespace, + Subsystem: MetricsSubsystemClientsMobileApp, + Name: "mobile_network_requests_average_speed", + Help: "Average speed of network requests in megabytes per second (MBps)", + Buckets: []float64{1000, 10000, 50000, 100000, 500000, 1000000, 5000000}, + ConstLabels: additionalLabels, }, []string{"platform", "agent", "network_request_group"}, ) m.MobileClientNetworkRequestsEffectiveLatency = prometheus.NewHistogramVec( prometheus.HistogramOpts{ - Namespace: MetricsNamespace, - Subsystem: MetricsSubsystemClientsMobileApp, - Name: "mobile_network_requests_effective_latency", - Help: "Effective latency of network requests in seconds", - Buckets: []float64{0.1, 0.25, 0.5, 1, 2.5, 5, 10}, + Namespace: MetricsNamespace, + Subsystem: MetricsSubsystemClientsMobileApp, + Name: "mobile_network_requests_effective_latency", + Help: "Effective latency of network requests in seconds", + Buckets: []float64{0.1, 0.25, 0.5, 1, 2.5, 5, 10}, + ConstLabels: additionalLabels, }, []string{"platform", "agent", "network_request_group"}, ) m.MobileClientNetworkRequestsElapsedTime = prometheus.NewHistogramVec( prometheus.HistogramOpts{ - Namespace: MetricsNamespace, - Subsystem: MetricsSubsystemClientsMobileApp, - Name: "mobile_network_requests_elapsed_time", - Help: "Total elapsed time of network requests in seconds", - Buckets: []float64{0.1, 0.25, 0.5, 1, 2.5, 5, 10}, + Namespace: MetricsNamespace, + Subsystem: MetricsSubsystemClientsMobileApp, + Name: "mobile_network_requests_elapsed_time", + Help: "Total elapsed time of network requests in seconds", + Buckets: []float64{0.1, 0.25, 0.5, 1, 2.5, 5, 10}, + ConstLabels: additionalLabels, }, []string{"platform", "agent", "network_request_group"}, ) m.MobileClientNetworkRequestsLatency = prometheus.NewHistogramVec( prometheus.HistogramOpts{ - Namespace: MetricsNamespace, - Subsystem: MetricsSubsystemClientsMobileApp, - Name: "mobile_network_requests_latency", - Help: "Latency of network requests in seconds", - Buckets: []float64{0.1, 0.25, 0.5, 1, 2.5, 5, 10}, + Namespace: MetricsNamespace, + Subsystem: MetricsSubsystemClientsMobileApp, + Name: "mobile_network_requests_latency", + Help: "Latency of network requests in seconds", + Buckets: []float64{0.1, 0.25, 0.5, 1, 2.5, 5, 10}, + ConstLabels: additionalLabels, }, []string{"platform", "agent", "network_request_group"}, ) m.MobileClientNetworkRequestsTotalCompressedSize = prometheus.NewHistogramVec( prometheus.HistogramOpts{ - Namespace: MetricsNamespace, - Subsystem: MetricsSubsystemClientsMobileApp, - Name: "mobile_network_requests_total_compressed_size", - Help: "Total compressed size of network requests in bytes", - Buckets: []float64{0.1, 0.5, 1, 2, 5, 10, 20, 50}, + Namespace: MetricsNamespace, + Subsystem: MetricsSubsystemClientsMobileApp, + Name: "mobile_network_requests_total_compressed_size", + Help: "Total compressed size of network requests in bytes", + Buckets: []float64{0.1, 0.5, 1, 2, 5, 10, 20, 50}, + ConstLabels: additionalLabels, }, []string{"platform", "agent", "network_request_group"}, ) m.MobileClientNetworkRequestsTotalParallelRequests = prometheus.NewHistogramVec( prometheus.HistogramOpts{ - Namespace: MetricsNamespace, - Subsystem: MetricsSubsystemClientsMobileApp, - Name: "mobile_network_requests_total_parallel_requests", - Help: "Total number of parallel network requests made", - Buckets: []float64{1, 2, 5, 10, 20, 50, 100}, + Namespace: MetricsNamespace, + Subsystem: MetricsSubsystemClientsMobileApp, + Name: "mobile_network_requests_total_parallel_requests", + Help: "Total number of parallel network requests made", + Buckets: []float64{1, 2, 5, 10, 20, 50, 100}, + ConstLabels: additionalLabels, }, []string{"platform", "agent", "network_request_group"}, ) m.MobileClientNetworkRequestsTotalRequests = prometheus.NewHistogramVec( prometheus.HistogramOpts{ - Namespace: MetricsNamespace, - Subsystem: MetricsSubsystemClientsMobileApp, - Name: "mobile_network_requests_total_requests", - Help: "Total number of network requests made", - Buckets: []float64{1, 2, 5, 10, 20, 50, 100}, + Namespace: MetricsNamespace, + Subsystem: MetricsSubsystemClientsMobileApp, + Name: "mobile_network_requests_total_requests", + Help: "Total number of network requests made", + Buckets: []float64{1, 2, 5, 10, 20, 50, 100}, + ConstLabels: additionalLabels, }, []string{"platform", "agent", "network_request_group"}, ) m.MobileClientNetworkRequestsTotalSequentialRequests = prometheus.NewHistogramVec( prometheus.HistogramOpts{ - Namespace: MetricsNamespace, - Subsystem: MetricsSubsystemClientsMobileApp, - Name: "mobile_network_requests_total_sequential_requests", - Help: "Total number of sequential network requests made", - Buckets: []float64{1, 2, 5, 10, 20, 50, 100}, + Namespace: MetricsNamespace, + Subsystem: MetricsSubsystemClientsMobileApp, + Name: "mobile_network_requests_total_sequential_requests", + Help: "Total number of sequential network requests made", + Buckets: []float64{1, 2, 5, 10, 20, 50, 100}, + ConstLabels: additionalLabels, }, []string{"platform", "agent", "network_request_group"}, ) m.MobileClientNetworkRequestsTotalSize = prometheus.NewHistogramVec( prometheus.HistogramOpts{ - Namespace: MetricsNamespace, - Subsystem: MetricsSubsystemClientsMobileApp, - Name: "mobile_network_requests_total_size", - Help: "Total uncompressed size of network requests in bytes", - Buckets: []float64{1000, 10000, 50000, 100000, 500000, 1000000, 5000000}, + Namespace: MetricsNamespace, + Subsystem: MetricsSubsystemClientsMobileApp, + Name: "mobile_network_requests_total_size", + Help: "Total uncompressed size of network requests in bytes", + Buckets: []float64{1000, 10000, 50000, 100000, 500000, 1000000, 5000000}, + ConstLabels: additionalLabels, }, []string{"platform", "agent", "network_request_group"}, ) @@ -1479,11 +1503,12 @@ func New(ps *platform.PlatformService, driver, dataSource string) *MetricsInterf m.MobileClientChannelSwitchDuration = prometheus.NewHistogramVec( prometheus.HistogramOpts{ - Namespace: MetricsNamespace, - Subsystem: MetricsSubsystemClientsMobileApp, - Name: "mobile_channel_switch", - Help: "Duration of the time taken from when a user clicks on a channel name, and the full channel sreen is loaded (seconds)", - Buckets: []float64{0.150, 0.200, 0.300, 0.400, 0.450, 0.500, 0.550, 0.600, 0.750, 1, 2, 3}, + Namespace: MetricsNamespace, + Subsystem: MetricsSubsystemClientsMobileApp, + Name: "mobile_channel_switch", + Help: "Duration of the time taken from when a user clicks on a channel name, and the full channel sreen is loaded (seconds)", + Buckets: []float64{0.150, 0.200, 0.300, 0.400, 0.450, 0.500, 0.550, 0.600, 0.750, 1, 2, 3}, + ConstLabels: additionalLabels, }, []string{"platform"}, ) @@ -1491,11 +1516,12 @@ func New(ps *platform.PlatformService, driver, dataSource string) *MetricsInterf m.MobileClientTeamSwitchDuration = prometheus.NewHistogramVec( prometheus.HistogramOpts{ - Namespace: MetricsNamespace, - Subsystem: MetricsSubsystemClientsMobileApp, - Name: "mobile_team_switch", - Help: "Duration of the time taken from when a user clicks on a team, and the full categories screen is loaded (seconds)", - Buckets: []float64{0.150, 0.200, 0.250, 0.300, 0.350, 0.400, 0.500, 0.750, 1, 2, 3}, + Namespace: MetricsNamespace, + Subsystem: MetricsSubsystemClientsMobileApp, + Name: "mobile_team_switch", + Help: "Duration of the time taken from when a user clicks on a team, and the full categories screen is loaded (seconds)", + Buckets: []float64{0.150, 0.200, 0.250, 0.300, 0.350, 0.400, 0.500, 0.750, 1, 2, 3}, + ConstLabels: additionalLabels, }, []string{"platform"}, ) @@ -1503,10 +1529,11 @@ func New(ps *platform.PlatformService, driver, dataSource string) *MetricsInterf m.MobileClientSessionMetadataGauge = prometheus.NewGaugeVec( prometheus.GaugeOpts{ - Namespace: MetricsNamespace, - Subsystem: MetricsSubsystemClientsMobileApp, - Name: "mobile_session_metadata", - Help: "The number of mobile sessions in each version, platform and whether they have the notifications disabled", + Namespace: MetricsNamespace, + Subsystem: MetricsSubsystemClientsMobileApp, + Name: "mobile_session_metadata", + Help: "The number of mobile sessions in each version, platform and whether they have the notifications disabled", + ConstLabels: additionalLabels, }, []string{"version", "platform", "notifications_disabled"}, ) @@ -1514,11 +1541,12 @@ func New(ps *platform.PlatformService, driver, dataSource string) *MetricsInterf m.DesktopClientCPUUsage = prometheus.NewHistogramVec( prometheus.HistogramOpts{ - Namespace: MetricsNamespace, - Subsystem: MetricsSubsystemClientsDesktopApp, - Name: "cpu_usage", - Help: "Average CPU usage of a specific process over an interval", - Buckets: []float64{0, 1, 2, 3, 5, 8, 13, 21, 34, 55, 80, 100}, + Namespace: MetricsNamespace, + Subsystem: MetricsSubsystemClientsDesktopApp, + Name: "cpu_usage", + Help: "Average CPU usage of a specific process over an interval", + Buckets: []float64{0, 1, 2, 3, 5, 8, 13, 21, 34, 55, 80, 100}, + ConstLabels: additionalLabels, }, []string{"platform", "version", "processName"}, ) @@ -1526,11 +1554,12 @@ func New(ps *platform.PlatformService, driver, dataSource string) *MetricsInterf m.DesktopClientMemoryUsage = prometheus.NewHistogramVec( prometheus.HistogramOpts{ - Namespace: MetricsNamespace, - Subsystem: MetricsSubsystemClientsDesktopApp, - Name: "memory_usage", - Help: "Memory usage in MB of a specific process", - Buckets: []float64{0, 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 3000, 5000}, + Namespace: MetricsNamespace, + Subsystem: MetricsSubsystemClientsDesktopApp, + Name: "memory_usage", + Help: "Memory usage in MB of a specific process", + Buckets: []float64{0, 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 3000, 5000}, + ConstLabels: additionalLabels, }, []string{"platform", "version", "processName"}, ) From c4718e45420c6a96edfac281a849e8621fba8478 Mon Sep 17 00:00:00 2001 From: Agniva De Sarker Date: Thu, 6 Feb 2025 21:48:08 +0530 Subject: [PATCH 007/164] MM-60211: Bump dependencies (#30005) https://mattermost.atlassian.net/browse/MM-60211 ```release-note NONE ``` Co-authored-by: Mattermost Build --- .../app/platform/web_broadcast_hook_test.go | 6 +- server/go.mod | 165 +++---- server/go.sum | 427 ++++++++++-------- .../platform/shared/filestore/s3_overrides.go | 6 + server/public/go.mod | 43 +- server/public/go.sum | 102 +++-- server/public/model/websocket_message_test.go | 2 +- 7 files changed, 411 insertions(+), 340 deletions(-) diff --git a/server/channels/app/platform/web_broadcast_hook_test.go b/server/channels/app/platform/web_broadcast_hook_test.go index 556286437a87..0573f62379a4 100644 --- a/server/channels/app/platform/web_broadcast_hook_test.go +++ b/server/channels/app/platform/web_broadcast_hook_test.go @@ -72,7 +72,7 @@ func TestRunBroadcastHooks(t *testing.T) { result := hub.runBroadcastHooks(event, webConn, hookIDs, hookArgs) assert.NotSame(t, event, result) - assert.NotSame(t, event.GetData(), result.GetData()) + assert.NotSame(t, model.NewPointer(event.GetData()), model.NewPointer(result.GetData())) assert.Equal(t, map[string]any{}, event.GetData()) assert.Equal(t, result.GetData(), map[string]any{ "changes_made": 1, @@ -119,7 +119,7 @@ func TestRunBroadcastHooks(t *testing.T) { result := hub.runBroadcastHooks(event, webConn, hookIDs, hookArgs) assert.NotSame(t, event, result) - assert.NotSame(t, event.GetData(), result.GetData()) + assert.NotSame(t, model.NewPointer(event.GetData()), model.NewPointer(result.GetData())) assert.Equal(t, event.GetData(), map[string]any{}) assert.Equal(t, result.GetData(), map[string]any{ "changes_made": 1, @@ -141,7 +141,7 @@ func TestRunBroadcastHooks(t *testing.T) { result := hub.runBroadcastHooks(event, webConn, hookIDs, hookArgs) assert.NotSame(t, event, result) - assert.NotSame(t, event.GetData(), result.GetData()) + assert.NotSame(t, model.NewPointer(event.GetData()), model.NewPointer(result.GetData())) assert.Equal(t, event.GetData(), map[string]any{}) assert.Equal(t, result.GetData(), map[string]any{ "changes_made": 10, diff --git a/server/go.mod b/server/go.mod index ec16cae73985..6a7716a1db31 100644 --- a/server/go.mod +++ b/server/go.mod @@ -6,22 +6,22 @@ toolchain go1.22.6 require ( code.sajari.com/docconv/v2 v2.0.0-pre.4 - github.com/Masterminds/semver/v3 v3.2.1 + github.com/Masterminds/semver/v3 v3.3.1 github.com/anthonynsimon/bild v0.14.0 github.com/avct/uasurfer v0.0.0-20240501094946-ca0c4d1e541b - github.com/aws/aws-sdk-go v1.55.5 + github.com/aws/aws-sdk-go v1.55.6 github.com/blang/semver/v4 v4.0.0 - github.com/blevesearch/bleve/v2 v2.4.1 + github.com/blevesearch/bleve/v2 v2.4.4-0.20250115090822-cbafdca08538 github.com/cespare/xxhash/v2 v2.3.0 github.com/dgryski/dgoogauth v0.0.0-20190221195224-5a805980a5f3 github.com/dyatlov/go-opengraph/opengraph v0.0.0-20220524092352-606d7b1e5f8a - github.com/elastic/go-elasticsearch/v8 v8.14.0 - github.com/fatih/color v1.17.0 - github.com/fsnotify/fsnotify v1.7.0 - github.com/getsentry/sentry-go v0.28.1 + github.com/elastic/go-elasticsearch/v8 v8.17.0 + github.com/fatih/color v1.18.0 + github.com/fsnotify/fsnotify v1.8.0 + github.com/getsentry/sentry-go v0.31.1 github.com/go-sql-driver/mysql v1.8.1 github.com/golang-jwt/jwt/v5 v5.2.1 - github.com/golang-migrate/migrate/v4 v4.17.1 + github.com/golang-migrate/migrate/v4 v4.18.2 github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 github.com/golang/mock v1.6.0 github.com/gorilla/handlers v1.5.2 @@ -31,52 +31,52 @@ require ( github.com/h2non/go-is-svg v0.0.0-20160927212452-35e8c4b0612c github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b github.com/hashicorp/go-multierror v1.1.1 - github.com/hashicorp/memberlist v0.5.1 + github.com/hashicorp/memberlist v0.5.2 github.com/icrowley/fake v0.0.0-20240710202011-f797eb4a99c0 github.com/isacikgoz/prompt v0.1.0 github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056 github.com/jmoiron/sqlx v1.4.0 - github.com/klauspost/compress v1.17.9 + github.com/klauspost/compress v1.17.11 github.com/ledongthuc/pdf v0.0.0-20240201131950-da5b75280b06 github.com/lib/pq v1.10.9 github.com/mattermost/go-i18n v1.11.1-0.20211013152124-5c415071e404 github.com/mattermost/gosaml2 v0.8.0 github.com/mattermost/ldap v0.0.0-20231116144001-0f480c025956 github.com/mattermost/logr/v2 v2.0.21 - github.com/mattermost/mattermost/server/public v0.1.5 + github.com/mattermost/mattermost/server/public v0.1.9 github.com/mattermost/morph v1.1.0 github.com/mattermost/rsc v0.0.0-20160330161541-bbaefb05eaa0 github.com/mattermost/squirrel v0.4.0 github.com/mholt/archiver/v3 v3.5.1 github.com/microcosm-cc/bluemonday v1.0.27 - github.com/minio/minio-go/v7 v7.0.74 + github.com/minio/minio-go/v7 v7.0.84 github.com/oov/psd v0.0.0-20220121172623-5db5eafcecbb github.com/opensearch-project/opensearch-go/v4 v4.3.0 github.com/pkg/errors v0.9.1 - github.com/prometheus/client_golang v1.19.1 + github.com/prometheus/client_golang v1.20.5 github.com/prometheus/client_model v0.6.1 - github.com/redis/rueidis v1.0.50 + github.com/redis/rueidis v1.0.53 github.com/reflog/dateconstraints v0.2.1 - github.com/rs/cors v1.11.0 + github.com/rs/cors v1.11.1 github.com/rudderlabs/analytics-go v3.3.3+incompatible github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.8.1 github.com/spf13/viper v1.19.0 - github.com/splitio/go-client/v6 v6.6.0 - github.com/stretchr/testify v1.9.0 + github.com/splitio/go-client/v6 v6.7.0 + github.com/stretchr/testify v1.10.0 github.com/throttled/throttled v2.2.5+incompatible - github.com/tinylib/msgp v1.2.0 + github.com/tinylib/msgp v1.2.5 github.com/tylerb/graceful v1.2.15 github.com/vmihailenco/msgpack/v5 v5.4.1 github.com/wiggin77/merror v1.0.5 github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c - github.com/yuin/goldmark v1.7.4 - golang.org/x/crypto v0.25.0 - golang.org/x/image v0.18.0 - golang.org/x/net v0.27.0 - golang.org/x/sync v0.7.0 - golang.org/x/term v0.22.0 + github.com/yuin/goldmark v1.7.8 + golang.org/x/crypto v0.32.0 + golang.org/x/image v0.23.0 + golang.org/x/net v0.34.0 + golang.org/x/sync v0.10.0 + golang.org/x/term v0.28.0 gopkg.in/mail.v2 v2.3.1 gopkg.in/yaml.v3 v3.0.1 ) @@ -87,34 +87,34 @@ require ( github.com/PuerkitoBio/goquery v1.9.2 // indirect github.com/RoaringBitmap/roaring v1.9.4 // indirect github.com/advancedlogic/GoOse v0.0.0-20231203033844-ae6b36caf275 // indirect - github.com/andybalholm/brotli v1.1.0 // indirect - github.com/andybalholm/cascadia v1.3.2 // indirect + github.com/andybalholm/brotli v1.1.1 // indirect + github.com/andybalholm/cascadia v1.3.3 // indirect github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de // indirect github.com/armon/go-metrics v0.4.1 // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/beevik/etree v1.4.1 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/bits-and-blooms/bitset v1.13.0 // indirect + github.com/bits-and-blooms/bitset v1.20.0 // indirect github.com/bits-and-blooms/bloom/v3 v3.7.0 // indirect - github.com/blevesearch/bleve_index_api v1.1.9 // indirect + github.com/blevesearch/bleve_index_api v1.2.1 // indirect github.com/blevesearch/geo v0.1.20 // indirect - github.com/blevesearch/go-faiss v1.0.19 // indirect + github.com/blevesearch/go-faiss v1.0.24 // indirect github.com/blevesearch/go-porterstemmer v1.0.3 // indirect github.com/blevesearch/gtreap v0.1.1 // indirect github.com/blevesearch/mmap-go v1.0.4 // indirect - github.com/blevesearch/scorch_segment_api/v2 v2.2.14 // indirect + github.com/blevesearch/scorch_segment_api/v2 v2.3.1 // indirect github.com/blevesearch/segment v0.9.1 // indirect github.com/blevesearch/snowballstem v0.9.0 // indirect github.com/blevesearch/upsidedown_store_api v1.0.2 // indirect - github.com/blevesearch/vellum v1.0.10 // indirect - github.com/blevesearch/zapx/v11 v11.3.10 // indirect - github.com/blevesearch/zapx/v12 v12.3.10 // indirect - github.com/blevesearch/zapx/v13 v13.3.10 // indirect - github.com/blevesearch/zapx/v14 v14.3.10 // indirect - github.com/blevesearch/zapx/v15 v15.3.13 // indirect - github.com/blevesearch/zapx/v16 v16.1.4 // indirect + github.com/blevesearch/vellum v1.1.0 // indirect + github.com/blevesearch/zapx/v11 v11.3.11 // indirect + github.com/blevesearch/zapx/v12 v12.3.11 // indirect + github.com/blevesearch/zapx/v13 v13.3.11 // indirect + github.com/blevesearch/zapx/v14 v14.3.11 // indirect + github.com/blevesearch/zapx/v15 v15.3.18 // indirect + github.com/blevesearch/zapx/v16 v16.1.11-0.20250107152255-021e66397612 // indirect github.com/corpix/uarand v0.2.0 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect @@ -128,45 +128,44 @@ require ( github.com/go-ini/ini v1.67.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-resty/resty/v2 v2.13.1 // indirect - github.com/goccy/go-json v0.10.3 // indirect + github.com/go-resty/resty/v2 v2.16.5 // indirect + github.com/goccy/go-json v0.10.4 // indirect github.com/golang/geo v0.0.0-20230421003525-6adc56603217 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/gomodule/redigo v2.0.0+incompatible // indirect - github.com/google/btree v1.1.2 // indirect + github.com/google/btree v1.1.3 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gopherjs/gopherjs v1.17.2 // indirect github.com/gorilla/css v1.0.1 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-hclog v1.6.3 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-metrics v0.5.4 // indirect github.com/hashicorp/go-msgpack/v2 v2.1.2 // indirect - github.com/hashicorp/go-plugin v1.6.1 // indirect - github.com/hashicorp/go-sockaddr v1.0.6 // indirect + github.com/hashicorp/go-plugin v1.6.3 // indirect + github.com/hashicorp/go-sockaddr v1.0.7 // indirect github.com/hashicorp/golang-lru v1.0.2 // indirect - github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/hashicorp/hcl v1.0.0 // indirect - github.com/hashicorp/yamux v0.1.1 // indirect + github.com/hashicorp/yamux v0.1.2 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/isacikgoz/fuzzy v0.2.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect - github.com/jonboulle/clockwork v0.4.0 // indirect + github.com/jonboulle/clockwork v0.5.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/cpuid/v2 v2.2.8 // indirect + github.com/klauspost/cpuid/v2 v2.2.9 // indirect github.com/klauspost/pgzip v1.2.6 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect github.com/levigross/exp-html v0.0.0-20120902181939-8df60c69a8f5 // indirect - github.com/magiconair/properties v1.8.7 // indirect + github.com/magiconair/properties v1.8.9 // indirect github.com/mattermost/xml-roundtrip-validator v0.1.0 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect - github.com/miekg/dns v1.1.61 // indirect + github.com/miekg/dns v1.1.63 // indirect github.com/minio/md5-simd v1.1.2 // indirect - github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect @@ -179,65 +178,63 @@ require ( github.com/otiai10/gosseract/v2 v2.4.1 // indirect github.com/pborman/uuid v1.2.1 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pelletier/go-toml/v2 v2.2.2 // indirect - github.com/philhofer/fwd v1.1.3-0.20240612014219-fbbf4953d986 // indirect - github.com/pierrec/lz4/v4 v4.1.21 // indirect + github.com/pelletier/go-toml/v2 v2.2.3 // indirect + github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c // indirect + github.com/pierrec/lz4/v4 v4.1.22 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/common v0.55.0 // indirect + github.com/prometheus/common v0.62.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect - github.com/redis/go-redis/v9 v9.6.0 // indirect + github.com/redis/go-redis/v9 v9.7.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/richardlehane/mscfb v1.0.4 // indirect - github.com/richardlehane/msoleps v1.0.3 // indirect + github.com/richardlehane/msoleps v1.0.4 // indirect github.com/rivo/uniseg v0.4.7 // indirect - github.com/rs/xid v1.5.0 // indirect + github.com/rs/xid v1.6.0 // indirect github.com/russellhaering/goxmldsig v1.4.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/sagikazarmark/locafero v0.6.0 // indirect + github.com/sagikazarmark/locafero v0.7.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect github.com/segmentio/backo-go v1.1.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect - github.com/spf13/afero v1.11.0 // indirect - github.com/spf13/cast v1.6.0 // indirect + github.com/spf13/afero v1.12.0 // indirect + github.com/spf13/cast v1.7.1 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/splitio/go-split-commons/v6 v6.0.0 // indirect + github.com/splitio/go-split-commons/v6 v6.1.0 // indirect github.com/splitio/go-toolkit/v5 v5.4.0 // indirect github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/subosito/gotenv v1.6.0 // indirect - github.com/tidwall/gjson v1.17.1 // indirect + github.com/tidwall/gjson v1.18.0 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect github.com/ulikunitz/xz v0.5.12 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/wiggin77/srslog v1.0.1 // indirect github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect - go.etcd.io/bbolt v1.3.10 // indirect - go.opentelemetry.io/otel v1.28.0 // indirect - go.opentelemetry.io/otel/metric v1.28.0 // indirect - go.opentelemetry.io/otel/trace v1.28.0 // indirect + go.etcd.io/bbolt v1.3.11 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/otel v1.34.0 // indirect + go.opentelemetry.io/otel/metric v1.34.0 // indirect + go.opentelemetry.io/otel/trace v1.34.0 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect - golang.org/x/mod v0.19.0 // indirect - golang.org/x/sys v0.24.0 // indirect - golang.org/x/text v0.16.0 // indirect - golang.org/x/tools v0.23.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240722135656-d784300faade // indirect - google.golang.org/grpc v1.65.0 // indirect - google.golang.org/protobuf v1.34.2 // indirect + golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 // indirect + golang.org/x/mod v0.22.0 // indirect + golang.org/x/sys v0.29.0 // indirect + golang.org/x/text v0.21.0 // indirect + golang.org/x/tools v0.29.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250124145028-65684f501c47 // indirect + google.golang.org/grpc v1.70.0 // indirect + google.golang.org/protobuf v1.36.4 // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - modernc.org/gc/v3 v3.0.0-20240722195230-4a140ff9c08e // indirect - modernc.org/libc v1.55.3 // indirect - modernc.org/mathutil v1.6.0 // indirect - modernc.org/memory v1.8.0 // indirect - modernc.org/sqlite v1.31.1 // indirect - modernc.org/strutil v1.2.0 // indirect - modernc.org/token v1.1.0 // indirect + modernc.org/libc v1.61.9 // indirect + modernc.org/mathutil v1.7.1 // indirect + modernc.org/memory v1.8.2 // indirect + modernc.org/sqlite v1.34.5 // indirect ) // Hack to prevent the willf/bitset module from being upgraded to 1.2.0. @@ -245,6 +242,10 @@ require ( // github.com/bits-and-blooms/bitset and a couple of dependent repos are yet // to update their module paths. exclude ( + // goquery requires go >= 1.23. Remove this once we upgrade to 1.23 + github.com/PuerkitoBio/goquery v1.9.3 + github.com/PuerkitoBio/goquery v1.10.0 + github.com/PuerkitoBio/goquery v1.10.1 github.com/RoaringBitmap/roaring v0.7.0 github.com/RoaringBitmap/roaring v0.7.1 github.com/dyatlov/go-opengraph v0.0.0-20210112100619-dae8665a5b09 diff --git a/server/go.sum b/server/go.sum index 2f5bc45664b5..c48c6533b697 100644 --- a/server/go.sum +++ b/server/go.sum @@ -15,8 +15,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/JalfResi/justext v0.0.0-20221106200834-be571e3e3052 h1:8T2zMbhLBbH9514PIQVHdsGhypMrsB4CxwbldKA9sBA= github.com/JalfResi/justext v0.0.0-20221106200834-be571e3e3052/go.mod h1:0SURuH1rsE8aVWvutuMZghRNrNrYEUzibzJfhEYR8L0= -github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= -github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4= +github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/PuerkitoBio/goquery v1.4.1/go.mod h1:T9ezsOHcCrDCgA8aF1Cqr3sSYbO/xgdy8/R/XiIMAhA= github.com/PuerkitoBio/goquery v1.9.2 h1:4/wZksC3KgkQw7SQgkKotmKljk0M6V8TUvA8Wb4yPeE= github.com/PuerkitoBio/goquery v1.9.2/go.mod h1:GHPCaP0ODyyxqcNoFGYlAprUFH81NuRPd0GX3Zu2Mvk= @@ -28,12 +28,13 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= -github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= -github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= +github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= +github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= -github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss= -github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU= +github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM= +github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/anthonynsimon/bild v0.14.0 h1:IFRkmKdNdqmexXHfEU7rPlAmdUZ8BDZEGtGHDnGWync= github.com/anthonynsimon/bild v0.14.0/go.mod h1:hcvEAyBjTW69qkKJTfpcDQ83sSZHxwOunsseDfeQhUs= @@ -44,8 +45,8 @@ github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJ github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= github.com/avct/uasurfer v0.0.0-20240501094946-ca0c4d1e541b h1:F1IDheTR2BqSIznXwfgxursfutFj5pNezhneejTPUYQ= github.com/avct/uasurfer v0.0.0-20240501094946-ca0c4d1e541b/go.mod h1:s+GCtuP4kZNxh1WGoqdWI1+PbluBcycrMMWuKQ9e5Nk= -github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= -github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk= +github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= @@ -57,48 +58,48 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/bits-and-blooms/bitset v1.12.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= -github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= -github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/bits-and-blooms/bitset v1.20.0 h1:2F+rfL86jE2d/bmw7OhqUg2Sj/1rURkBn3MdfoPyRVU= +github.com/bits-and-blooms/bitset v1.20.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/bits-and-blooms/bloom/v3 v3.7.0 h1:VfknkqV4xI+PsaDIsoHueyxVDZrfvMn56jeWUzvzdls= github.com/bits-and-blooms/bloom/v3 v3.7.0/go.mod h1:VKlUSvp0lFIYqxJjzdnSsZEw4iHb1kOL2tfHTgyJBHg= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= -github.com/blevesearch/bleve/v2 v2.4.1 h1:8QWqsifq693mN3h6cSigKqkKUsUfv5hu0FDgz/4bFuA= -github.com/blevesearch/bleve/v2 v2.4.1/go.mod h1:Ezmvsouspi+uVwnDzjIsCeUIT0WuBKlicP5JZnExWzo= -github.com/blevesearch/bleve_index_api v1.1.9 h1:Cpq0Lp3As0Gfk3+PmcoNDRKeI50C5yuFNpj0YlN/bOE= -github.com/blevesearch/bleve_index_api v1.1.9/go.mod h1:PbcwjIcRmjhGbkS/lJCpfgVSMROV6TRubGGAODaK1W8= +github.com/blevesearch/bleve/v2 v2.4.4-0.20250115090822-cbafdca08538 h1:61w7gtAsihs4iJEc0d65SgDrr4+XhzjHvwZd1r5oXQo= +github.com/blevesearch/bleve/v2 v2.4.4-0.20250115090822-cbafdca08538/go.mod h1:Ah9SCMQTsRHCgaNKyuUzbnJLZ9UL8lZSQ03sVs8TmMM= +github.com/blevesearch/bleve_index_api v1.2.1 h1:IuXwLvmyp7I7+e0FOA68gcHHLfzSQ4AqQ8wVab5uxk0= +github.com/blevesearch/bleve_index_api v1.2.1/go.mod h1:rKQDl4u51uwafZxFrPD1R7xFOwKnzZW7s/LSeK4lgo0= github.com/blevesearch/geo v0.1.20 h1:paaSpu2Ewh/tn5DKn/FB5SzvH0EWupxHEIwbCk/QPqM= github.com/blevesearch/geo v0.1.20/go.mod h1:DVG2QjwHNMFmjo+ZgzrIq2sfCh6rIHzy9d9d0B59I6w= -github.com/blevesearch/go-faiss v1.0.19 h1:UKoP8hS7DVsVSRRloNJb4qPfe2UQ99pP4D3oXd23g2A= -github.com/blevesearch/go-faiss v1.0.19/go.mod h1:jrxHrbl42X/RnDPI+wBoZU8joxxuRwedrxqswQ3xfU8= +github.com/blevesearch/go-faiss v1.0.24 h1:K79IvKjoKHdi7FdiXEsAhxpMuns0x4fM0BO93bW5jLI= +github.com/blevesearch/go-faiss v1.0.24/go.mod h1:OMGQwOaRRYxrmeNdMrXJPvVx8gBnvE5RYrr0BahNnkk= github.com/blevesearch/go-porterstemmer v1.0.3 h1:GtmsqID0aZdCSNiY8SkuPJ12pD4jI+DdXTAn4YRcHCo= github.com/blevesearch/go-porterstemmer v1.0.3/go.mod h1:angGc5Ht+k2xhJdZi511LtmxuEf0OVpvUUNrwmM1P7M= github.com/blevesearch/gtreap v0.1.1 h1:2JWigFrzDMR+42WGIN/V2p0cUvn4UP3C4Q5nmaZGW8Y= github.com/blevesearch/gtreap v0.1.1/go.mod h1:QaQyDRAT51sotthUWAH4Sj08awFSSWzgYICSZ3w0tYk= github.com/blevesearch/mmap-go v1.0.4 h1:OVhDhT5B/M1HNPpYPBKIEJaD0F3Si+CrEKULGCDPWmc= github.com/blevesearch/mmap-go v1.0.4/go.mod h1:EWmEAOmdAS9z/pi/+Toxu99DnsbhG1TIxUoRmJw/pSs= -github.com/blevesearch/scorch_segment_api/v2 v2.2.14 h1:fgMLMpGWR7u2TdRm7XSZVWhPvMAcdYHh25Lq1fQ6Fjo= -github.com/blevesearch/scorch_segment_api/v2 v2.2.14/go.mod h1:B7+a7vfpY4NsjuTkpv/eY7RZ91Xr90VaJzT2t7upZN8= +github.com/blevesearch/scorch_segment_api/v2 v2.3.1 h1:jjexIzwOdBtC9MlUceNErYHepLvoKxTdA5atbeZSRWE= +github.com/blevesearch/scorch_segment_api/v2 v2.3.1/go.mod h1:Np3Y03rsemM5TsyFxQ3wy+tG97EcviLTbp2S5W0tpRY= github.com/blevesearch/segment v0.9.1 h1:+dThDy+Lvgj5JMxhmOVlgFfkUtZV2kw49xax4+jTfSU= github.com/blevesearch/segment v0.9.1/go.mod h1:zN21iLm7+GnBHWTao9I+Au/7MBiL8pPFtJBJTsk6kQw= github.com/blevesearch/snowballstem v0.9.0 h1:lMQ189YspGP6sXvZQ4WZ+MLawfV8wOmPoD/iWeNXm8s= github.com/blevesearch/snowballstem v0.9.0/go.mod h1:PivSj3JMc8WuaFkTSRDW2SlrulNWPl4ABg1tC/hlgLs= github.com/blevesearch/upsidedown_store_api v1.0.2 h1:U53Q6YoWEARVLd1OYNc9kvhBMGZzVrdmaozG2MfoB+A= github.com/blevesearch/upsidedown_store_api v1.0.2/go.mod h1:M01mh3Gpfy56Ps/UXHjEO/knbqyQ1Oamg8If49gRwrQ= -github.com/blevesearch/vellum v1.0.10 h1:HGPJDT2bTva12hrHepVT3rOyIKFFF4t7Gf6yMxyMIPI= -github.com/blevesearch/vellum v1.0.10/go.mod h1:ul1oT0FhSMDIExNjIxHqJoGpVrBpKCdgDQNxfqgJt7k= -github.com/blevesearch/zapx/v11 v11.3.10 h1:hvjgj9tZ9DeIqBCxKhi70TtSZYMdcFn7gDb71Xo/fvk= -github.com/blevesearch/zapx/v11 v11.3.10/go.mod h1:0+gW+FaE48fNxoVtMY5ugtNHHof/PxCqh7CnhYdnMzQ= -github.com/blevesearch/zapx/v12 v12.3.10 h1:yHfj3vXLSYmmsBleJFROXuO08mS3L1qDCdDK81jDl8s= -github.com/blevesearch/zapx/v12 v12.3.10/go.mod h1:0yeZg6JhaGxITlsS5co73aqPtM04+ycnI6D1v0mhbCs= -github.com/blevesearch/zapx/v13 v13.3.10 h1:0KY9tuxg06rXxOZHg3DwPJBjniSlqEgVpxIqMGahDE8= -github.com/blevesearch/zapx/v13 v13.3.10/go.mod h1:w2wjSDQ/WBVeEIvP0fvMJZAzDwqwIEzVPnCPrz93yAk= -github.com/blevesearch/zapx/v14 v14.3.10 h1:SG6xlsL+W6YjhX5N3aEiL/2tcWh3DO75Bnz77pSwwKU= -github.com/blevesearch/zapx/v14 v14.3.10/go.mod h1:qqyuR0u230jN1yMmE4FIAuCxmahRQEOehF78m6oTgns= -github.com/blevesearch/zapx/v15 v15.3.13 h1:6EkfaZiPlAxqXz0neniq35my6S48QI94W/wyhnpDHHQ= -github.com/blevesearch/zapx/v15 v15.3.13/go.mod h1:Turk/TNRKj9es7ZpKK95PS7f6D44Y7fAFy8F4LXQtGg= -github.com/blevesearch/zapx/v16 v16.1.4 h1:TBQfG77g2UUXwfjOVcEtB9pXkg6JBmGXkeZKI67+TiA= -github.com/blevesearch/zapx/v16 v16.1.4/go.mod h1:+Q+Z89Iv7ewhdX2jyE6Qs/RUnN4tZuokaQ0xvTaFmx8= +github.com/blevesearch/vellum v1.1.0 h1:CinkGyIsgVlYf8Y2LUQHvdelgXr6PYuvoDIajq6yR9w= +github.com/blevesearch/vellum v1.1.0/go.mod h1:QgwWryE8ThtNPxtgWJof5ndPfx0/YMBh+W2weHKPw8Y= +github.com/blevesearch/zapx/v11 v11.3.11 h1:r6/wFHFAKWvXJb82f5aO53l6p+gRH6eiX7S1tb3VGc0= +github.com/blevesearch/zapx/v11 v11.3.11/go.mod h1:0+gW+FaE48fNxoVtMY5ugtNHHof/PxCqh7CnhYdnMzQ= +github.com/blevesearch/zapx/v12 v12.3.11 h1:GBBAmXesxXLV5UZ+FZ0qILb7HPssT+kxEkbPPfp5HPM= +github.com/blevesearch/zapx/v12 v12.3.11/go.mod h1:0yeZg6JhaGxITlsS5co73aqPtM04+ycnI6D1v0mhbCs= +github.com/blevesearch/zapx/v13 v13.3.11 h1:H5ZvgS1qM1XKzsAuwp3kvDfh5sJFu9bLH/B8U6Im5e8= +github.com/blevesearch/zapx/v13 v13.3.11/go.mod h1:w2wjSDQ/WBVeEIvP0fvMJZAzDwqwIEzVPnCPrz93yAk= +github.com/blevesearch/zapx/v14 v14.3.11 h1:pg+c/YFzMJ32GkOwLzH/HAQ/GBr6y1Ar7/K5ZQpxTNo= +github.com/blevesearch/zapx/v14 v14.3.11/go.mod h1:qqyuR0u230jN1yMmE4FIAuCxmahRQEOehF78m6oTgns= +github.com/blevesearch/zapx/v15 v15.3.18 h1:yJcQnQyHGNF6rAiwq85OHn3HaXo26t7vgd83RclEw7U= +github.com/blevesearch/zapx/v15 v15.3.18/go.mod h1:vXRQzJJvlGVCdmOD5hg7t7JdjUT5DmDPhsAfjvtzIq8= +github.com/blevesearch/zapx/v16 v16.1.11-0.20250107152255-021e66397612 h1:LhORiqEVyUPUrVETzmmVuT0Yudsz2R3qGLFJWUpMsQo= +github.com/blevesearch/zapx/v16 v16.1.11-0.20250107152255-021e66397612/go.mod h1:+FIylxb+5Z/sFVmNaGpppGLHKBMUEnPSbkKoi+izER8= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= @@ -116,8 +117,9 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/corpix/uarand v0.2.0 h1:U98xXwud/AVuCpkpgfPF7J5TQgr7R5tqT8VZP5KWbzE= github.com/corpix/uarand v0.2.0/go.mod h1:/3Z1QIqWkDIhf6XWn/08/uMHoQ8JUoTIKc2iPchBOmM= -github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -137,12 +139,12 @@ github.com/dyatlov/go-opengraph/opengraph v0.0.0-20220524092352-606d7b1e5f8a h1: github.com/dyatlov/go-opengraph/opengraph v0.0.0-20220524092352-606d7b1e5f8a/go.mod h1:emQhSYTXqB0xxjLITTw4EaWZ+8IIQYw+kx9GqNUKdLg= github.com/elastic/elastic-transport-go/v8 v8.6.0 h1:Y2S/FBjx1LlCv5m6pWAF2kDJAHoSjSRSJCApolgfthA= github.com/elastic/elastic-transport-go/v8 v8.6.0/go.mod h1:YLHer5cj0csTzNFXoNQ8qhtGY1GTvSqPnKWKaqQE3Hk= -github.com/elastic/go-elasticsearch/v8 v8.14.0 h1:1ywU8WFReLLcxE1WJqii3hTtbPUE2hc38ZK/j4mMFow= -github.com/elastic/go-elasticsearch/v8 v8.14.0/go.mod h1:WRvnlGkSuZyp83M2U8El/LGXpCjYLrvlkSgkAH4O5I4= +github.com/elastic/go-elasticsearch/v8 v8.17.0 h1:e9cWksE/Fr7urDRmGPGp47Nsp4/mvNOrU8As1l2HQQ0= +github.com/elastic/go-elasticsearch/v8 v8.17.0/go.mod h1:lGMlgKIbYoRvay3xWBeKahAiJOgmFDsjZC39nmO3H64= github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= -github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/fatih/set v0.2.1 h1:nn2CaJyknWE/6txyUDGwysr3G5QC6xWB/PtVjPBbeaA= github.com/fatih/set v0.2.1/go.mod h1:+RKtMCH+favT2+3YecHGxcc0b4KyVWA1QWWJUs4E0CI= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= @@ -153,10 +155,10 @@ github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiD github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= -github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= -github.com/getsentry/sentry-go v0.28.1 h1:zzaSm/vHmGllRM6Tpx1492r0YDzauArdBfkJRtY6P5k= -github.com/getsentry/sentry-go v0.28.1/go.mod h1:1fQZ+7l7eeJ3wYi82q5Hg8GqAPgefRq+FP/QhafYVgg= +github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= +github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/getsentry/sentry-go v0.31.1 h1:ELVc0h7gwyhnXHDouXkhqTFSO5oslsRDk0++eyE0KJ4= +github.com/getsentry/sentry-go v0.31.1/go.mod h1:CYNcMMz73YigoHljQRG+qPF+eMq8gG72XcGN/p71BAY= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gigawattio/window v0.0.0-20180317192513-0f5467e35573 h1:u8AQ9bPa9oC+8/A/jlWouakhIvkFfuxgIIRjiy8av7I= github.com/gigawattio/window v0.0.0-20180317192513-0f5467e35573/go.mod h1:eBvb3i++NHDH4Ugo9qCvMw8t0mTSctaEa5blJbWcNxs= @@ -170,26 +172,28 @@ github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-resty/resty/v2 v2.0.0/go.mod h1:dZGr0i9PLlaaTD4H/hoZIDjQ+r6xq8mgbRzHZf7f2J8= -github.com/go-resty/resty/v2 v2.13.1 h1:x+LHXBI2nMB1vqndymf26quycC4aggYJ7DECYbiz03g= -github.com/go-resty/resty/v2 v2.13.1/go.mod h1:GznXlLxkq6Nh4sU59rPmUw3VtgpO3aS96ORAI6Q7d+0= +github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM= +github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA= github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= -github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM= +github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= -github.com/golang-migrate/migrate/v4 v4.17.1 h1:4zQ6iqL6t6AiItphxJctQb3cFqWiSpMnX7wLTPnnYO4= -github.com/golang-migrate/migrate/v4 v4.17.1/go.mod h1:m8hinFyWBn0SA4QKHuKh175Pm9wjmxj3S2Mia7dbXzM= +github.com/golang-migrate/migrate/v4 v4.18.2 h1:2VSCMz7x7mjyTXx3m2zPokOY82LTRgxK1yQYKo6wWQ8= +github.com/golang-migrate/migrate/v4 v4.18.2/go.mod h1:2CM6tJvn2kqPXwnXO/d3rAQYiyoIm180VsO8PRX6Rpk= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/geo v0.0.0-20230421003525-6adc56603217 h1:HKlyj6in2JV6wVkmQ4XmG/EIm+SCYlPZ+V4GWit7Z+I= @@ -203,6 +207,13 @@ github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+Licev github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -211,11 +222,13 @@ github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= -github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= +github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -259,28 +272,28 @@ github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVH github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-metrics v0.5.4 h1:8mmPiIJkTPPEbAiV97IxdAGNdRdaWwVap1BU6elejKY= +github.com/hashicorp/go-metrics v0.5.4/go.mod h1:CG5yz4NZ/AI/aQt9Ucm/vdBnbh7fvmv4lxZ350i+QQI= github.com/hashicorp/go-msgpack/v2 v2.1.2 h1:4Ee8FTp834e+ewB71RDrQ0VKpyFdrKOjvYtnQ/ltVj0= github.com/hashicorp/go-msgpack/v2 v2.1.2/go.mod h1:upybraOAblm4S7rx0+jeNy+CWWhzywQsSRV5033mMu4= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-plugin v1.6.1 h1:P7MR2UP6gNKGPp+y7EZw2kOiq4IR9WiqLvp0XOsVdwI= -github.com/hashicorp/go-plugin v1.6.1/go.mod h1:XPHFku2tFo3o3QKFgSYo+cghcUhw1NA1hZyMK0PWAw0= +github.com/hashicorp/go-plugin v1.6.3 h1:xgHB+ZUSYeuJi96WtxEjzi23uh7YQpznjGh0U0UUrwg= +github.com/hashicorp/go-plugin v1.6.3/go.mod h1:MRobyh+Wc/nYy1V4KAXUiYfzxoYhs7V1mlH1Z7iY2h0= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= -github.com/hashicorp/go-sockaddr v1.0.6 h1:RSG8rKU28VTUTvEKghe5gIhIQpv8evvNpnDEyqO4u9I= -github.com/hashicorp/go-sockaddr v1.0.6/go.mod h1:uoUUmtwU7n9Dv3O4SNLeFvg0SxQ3lyjsj6+CCykpaxI= +github.com/hashicorp/go-sockaddr v1.0.7 h1:G+pTkSO01HpR5qCxg7lxfsFEZaG+C0VssTy/9dbT+Fw= +github.com/hashicorp/go-sockaddr v1.0.7/go.mod h1:FZQbEYa1pxkQ7WLpyXJ6cbjpT8q0YgQaK/JakXqGyWw= github.com/hashicorp/go-uuid v1.0.0 h1:RS8zrF7PhGwyNPOtxSClXXj9HA8feRnJzgnI1RJCSnM= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= -github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/memberlist v0.5.1 h1:mk5dRuzeDNis2bi6LLoQIXfMH7JQvAzt3mQD0vNZZUo= -github.com/hashicorp/memberlist v0.5.1/go.mod h1:zGDXV6AqbDTKTM6yxW0I4+JtFzZAJVoIPvss4hV8F24= -github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= -github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= +github.com/hashicorp/memberlist v0.5.2 h1:rJoNPWZ0juJBgqn48gjy59K5H4rNgvUoM1kUD7bXiuI= +github.com/hashicorp/memberlist v0.5.2/go.mod h1:Ri9p/tRShbjYnpNf4FFPXG7wxEGY4Nrcn6E7jrVa//4= +github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8= +github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns= github.com/icrowley/fake v0.0.0-20240710202011-f797eb4a99c0 h1:ufr2e4uIgz/Ft0RPudkFMyVrp77buvTFxqoDvwNGVSk= github.com/icrowley/fake v0.0.0-20240710202011-f797eb4a99c0/go.mod h1:dQ6TM/OGAe+cMws81eTe4Btv1dKxfPZ2CX+YaAFAPN4= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= @@ -302,27 +315,32 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfC github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= -github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4= -github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc= +github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I= +github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= -github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= -github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY= +github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU= github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -347,8 +365,8 @@ github.com/levigross/exp-html v0.0.0-20120902181939-8df60c69a8f5/go.mod h1:QMe2w github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= -github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= -github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM= +github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mattermost/go-i18n v1.11.1-0.20211013152124-5c415071e404 h1:Khvh6waxG1cHc4Cz5ef9n3XVCxRWpAKUtqg9PJl5+y8= github.com/mattermost/go-i18n v1.11.1-0.20211013152124-5c415071e404/go.mod h1:RyS7FDNQlzF1PsjbJWHRI35exqaKGSO9qD4iv8QjE34= @@ -358,8 +376,8 @@ github.com/mattermost/ldap v0.0.0-20231116144001-0f480c025956 h1:Y1Tu/swM31pVwwb github.com/mattermost/ldap v0.0.0-20231116144001-0f480c025956/go.mod h1:SRl30Lb7/QoYyohYeVBuqYvvmXSZJxZgiV3Zf6VbxjI= github.com/mattermost/logr/v2 v2.0.21 h1:CMHsP+nrbRlEC4g7BwOk1GAnMtHkniFhlSQPXy52be4= github.com/mattermost/logr/v2 v2.0.21/go.mod h1:kZkB/zqKL9e+RY5gB3vGpsyenC+TpuiOenjMkvJJbzc= -github.com/mattermost/mattermost/server/public v0.1.5 h1:hi7cKEAL/sMDu0WlqcQ8qJWVRgf1EcrS1vHBRjCOH10= -github.com/mattermost/mattermost/server/public v0.1.5/go.mod h1:PDPb/iqzJJ5ZvK/m70oDF55AXN/cOvVFj96Yu4e6j+Q= +github.com/mattermost/mattermost/server/public v0.1.9 h1:l/OKPRVuFeqL0yqRVC/JpveG5sLNKcT9llxqMkO9e+s= +github.com/mattermost/mattermost/server/public v0.1.9/go.mod h1:SkTKbMul91Rq0v2dIxe8mqzUOY+3KwlwwLmAlxDfGCk= github.com/mattermost/morph v1.1.0 h1:Q9vrJbeM3s2jfweGheq12EFIzdNp9a/6IovcbvOQ6Cw= github.com/mattermost/morph v1.1.0/go.mod h1:gD+EaqX2UMyyuzmF4PFh4r33XneQ8Nzi+0E8nXjMa3A= github.com/mattermost/rsc v0.0.0-20160330161541-bbaefb05eaa0 h1:G9tL6JXRBMzjuD1kkBtcnd42kUiT6QDwxfFYu7adM6o= @@ -371,11 +389,10 @@ github.com/mattermost/xml-roundtrip-validator v0.1.0/go.mod h1:qccnGMcpgwcNaBnxq github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= @@ -392,14 +409,12 @@ github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssn github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk= github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA= -github.com/miekg/dns v1.1.61 h1:nLxbwF3XxhwVSm8g9Dghm9MHPaUZuqhPiGL+675ZmEs= -github.com/miekg/dns v1.1.61/go.mod h1:mnAarhS3nWaW+NVP2wTkYVIZyHNJ098SJZUki3eykwQ= +github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY= +github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= -github.com/minio/minio-go/v7 v7.0.74 h1:fTo/XlPBTSpo3BAMshlwKL5RspXRv9us5UeHEGYCFe0= -github.com/minio/minio-go/v7 v7.0.74/go.mod h1:qydcVzV8Hqtj1VtEocfxbmVFa2siu6HGa+LDEPogjD8= -github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= -github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= +github.com/minio/minio-go/v7 v7.0.84 h1:D1HVmAF8JF8Bpi6IU4V9vIEj+8pc+xU88EWMs2yed0E= +github.com/minio/minio-go/v7 v7.0.84/go.mod h1:57YXpvc5l3rjPdhqNrDsvVlY0qPI6UTk1bflAe+9doY= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -414,6 +429,7 @@ github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOl github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= @@ -444,13 +460,13 @@ github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtP github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= -github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= -github.com/philhofer/fwd v1.1.3-0.20240612014219-fbbf4953d986 h1:jYi87L8j62qkXzaYHAQAhEapgukhenIMZRBKTNRLHJ4= -github.com/philhofer/fwd v1.1.3-0.20240612014219-fbbf4953d986/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= +github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= +github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= +github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c h1:dAMKvw0MlJT1GshSTtih8C2gDs04w8dReiOGXrGLNoY= +github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= -github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU= +github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= @@ -465,8 +481,10 @@ github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= -github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= -github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= +github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -475,18 +493,22 @@ github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQy github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= -github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= -github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= +github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= -github.com/redis/go-redis/v9 v9.6.0 h1:NLck+Rab3AOTHw21CGRpvQpgTrAU4sgdCswqGtlhGRA= -github.com/redis/go-redis/v9 v9.6.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= -github.com/redis/rueidis v1.0.50 h1:UdsB/2EadJMGFIUuzxqFuWM2BSjXt8jYtml6eXkhJLE= -github.com/redis/rueidis v1.0.50/go.mod h1:by+34b0cFXndxtYmPAHpoTHO5NkosDlBvhexoTURIxM= +github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E= +github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw= +github.com/redis/rueidis v1.0.53 h1:r3eT4bp7Nyt+kSldT2po/EO9YeawHfZDY9TJBrHRLD4= +github.com/redis/rueidis v1.0.53/go.mod h1:by+34b0cFXndxtYmPAHpoTHO5NkosDlBvhexoTURIxM= github.com/reflog/dateconstraints v0.2.1 h1:Hz1n2Q1vEm0Rj5gciDQcCN1iPBwfFjxUJy32NknGP/s= github.com/reflog/dateconstraints v0.2.1/go.mod h1:Ax8AxTBcJc3E/oVS2hd2j7RDM/5MDtuPwuR7lIHtPLo= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= @@ -494,20 +516,20 @@ github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qq github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM= github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk= github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= -github.com/richardlehane/msoleps v1.0.3 h1:aznSZzrwYRl3rLKRT3gUk9am7T/mLNSnJINvN0AQoVM= -github.com/richardlehane/msoleps v1.0.3/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= +github.com/richardlehane/msoleps v1.0.4 h1:WuESlvhX3gH2IHcd8UqyCuFY5yiq/GR/yqaSM/9/g00= +github.com/richardlehane/msoleps v1.0.4/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= -github.com/rs/cors v1.11.0 h1:0B9GE/r9Bc2UxRMMtymBkHTenPkHDv0CW4Y98GBY+po= -github.com/rs/cors v1.11.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= -github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= -github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA= +github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= +github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= github.com/rudderlabs/analytics-go v3.3.3+incompatible h1:OG0XlKoXfr539e2t1dXtTB+Gr89uFW+OUNQBVhHIIBY= github.com/rudderlabs/analytics-go v3.3.3+incompatible/go.mod h1:LF8/ty9kUX4PTY3l5c97K3nZZaX5Hwsvt+NBaRL/f30= github.com/russellhaering/goxmldsig v1.2.0/go.mod h1:gM4MDENBQf7M+V824SGfyIUVFWydB7n0KkEubVJl+Tw= @@ -518,8 +540,8 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd h1:CmH9+J6ZSsIjUK3dcGsnCnO41eRBOnY12zwkn5qVwgc= github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= -github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk= -github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0= +github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo= +github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7PQSMz9NShzorzCiG2fk9+xuCgLkPeCvMHYR2OWg= @@ -553,34 +575,33 @@ github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5k github.com/simplereach/timeutils v1.2.0/go.mod h1:VVbQDfN/FHRZa1LSqcwo4kNZ62OOyqLLGQKYB3pB0Q8= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= -github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= -github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= -github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= -github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= +github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4= +github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= +github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= -github.com/splitio/go-client/v6 v6.6.0 h1:/MNcVJ9cyYWECgHmdfO2ovI2T9cKaw+soT2upBMKJ38= -github.com/splitio/go-client/v6 v6.6.0/go.mod h1:L7kuKzzTIvIBrN8QILvmONJb3wBPfv/IGqyrsZtqlnI= -github.com/splitio/go-split-commons/v6 v6.0.0 h1:qenr5qbXafjvM832C64CVpjtlShuQiWCwtR5I2h4ogM= -github.com/splitio/go-split-commons/v6 v6.0.0/go.mod h1:TsvIh3XP7yjc7ly4vpj06AkoBND36SodPs5qfhb8rHc= +github.com/splitio/go-client/v6 v6.7.0 h1:1r+rYRTXvIo8xCwwiqbcVkw9ZSGXD02aFT+1P02teiQ= +github.com/splitio/go-client/v6 v6.7.0/go.mod h1:vXT4V2MystDJd6fEmqO64t07Ws2oLmdRg0gu2eOuyIo= +github.com/splitio/go-split-commons/v6 v6.1.0 h1:k3mwr12DF6gbEaV8XXU/tSAQlPkIEuzIgTEneYhGg2I= +github.com/splitio/go-split-commons/v6 v6.1.0/go.mod h1:D/XIY/9Hmfk9ivWsRsJVp439kEdmHbzUi3PKzQQDOXY= github.com/splitio/go-toolkit/v5 v5.4.0 h1:g5WFpRhQomnXCmvfsNOWV4s5AuUrWIZ+amM68G8NBKM= github.com/splitio/go-toolkit/v5 v5.4.0/go.mod h1:xYhUvV1gga9/1029Wbp5pjnR6Cy8nvBpjw99wAbsMko= github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo= github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -588,19 +609,16 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/throttled/throttled v2.2.5+incompatible h1:65UB52X0qNTYiT0Sohp8qLYVFwZQPDw85uSa65OljjQ= github.com/throttled/throttled v2.2.5+incompatible/go.mod h1:0BjlrEGQmvxps+HuXLsyRdqpSRvJpq0PNIsOtqP9Nos= -github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U= -github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= +github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= @@ -608,8 +626,8 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= -github.com/tinylib/msgp v1.2.0 h1:0uKB/662twsVBpYUPbokj4sTSKhWFKB7LopO2kWK8lY= -github.com/tinylib/msgp v1.2.0/go.mod h1:2vIGs3lcUo8izAATNobrCHevYZC/LMsJtw4JPiYPHro= +github.com/tinylib/msgp v1.2.5 h1:WeQg1whrXRFiZusidTQqzETkRpGjFjcIhW6uqWH09po= +github.com/tinylib/msgp v1.2.5/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= @@ -635,23 +653,31 @@ github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofm github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c h1:3lbZUMbMiGUW/LMkfsEABsc5zNT9+b1CvsJx47JzJ8g= github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c/go.mod h1:UrdRz5enIKZ63MEE3IF9l2/ebyx59GyGgPi+tICQdmM= +github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= +github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yuin/goldmark v1.7.4 h1:BDXOHExt+A7gwPCJgPIIq7ENvceR7we7rOS9TNoLZeg= -github.com/yuin/goldmark v1.7.4/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= -go.etcd.io/bbolt v1.3.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0= -go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ= +github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic= +github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= +go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0= +go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= -go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= -go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= -go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= -go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= -go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8= -go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= -go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= -go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= +go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= +go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= +go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= +go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4= +go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU= +go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU= +go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= +go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= +go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= @@ -661,25 +687,31 @@ golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= -golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= +golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= -golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ= -golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E= +golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA= +golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= +golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68= +golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= -golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= +golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -694,18 +726,20 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= -golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= +golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -717,11 +751,15 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -731,12 +769,18 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -745,28 +789,31 @@ golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= -golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= -golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= -golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= +golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= +golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= @@ -774,12 +821,12 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= -golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= +golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -789,8 +836,10 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= -golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= +golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -807,16 +856,23 @@ google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoA google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240722135656-d784300faade h1:oCRSWfwGXQsqlVdErcyTt4A93Y8fo0/9D4b1gnI++qo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240722135656-d784300faade/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250124145028-65684f501c47 h1:91mG8dNTpkC0uChJUQ9zCiRqx3GEEFOWaRZ0mI6Oj2I= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250124145028-65684f501c47/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= -google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= +google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM= +google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= @@ -839,6 +895,7 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= @@ -849,30 +906,28 @@ grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJd honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ= -modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ= -modernc.org/ccgo/v4 v4.19.2 h1:lwQZgvboKD0jBwdaeVCTouxhxAyN6iawF3STraAal8Y= -modernc.org/ccgo/v4 v4.19.2/go.mod h1:ysS3mxiMV38XGRTTcgo0DQTeTmAO4oCmJl1nX9VFI3s= +modernc.org/cc/v4 v4.24.4 h1:TFkx1s6dCkQpd6dKurBNmpo+G8Zl4Sq/ztJ+2+DEsh0= +modernc.org/cc/v4 v4.24.4/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= +modernc.org/ccgo/v4 v4.23.13 h1:PFiaemQwE/jdwi8XEHyEV+qYWoIuikLP3T4rvDeJb00= +modernc.org/ccgo/v4 v4.23.13/go.mod h1:vdN4h2WR5aEoNondUx26K7G8X+nuBscYnAEWSRmN2/0= modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= -modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw= -modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU= -modernc.org/gc/v3 v3.0.0-20240722195230-4a140ff9c08e h1:WPC4v0rNIFb2PY+nBBEEKyugPPRHPzUgyN3xZPpGK58= -modernc.org/gc/v3 v3.0.0-20240722195230-4a140ff9c08e/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4= -modernc.org/libc v1.55.3 h1:AzcW1mhlPNrRtjS5sS+eW2ISCgSOLLNyFzRh/V3Qj/U= -modernc.org/libc v1.55.3/go.mod h1:qFXepLhz+JjFThQ4kzwzOjA/y/artDeg+pcYnY+Q83w= -modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= -modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= -modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E= -modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU= -modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= -modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc= -modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss= -modernc.org/sqlite v1.31.1 h1:XVU0VyzxrYHlBhIs1DiEgSl0ZtdnPtbLVy8hSkzxGrs= -modernc.org/sqlite v1.31.1/go.mod h1:UqoylwmTb9F+IqXERT8bW9zzOWN8qwAIcLdzeBZs4hA= -modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= -modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= +modernc.org/gc/v2 v2.6.1 h1:+Qf6xdG8l7B27TQ8D8lw/iFMUj1RXRBOuMUWziJOsk8= +modernc.org/gc/v2 v2.6.1/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito= +modernc.org/libc v1.61.9 h1:PLSBXVkifXGELtJ5BOnBUyAHr7lsatNwFU/RRo4kfJM= +modernc.org/libc v1.61.9/go.mod h1:61xrnzk/aR8gr5bR7Uj/lLFLuXu2/zMpIjcry63Eumk= +modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= +modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= +modernc.org/memory v1.8.2 h1:cL9L4bcoAObu4NkxOlKWBWtNHIsnnACGF/TbqQ6sbcI= +modernc.org/memory v1.8.2/go.mod h1:ZbjSvMO5NQ1A2i3bWeDiVMxIorXwdClKE/0SZ+BMotU= +modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8= +modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= +modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= +modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= +modernc.org/sqlite v1.34.5 h1:Bb6SR13/fjp15jt70CL4f18JIN7p7dnMExd+UFnF15g= +modernc.org/sqlite v1.34.5/go.mod h1:YLuNmX9NKs8wRNK2ko1LW1NGYcc9FkBO69JOt1AR9JE= +modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= +modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= diff --git a/server/platform/shared/filestore/s3_overrides.go b/server/platform/shared/filestore/s3_overrides.go index 06cabb7fa527..42f865ba15bf 100644 --- a/server/platform/shared/filestore/s3_overrides.go +++ b/server/platform/shared/filestore/s3_overrides.go @@ -40,6 +40,12 @@ type customProvider struct { isSignV2 bool } +// RetrieveWithCredContext simply calls Retrieve, because we don't need to use +// the param. +func (cp customProvider) RetrieveWithCredContext(_ *credentials.CredContext) (credentials.Value, error) { + return cp.Retrieve() +} + // Retrieve just returns empty credentials. func (cp customProvider) Retrieve() (credentials.Value, error) { sign := credentials.SignatureV4 diff --git a/server/public/go.mod b/server/public/go.mod index 076c7e6dc7d1..474060c8b51f 100644 --- a/server/public/go.mod +++ b/server/public/go.mod @@ -1,6 +1,6 @@ module github.com/mattermost/mattermost/server/public -go 1.22 +go 1.22.0 toolchain go1.22.6 @@ -14,60 +14,59 @@ require ( github.com/gorilla/websocket v1.5.3 github.com/hashicorp/go-hclog v1.6.3 github.com/hashicorp/go-multierror v1.1.1 - github.com/hashicorp/go-plugin v1.6.1 + github.com/hashicorp/go-plugin v1.6.3 github.com/lib/pq v1.10.9 github.com/mattermost/go-i18n v1.11.1-0.20211013152124-5c415071e404 github.com/mattermost/ldap v0.0.0-20231116144001-0f480c025956 github.com/mattermost/logr/v2 v2.0.21 - github.com/nicksnyder/go-i18n/v2 v2.4.0 + github.com/nicksnyder/go-i18n/v2 v2.5.0 github.com/pborman/uuid v1.2.1 github.com/pkg/errors v0.9.1 github.com/rudderlabs/analytics-go v3.3.3+incompatible github.com/sirupsen/logrus v1.9.3 - github.com/stretchr/testify v1.9.0 - github.com/tinylib/msgp v1.2.0 + github.com/stretchr/testify v1.10.0 + github.com/tinylib/msgp v1.2.5 github.com/vmihailenco/msgpack/v5 v5.4.1 - golang.org/x/crypto v0.25.0 - golang.org/x/net v0.27.0 - golang.org/x/oauth2 v0.21.0 - golang.org/x/text v0.16.0 - golang.org/x/tools v0.23.0 + golang.org/x/crypto v0.32.0 + golang.org/x/net v0.34.0 + golang.org/x/oauth2 v0.25.0 + golang.org/x/text v0.21.0 + golang.org/x/tools v0.29.0 gopkg.in/yaml.v3 v3.0.1 ) require ( filippo.io/edwards25519 v1.1.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/fatih/color v1.17.0 // indirect + github.com/fatih/color v1.18.0 // indirect github.com/go-asn1-ber/asn1-ber v1.5.7 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect - github.com/hashicorp/yamux v0.1.1 // indirect + github.com/hashicorp/yamux v0.1.2 // indirect github.com/kr/pretty v0.3.1 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/oklog/run v1.1.0 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/philhofer/fwd v1.1.3-0.20240612014219-fbbf4953d986 // indirect + github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/segmentio/backo-go v1.1.0 // indirect github.com/stretchr/objx v0.5.2 // indirect - github.com/tidwall/gjson v1.17.1 // indirect + github.com/tidwall/gjson v1.18.0 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/wiggin77/merror v1.0.5 // indirect github.com/wiggin77/srslog v1.0.1 // indirect github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect - golang.org/x/mod v0.19.0 // indirect - golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.22.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240722135656-d784300faade // indirect - google.golang.org/grpc v1.65.0 // indirect - google.golang.org/protobuf v1.34.2 // indirect + golang.org/x/mod v0.22.0 // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/sys v0.29.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250124145028-65684f501c47 // indirect + google.golang.org/grpc v1.70.0 // indirect + google.golang.org/protobuf v1.36.4 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/server/public/go.sum b/server/public/go.sum index 22ab39c8bd78..ae28f3895d96 100644 --- a/server/public/go.sum +++ b/server/public/go.sum @@ -10,8 +10,8 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= -github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= +github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= @@ -31,8 +31,8 @@ github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25Kn github.com/dyatlov/go-opengraph/opengraph v0.0.0-20220524092352-606d7b1e5f8a h1:etIrTD8BQqzColk9nKRusM9um5+1q0iOEJLqfBMIK64= github.com/dyatlov/go-opengraph/opengraph v0.0.0-20220524092352-606d7b1e5f8a/go.mod h1:emQhSYTXqB0xxjLITTw4EaWZ+8IIQYw+kx9GqNUKdLg= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= -github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= @@ -42,6 +42,10 @@ github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aev github.com/go-asn1-ber/asn1-ber v1.5.7 h1:DTX+lbVTWaTw1hQ+PbZPlnDZPEIs0SS/GCZAl535dDk= github.com/go-asn1-ber/asn1-ber v1.5.7/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -82,10 +86,10 @@ github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB1 github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-plugin v1.6.1 h1:P7MR2UP6gNKGPp+y7EZw2kOiq4IR9WiqLvp0XOsVdwI= -github.com/hashicorp/go-plugin v1.6.1/go.mod h1:XPHFku2tFo3o3QKFgSYo+cghcUhw1NA1hZyMK0PWAw0= -github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= -github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= +github.com/hashicorp/go-plugin v1.6.3 h1:xgHB+ZUSYeuJi96WtxEjzi23uh7YQpznjGh0U0UUrwg= +github.com/hashicorp/go-plugin v1.6.3/go.mod h1:MRobyh+Wc/nYy1V4KAXUiYfzxoYhs7V1mlH1Z7iY2h0= +github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8= +github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns= github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo= @@ -113,23 +117,20 @@ github.com/mattermost/logr/v2 v2.0.21 h1:CMHsP+nrbRlEC4g7BwOk1GAnMtHkniFhlSQPXy5 github.com/mattermost/logr/v2 v2.0.21/go.mod h1:kZkB/zqKL9e+RY5gB3vGpsyenC+TpuiOenjMkvJJbzc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= -github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= -github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= -github.com/nicksnyder/go-i18n/v2 v2.4.0 h1:3IcvPOAvnCKwNm0TB0dLDTuawWEj+ax/RERNC+diLMM= -github.com/nicksnyder/go-i18n/v2 v2.4.0/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7gMc814+6wVyEI4= +github.com/nicksnyder/go-i18n/v2 v2.5.0 h1:3wH1gpaekcgGuwzWdSu7JwJhH9Tk87k1ezt0i1p2/Is= +github.com/nicksnyder/go-i18n/v2 v2.5.0/go.mod h1:DrhgsSDZxoAfvVrBVLXoxZn/pN5TXqaDbq7ju94viiQ= github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= @@ -138,8 +139,8 @@ github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtP github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/philhofer/fwd v1.1.3-0.20240612014219-fbbf4953d986 h1:jYi87L8j62qkXzaYHAQAhEapgukhenIMZRBKTNRLHJ4= -github.com/philhofer/fwd v1.1.3-0.20240612014219-fbbf4953d986/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= +github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c h1:dAMKvw0MlJT1GshSTtih8C2gDs04w8dReiOGXrGLNoY= +github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -193,18 +194,18 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= -github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U= -github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= +github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= -github.com/tinylib/msgp v1.2.0 h1:0uKB/662twsVBpYUPbokj4sTSKhWFKB7LopO2kWK8lY= -github.com/tinylib/msgp v1.2.0/go.mod h1:2vIGs3lcUo8izAATNobrCHevYZC/LMsJtw4JPiYPHro= +github.com/tinylib/msgp v1.2.5 h1:WeQg1whrXRFiZusidTQqzETkRpGjFjcIhW6uqWH09po= +github.com/tinylib/msgp v1.2.5/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0= github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= @@ -219,21 +220,31 @@ github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c h1:3lbZUMbMiGUW/LMkfsEAB github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c/go.mod h1:UrdRz5enIKZ63MEE3IF9l2/ebyx59GyGgPi+tICQdmM= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= +go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U= +go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg= +go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M= +go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8= +go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4= +go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU= +go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU= +go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= +go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM= +go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= -golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= +golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= -golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= +golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -246,14 +257,14 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= -golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= +golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= +golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= -golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70= +golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -261,8 +272,8 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -280,18 +291,17 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= -golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -301,8 +311,8 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= -golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= +golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= +golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -318,16 +328,16 @@ google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoA google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240722135656-d784300faade h1:oCRSWfwGXQsqlVdErcyTt4A93Y8fo0/9D4b1gnI++qo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240722135656-d784300faade/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250124145028-65684f501c47 h1:91mG8dNTpkC0uChJUQ9zCiRqx3GEEFOWaRZ0mI6Oj2I= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250124145028-65684f501c47/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= -google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= +google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= +google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM= +google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/server/public/model/websocket_message_test.go b/server/public/model/websocket_message_test.go index aee60449166c..28b12515b768 100644 --- a/server/public/model/websocket_message_test.go +++ b/server/public/model/websocket_message_test.go @@ -228,7 +228,7 @@ func TestWebSocketEventDeepCopy(t *testing.T) { evCopy := ev.DeepCopy() require.Equal(t, ev, evCopy) - require.NotSame(t, ev.data, evCopy.data) + require.NotSame(t, &ev.data, &evCopy.data) require.NotSame(t, ev.broadcast, evCopy.broadcast) require.NotSame(t, ev.precomputedJSON, evCopy.precomputedJSON) From 7d9521d78306980e866acf1fa94e0324d05d5413 Mon Sep 17 00:00:00 2001 From: Jesse Hallam Date: Thu, 6 Feb 2025 12:36:31 -0400 Subject: [PATCH 008/164] MM-62142: remove SELECT * from status store (round 2) (#30060) * Revert "Revert "[MM-62142] Avoid SELECT * in status_store.go (#29610)" (#29985)" This reverts commit d345e92136ef968fc22b7d2416b993a5654c9113. * add tests for StatusGet and StatusGetByIds * handle NULL columns in the Status Store * simplify status store * more builder simplifications and tests * expose GetQueryPlaceholder --------- Co-authored-by: Mattermost Build --- .../channels/store/sqlstore/sqlx_wrapper.go | 5 + .../channels/store/sqlstore/status_store.go | 130 +++++------ .../store/sqlstore/status_store_test.go | 2 +- .../channels/store/storetest/channel_store.go | 3 + .../channels/store/storetest/status_store.go | 216 ++++++++++++++++-- server/channels/store/storetest/utils.go | 15 ++ 6 files changed, 282 insertions(+), 89 deletions(-) diff --git a/server/channels/store/sqlstore/sqlx_wrapper.go b/server/channels/store/sqlstore/sqlx_wrapper.go index bd294ba628af..7a9a46108584 100644 --- a/server/channels/store/sqlstore/sqlx_wrapper.go +++ b/server/channels/store/sqlstore/sqlx_wrapper.go @@ -20,6 +20,7 @@ import ( "github.com/mattermost/mattermost/server/public/model" "github.com/mattermost/mattermost/server/public/shared/mlog" "github.com/mattermost/mattermost/server/v8/channels/store/storetest" + sq "github.com/mattermost/squirrel" ) type StoreTestWrapper struct { @@ -38,6 +39,10 @@ func (w *StoreTestWrapper) DriverName() string { return w.orig.DriverName() } +func (w *StoreTestWrapper) GetQueryPlaceholder() sq.PlaceholderFormat { + return w.orig.getQueryPlaceholder() +} + type Builder interface { ToSql() (string, []any, error) } diff --git a/server/channels/store/sqlstore/status_store.go b/server/channels/store/sqlstore/status_store.go index 40f55773ccd9..1922d3a62138 100644 --- a/server/channels/store/sqlstore/status_store.go +++ b/server/channels/store/sqlstore/status_store.go @@ -17,10 +17,28 @@ import ( type SqlStatusStore struct { *SqlStore + + statusSelectQuery sq.SelectBuilder } func newSqlStatusStore(sqlStore *SqlStore) store.StatusStore { - return &SqlStatusStore{sqlStore} + s := SqlStatusStore{ + SqlStore: sqlStore, + } + + manualColumnName := quoteColumnName(s.DriverName(), "Manual") + s.statusSelectQuery = s.getQueryBuilder(). + Select( + "COALESCE(UserId, '') AS UserId", + "COALESCE(Status, '') AS Status", + fmt.Sprintf("COALESCE(%s, FALSE) AS %s", manualColumnName, manualColumnName), + "COALESCE(LastActivityAt, 0) AS LastActivityAt", + "COALESCE(DNDEndTime, 0) AS DNDEndTime", + "COALESCE(PrevStatus, '') AS PrevStatus", + ). + From("Status") + + return &s } func (s SqlStatusStore) SaveOrUpdate(st *model.Status) error { @@ -37,12 +55,7 @@ func (s SqlStatusStore) SaveOrUpdate(st *model.Status) error { st.Status, st.Manual, st.LastActivityAt, st.DNDEndTime, st.PrevStatus)) } - queryString, args, err := query.ToSql() - if err != nil { - return errors.Wrap(err, "status_tosql") - } - - if _, err := s.GetMaster().Exec(queryString, args...); err != nil { + if _, err := s.GetMaster().ExecBuilder(query); err != nil { return errors.Wrap(err, "failed to upsert Status") } @@ -50,9 +63,10 @@ func (s SqlStatusStore) SaveOrUpdate(st *model.Status) error { } func (s SqlStatusStore) Get(userId string) (*model.Status, error) { - var status model.Status + query := s.statusSelectQuery.Where(sq.Eq{"UserId": userId}) - if err := s.GetReplica().Get(&status, "SELECT * FROM Status WHERE UserId = ?", userId); err != nil { + var status model.Status + if err := s.GetReplica().GetBuilder(&status, query); err != nil { if err == sql.ErrNoRows { return nil, store.NewErrNotFound("Status", fmt.Sprintf("userId=%s", userId)) } @@ -62,30 +76,13 @@ func (s SqlStatusStore) Get(userId string) (*model.Status, error) { } func (s SqlStatusStore) GetByIds(userIds []string) ([]*model.Status, error) { - query := s.getQueryBuilder(). - Select(fmt.Sprintf("UserId, Status, %s, LastActivityAt", quoteColumnName(s.DriverName(), "Manual"))). - From("Status"). - Where(sq.Eq{"UserId": userIds}) - queryString, args, err := query.ToSql() - if err != nil { - return nil, errors.Wrap(err, "status_tosql") - } - rows, err := s.GetReplica().DB.Query(queryString, args...) + query := s.statusSelectQuery.Where(sq.Eq{"UserId": userIds}) + + statuses := []*model.Status{} + err := s.GetReplica().SelectBuilder(&statuses, query) if err != nil { return nil, errors.Wrap(err, "failed to find Statuses") } - statuses := []*model.Status{} - defer rows.Close() - for rows.Next() { - var status model.Status - if err = rows.Scan(&status.UserId, &status.Status, &status.Manual, &status.LastActivityAt); err != nil { - return nil, errors.Wrap(err, "unable to scan from rows") - } - statuses = append(statuses, &status) - } - if err = rows.Err(); err != nil { - return nil, errors.Wrap(err, "failed while iterating over rows") - } return statuses, nil } @@ -94,24 +91,19 @@ func (s SqlStatusStore) GetByIds(userIds []string) ([]*model.Status, error) { func (s SqlStatusStore) updateExpiredStatuses(t *sqlxTxWrapper) ([]*model.Status, error) { statuses := []*model.Status{} currUnixTime := time.Now().UTC().Unix() - selectQuery, selectParams, err := s.getQueryBuilder(). - Select("*"). - From("Status"). - Where( - sq.And{ - sq.Eq{"Status": model.StatusDnd}, - sq.Gt{"DNDEndTime": 0}, - sq.LtOrEq{"DNDEndTime": currUnixTime}, - }, - ).ToSql() - if err != nil { - return nil, errors.Wrap(err, "status_tosql") - } - err = t.Select(&statuses, selectQuery, selectParams...) + selectQuery := s.statusSelectQuery.Where( + sq.And{ + sq.Eq{"Status": model.StatusDnd}, + sq.Gt{"DNDEndTime": 0}, + sq.LtOrEq{"DNDEndTime": currUnixTime}, + }, + ) + + err := t.SelectBuilder(&statuses, selectQuery) if err != nil { return nil, errors.Wrap(err, "updateExpiredStatusesT: failed to get expired dnd statuses") } - updateQuery, args, err := s.getQueryBuilder(). + updateQuery := s.getQueryBuilder(). Update("Status"). Where( sq.And{ @@ -123,14 +115,9 @@ func (s SqlStatusStore) updateExpiredStatuses(t *sqlxTxWrapper) ([]*model.Status Set("Status", sq.Expr("PrevStatus")). Set("PrevStatus", model.StatusDnd). Set("DNDEndTime", 0). - Set(quoteColumnName(s.DriverName(), "Manual"), false). - ToSql() + Set(quoteColumnName(s.DriverName(), "Manual"), false) - if err != nil { - return nil, errors.Wrap(err, "status_tosql") - } - - if _, err := t.Exec(updateQuery, args...); err != nil { + if _, err := t.ExecBuilder(updateQuery); err != nil { return nil, errors.Wrapf(err, "updateExpiredStatusesT: failed to update statuses") } @@ -162,7 +149,7 @@ func (s SqlStatusStore) UpdateExpiredDNDStatuses() (_ []*model.Status, err error return statuses, nil } - queryString, args, err := s.getQueryBuilder(). + queryString := s.getQueryBuilder(). Update("Status"). Where( sq.And{ @@ -175,30 +162,13 @@ func (s SqlStatusStore) UpdateExpiredDNDStatuses() (_ []*model.Status, err error Set("PrevStatus", model.StatusDnd). Set("DNDEndTime", 0). Set(quoteColumnName(s.DriverName(), "Manual"), false). - Suffix("RETURNING *"). - ToSql() - - if err != nil { - return nil, errors.Wrap(err, "status_tosql") - } + Suffix("RETURNING *") - rows, err := s.GetMaster().Query(queryString, args...) + statuses := []*model.Status{} + err = s.GetMaster().SelectBuilder(&statuses, queryString) if err != nil { return nil, errors.Wrap(err, "failed to find Statuses") } - defer rows.Close() - statuses := []*model.Status{} - for rows.Next() { - var status model.Status - if err = rows.Scan(&status.UserId, &status.Status, &status.Manual, &status.LastActivityAt, - &status.DNDEndTime, &status.PrevStatus); err != nil { - return nil, errors.Wrap(err, "unable to scan from rows") - } - statuses = append(statuses, &status) - } - if err = rows.Err(); err != nil { - return nil, errors.Wrap(err, "failed while iterating over rows") - } return statuses, nil } @@ -212,8 +182,13 @@ func (s SqlStatusStore) ResetAll() error { func (s SqlStatusStore) GetTotalActiveUsersCount() (int64, error) { time := model.GetMillis() - (1000 * 60 * 60 * 24) + query := s.getQueryBuilder(). + Select("COUNT(UserId)"). + From("Status"). + Where(sq.Gt{"LastActivityAt": time}) + var count int64 - err := s.GetReplica().Get(&count, "SELECT COUNT(UserId) FROM Status WHERE LastActivityAt > ?", time) + err := s.GetReplica().GetBuilder(&count, query) if err != nil { return count, errors.Wrap(err, "failed to count active users") } @@ -221,7 +196,12 @@ func (s SqlStatusStore) GetTotalActiveUsersCount() (int64, error) { } func (s SqlStatusStore) UpdateLastActivityAt(userId string, lastActivityAt int64) error { - if _, err := s.GetMaster().Exec("UPDATE Status SET LastActivityAt = ? WHERE UserId = ?", lastActivityAt, userId); err != nil { + builder := s.getQueryBuilder(). + Update("Status"). + Set("LastActivityAt", lastActivityAt). + Where(sq.Eq{"UserId": userId}) + + if _, err := s.GetMaster().ExecBuilder(builder); err != nil { return errors.Wrapf(err, "failed to update last activity for userId=%s", userId) } diff --git a/server/channels/store/sqlstore/status_store_test.go b/server/channels/store/sqlstore/status_store_test.go index a6f1d9a454f1..1f0b049f5010 100644 --- a/server/channels/store/sqlstore/status_store_test.go +++ b/server/channels/store/sqlstore/status_store_test.go @@ -10,5 +10,5 @@ import ( ) func TestStatusStore(t *testing.T) { - StoreTest(t, storetest.TestStatusStore) + StoreTestWithSqlStore(t, storetest.TestStatusStore) } diff --git a/server/channels/store/storetest/channel_store.go b/server/channels/store/storetest/channel_store.go index abd9651c3d3d..521f61c26c56 100644 --- a/server/channels/store/storetest/channel_store.go +++ b/server/channels/store/storetest/channel_store.go @@ -24,11 +24,13 @@ import ( "github.com/mattermost/mattermost/server/public/shared/timezones" "github.com/mattermost/mattermost/server/v8/channels/store" "github.com/mattermost/mattermost/server/v8/channels/utils" + sq "github.com/mattermost/squirrel" ) type SqlStore interface { GetMaster() SqlXExecutor DriverName() string + GetQueryPlaceholder() sq.PlaceholderFormat } type SqlXExecutor interface { @@ -5766,6 +5768,7 @@ func (s ByChannelDisplayName) Len() int { return len(s) } func (s ByChannelDisplayName) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + func (s ByChannelDisplayName) Less(i, j int) bool { if s[i].DisplayName != s[j].DisplayName { return s[i].DisplayName < s[j].DisplayName diff --git a/server/channels/store/storetest/status_store.go b/server/channels/store/storetest/status_store.go index afc456571dd7..9215dc7fb380 100644 --- a/server/channels/store/storetest/status_store.go +++ b/server/channels/store/storetest/status_store.go @@ -7,6 +7,8 @@ import ( "testing" "time" + sq "github.com/mattermost/squirrel" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/mattermost/mattermost/server/public/model" @@ -14,13 +16,15 @@ import ( "github.com/mattermost/mattermost/server/v8/channels/store" ) -func TestStatusStore(t *testing.T, rctx request.CTX, ss store.Store) { +func TestStatusStore(t *testing.T, rctx request.CTX, ss store.Store, s SqlStore) { t.Run("", func(t *testing.T) { testStatusStore(t, rctx, ss) }) t.Run("ActiveUserCount", func(t *testing.T) { testActiveUserCount(t, rctx, ss) }) t.Run("UpdateExpiredDNDStatuses", func(t *testing.T) { testUpdateExpiredDNDStatuses(t, rctx, ss) }) + t.Run("Get", func(t *testing.T) { testStatusGet(t, rctx, ss, s) }) + t.Run("GetByIds", func(t *testing.T) { testStatusGetByIds(t, rctx, ss, s) }) } -func testStatusStore(t *testing.T, rctx request.CTX, ss store.Store) { +func testStatusStore(t *testing.T, _ request.CTX, ss store.Store) { status := &model.Status{UserId: model.NewId(), Status: model.StatusOnline, Manual: false, LastActivityAt: 0, ActiveChannel: ""} require.NoError(t, ss.Status().SaveOrUpdate(status)) @@ -51,12 +55,19 @@ func testStatusStore(t *testing.T, rctx request.CTX, ss store.Store) { } func testActiveUserCount(t *testing.T, rctx request.CTX, ss store.Store) { - status := &model.Status{UserId: model.NewId(), Status: model.StatusOnline, Manual: false, LastActivityAt: model.GetMillis(), ActiveChannel: ""} - require.NoError(t, ss.Status().SaveOrUpdate(status)) + status1 := &model.Status{UserId: model.NewId(), Status: model.StatusOnline, Manual: false, LastActivityAt: model.GetMillis(), ActiveChannel: ""} + require.NoError(t, ss.Status().SaveOrUpdate(status1)) + + count1, err := ss.Status().GetTotalActiveUsersCount() + require.NoError(t, err) + assert.Greater(t, count1, int64(0)) + + status2 := &model.Status{UserId: model.NewId(), Status: model.StatusOnline, Manual: false, LastActivityAt: model.GetMillis(), ActiveChannel: ""} + require.NoError(t, ss.Status().SaveOrUpdate(status2)) - count, err := ss.Status().GetTotalActiveUsersCount() + count2, err := ss.Status().GetTotalActiveUsersCount() require.NoError(t, err) - require.True(t, count > 0, "expected count > 0, got %d", count) + assert.Equal(t, count1+1, count2) } type ByUserId []*model.Status @@ -68,8 +79,15 @@ func (s ByUserId) Less(i, j int) bool { return s[i].UserId < s[j].UserId } func testUpdateExpiredDNDStatuses(t *testing.T, rctx request.CTX, ss store.Store) { userID := NewTestID() - status := &model.Status{UserId: userID, Status: model.StatusDnd, Manual: true, - DNDEndTime: time.Now().Add(5 * time.Second).Unix(), PrevStatus: model.StatusOnline} + status := &model.Status{ + UserId: userID, + Status: model.StatusDnd, + Manual: true, + LastActivityAt: time.Now().Unix(), + ActiveChannel: "channel-id", + DNDEndTime: time.Now().Add(5 * time.Second).Unix(), + PrevStatus: model.StatusOnline, + } require.NoError(t, ss.Status().SaveOrUpdate(status)) time.Sleep(2 * time.Second) @@ -87,9 +105,181 @@ func testUpdateExpiredDNDStatuses(t *testing.T, rctx request.CTX, ss store.Store require.Len(t, statuses, 1) updatedStatus := *statuses[0] - require.Equal(t, updatedStatus.UserId, userID) - require.Equal(t, updatedStatus.Status, model.StatusOnline) - require.Equal(t, updatedStatus.DNDEndTime, int64(0)) - require.Equal(t, updatedStatus.PrevStatus, model.StatusDnd) - require.Equal(t, updatedStatus.Manual, false) + assert.Equal(t, updatedStatus.UserId, userID) + assert.Equal(t, updatedStatus.Status, model.StatusOnline) + assert.Equal(t, updatedStatus.Manual, false) + assert.Equal(t, updatedStatus.LastActivityAt, updatedStatus.LastActivityAt) + assert.Empty(t, updatedStatus.ActiveChannel) + assert.Equal(t, updatedStatus.DNDEndTime, int64(0)) + assert.Equal(t, updatedStatus.PrevStatus, model.StatusDnd) +} + +func insertNullStatus(t *testing.T, ss store.Store, s SqlStore) string { + userId := model.NewId() + db := ss.GetInternalMasterDB() + + // Insert status with explicit NULL values + builder := sq.StatementBuilder.PlaceholderFormat(s.GetQueryPlaceholder()). + Insert("Status"). + Columns("UserId", "Status", quoteColumnName(s.DriverName(), "Manual"), "LastActivityAt", "DNDEndTime", "PrevStatus"). + Values(userId, nil, nil, nil, nil, nil) + + query, args, err := builder.ToSql() + require.NoError(t, err) + + _, err = db.Exec(query, args...) + require.NoError(t, err) + + return userId +} + +func testStatusGet(t *testing.T, _ request.CTX, ss store.Store, s SqlStore) { + t.Run("null columns", func(t *testing.T) { + userId := insertNullStatus(t, ss, s) + + received, err := ss.Status().Get(userId) + require.NoError(t, err) + assert.Equal(t, userId, received.UserId) + assert.Empty(t, received.Status) + assert.False(t, received.Manual) + assert.Equal(t, int64(0), received.LastActivityAt) + assert.Empty(t, received.ActiveChannel) + assert.Equal(t, int64(0), received.DNDEndTime) + assert.Empty(t, received.PrevStatus) + }) + + t.Run("status1", func(t *testing.T) { + status1 := &model.Status{ + UserId: model.NewId(), + Status: model.StatusDnd, + Manual: true, + LastActivityAt: 1234, + ActiveChannel: "channel-id", + DNDEndTime: model.GetMillis(), + PrevStatus: model.StatusOnline, + } + require.NoError(t, ss.Status().SaveOrUpdate(status1)) + + received, err := ss.Status().Get(status1.UserId) + require.NoError(t, err) + assert.Equal(t, status1.UserId, received.UserId) + assert.Equal(t, status1.Status, received.Status) + assert.Equal(t, status1.Manual, received.Manual) + assert.Equal(t, status1.LastActivityAt, received.LastActivityAt) + assert.Empty(t, received.ActiveChannel) + assert.Equal(t, status1.DNDEndTime, received.DNDEndTime) + assert.Equal(t, status1.PrevStatus, received.PrevStatus) + }) + + t.Run("status2", func(t *testing.T) { + status2 := &model.Status{ + UserId: model.NewId(), + Status: model.StatusOffline, + Manual: false, + LastActivityAt: 12345, + ActiveChannel: "channel-id2", + DNDEndTime: model.GetMillis(), + PrevStatus: model.StatusAway, + } + require.NoError(t, ss.Status().SaveOrUpdate(status2)) + + received, err := ss.Status().Get(status2.UserId) + require.NoError(t, err) + assert.Equal(t, status2.UserId, received.UserId) + assert.Equal(t, status2.Status, received.Status) + assert.Equal(t, status2.Manual, received.Manual) + assert.Equal(t, status2.LastActivityAt, received.LastActivityAt) + assert.Empty(t, received.ActiveChannel) + assert.Equal(t, status2.DNDEndTime, received.DNDEndTime) + assert.Equal(t, status2.PrevStatus, received.PrevStatus) + }) +} + +func testStatusGetByIds(t *testing.T, _ request.CTX, ss store.Store, s SqlStore) { + t.Run("null columns, single user", func(t *testing.T) { + userId := insertNullStatus(t, ss, s) + + received, err := ss.Status().GetByIds([]string{userId}) + require.NoError(t, err) + require.Len(t, received, 1) + assert.Equal(t, userId, received[0].UserId) + assert.Empty(t, received[0].Status) + assert.False(t, received[0].Manual) + assert.Equal(t, int64(0), received[0].LastActivityAt) + assert.Empty(t, received[0].ActiveChannel) + assert.Equal(t, int64(0), received[0].DNDEndTime) + assert.Empty(t, received[0].PrevStatus) + }) + + t.Run("single user", func(t *testing.T) { + status1 := &model.Status{ + UserId: model.NewId(), + Status: model.StatusDnd, + Manual: true, + LastActivityAt: 1234, + ActiveChannel: "channel-id", + DNDEndTime: model.GetMillis(), + PrevStatus: model.StatusOnline, + } + require.NoError(t, ss.Status().SaveOrUpdate(status1)) + + received, err := ss.Status().GetByIds([]string{status1.UserId}) + require.NoError(t, err) + require.Len(t, received, 1) + assert.Equal(t, status1.UserId, received[0].UserId) + assert.Equal(t, status1.Status, received[0].Status) + assert.Equal(t, status1.Manual, received[0].Manual) + assert.Equal(t, status1.LastActivityAt, received[0].LastActivityAt) + assert.Empty(t, received[0].ActiveChannel) + assert.Equal(t, status1.DNDEndTime, received[0].DNDEndTime) + assert.Equal(t, status1.PrevStatus, received[0].PrevStatus) + }) + + t.Run("multiple users", func(t *testing.T) { + status1 := &model.Status{ + UserId: model.NewId(), + Status: model.StatusDnd, + Manual: true, + LastActivityAt: 1234, + ActiveChannel: "channel-id", + DNDEndTime: model.GetMillis(), + PrevStatus: model.StatusOnline, + } + require.NoError(t, ss.Status().SaveOrUpdate(status1)) + + status2 := &model.Status{ + UserId: model.NewId(), + Status: model.StatusOffline, + Manual: false, + LastActivityAt: 12345, + ActiveChannel: "channel-id2", + DNDEndTime: model.GetMillis(), + PrevStatus: model.StatusAway, + } + require.NoError(t, ss.Status().SaveOrUpdate(status2)) + + received, err := ss.Status().GetByIds([]string{status1.UserId, status2.UserId}) + require.NoError(t, err) + require.Len(t, received, 2) + + for _, status := range received { + if status.UserId == status1.UserId { + assert.Equal(t, status1.UserId, status.UserId) + assert.Equal(t, status1.Status, status.Status) + assert.Equal(t, status1.Manual, status.Manual) + assert.Equal(t, status1.LastActivityAt, status.LastActivityAt) + assert.Empty(t, status.ActiveChannel) + assert.Equal(t, status1.DNDEndTime, status.DNDEndTime) + assert.Equal(t, status1.PrevStatus, status.PrevStatus) + } else { + assert.Equal(t, status2.UserId, status.UserId) + assert.Equal(t, status2.Status, status.Status) + assert.Equal(t, status2.Manual, status.Manual) + assert.Equal(t, status2.LastActivityAt, status.LastActivityAt) + assert.Empty(t, status.ActiveChannel) + assert.Equal(t, status2.DNDEndTime, status.DNDEndTime) + assert.Equal(t, status2.PrevStatus, status.PrevStatus) + } + } + }) } diff --git a/server/channels/store/storetest/utils.go b/server/channels/store/storetest/utils.go index 5a9995aa3ce9..5f437415b783 100644 --- a/server/channels/store/storetest/utils.go +++ b/server/channels/store/storetest/utils.go @@ -4,6 +4,8 @@ package storetest import ( + "fmt" + "github.com/mattermost/mattermost/server/public/model" ) @@ -19,3 +21,16 @@ func NewTestID() string { return string(newID) } + +// Adds backtiks to the column name for MySQL, this is required if +// the column name is a reserved keyword. +// +// `ColumnName` - MySQL +// ColumnName - Postgres +func quoteColumnName(driver string, columnName string) string { + if driver == model.DatabaseDriverMysql { + return fmt.Sprintf("`%s`", columnName) + } + + return columnName +} From 144986c1ade5da9700710e46275466e0f1ba1431 Mon Sep 17 00:00:00 2001 From: "unified-ci-app[bot]" <121569378+unified-ci-app[bot]@users.noreply.github.com> Date: Fri, 7 Feb 2025 11:20:09 +0200 Subject: [PATCH 009/164] Update latest minor version to 10.6.0 (#30138) Automatic Merge --- server/public/model/version.go | 1 + 1 file changed, 1 insertion(+) diff --git a/server/public/model/version.go b/server/public/model/version.go index 3c076f087427..0cd1fe192ac1 100644 --- a/server/public/model/version.go +++ b/server/public/model/version.go @@ -13,6 +13,7 @@ import ( // It should be maintained in chronological order with most current // release at the front of the list. var versions = []string{ + "10.6.0", "10.5.0", "10.4.0", "10.3.0", From e475568d6d1bb38a11370ab9bb8368d5c17f5626 Mon Sep 17 00:00:00 2001 From: Nick Misasi Date: Fri, 7 Feb 2025 09:34:09 -0500 Subject: [PATCH 010/164] [MM-62669] Updates to cloud specific imagery in system console (#29963) * Updates to cloud specific imagery in system console * Revert something from images * Fix linter * Updates from PR feedback * Fix lint * Add svg * Lint fixes, update another SVG * change to pre-existing already-themed version of svg * change the folder of the file * add file * Adjust styling --- .../billing/billing_history.scss | 3 +- .../admin_console/billing/billing_history.tsx | 4 +- .../billing_summary/billing_summary.tsx | 4 +- .../billing/company_info_display.scss | 1 - .../billing/company_info_display.tsx | 6 +- .../chat_illustration.tsx | 0 .../svg_images_components/company_svg.tsx | 523 ++++++++++++++---- .../empty_billing_history_svg.tsx | 378 ++++--------- .../pricing_modal/blank_card_image.svg.tsx | 523 ------------------ .../src/components/pricing_modal/card.tsx | 4 +- .../global_threads/global_threads.tsx | 2 +- 11 files changed, 548 insertions(+), 900 deletions(-) rename webapp/channels/src/components/{threading/common => common/svg_images_components}/chat_illustration.tsx (100%) delete mode 100644 webapp/channels/src/components/pricing_modal/blank_card_image.svg.tsx diff --git a/webapp/channels/src/components/admin_console/billing/billing_history.scss b/webapp/channels/src/components/admin_console/billing/billing_history.scss index c6022249f80b..d6569ee436f8 100644 --- a/webapp/channels/src/components/admin_console/billing/billing_history.scss +++ b/webapp/channels/src/components/admin_console/billing/billing_history.scss @@ -35,7 +35,7 @@ } .BillingHistory__cardBody { - padding: 20px; + padding: 48px; } .BillingHistory__noHistory { @@ -53,7 +53,6 @@ .BillingHistory__noHistory-link { margin-top: 20px; - margin-bottom: 40px; color: var(--sys-button-bg); font-size: 14px; font-weight: 600; diff --git a/webapp/channels/src/components/admin_console/billing/billing_history.tsx b/webapp/channels/src/components/admin_console/billing/billing_history.tsx index c3e10d5f9a06..238841af5eaf 100644 --- a/webapp/channels/src/components/admin_console/billing/billing_history.tsx +++ b/webapp/channels/src/components/admin_console/billing/billing_history.tsx @@ -36,8 +36,8 @@ interface NoBillingHistorySectionProps { export const NoBillingHistorySection = (props: NoBillingHistorySectionProps) => (
{ let body = noCompanyInfoSection; const address = companyInfo?.company_address?.line1 ? companyInfo.company_address : companyInfo?.billing_address; - const isCompanyBillingFilled = address?.line1 !== undefined; + const isCompanyBillingFilled = address?.line1 !== undefined && address?.line1 !== ''; if (isCompanyBillingFilled) { body = (
diff --git a/webapp/channels/src/components/threading/common/chat_illustration.tsx b/webapp/channels/src/components/common/svg_images_components/chat_illustration.tsx similarity index 100% rename from webapp/channels/src/components/threading/common/chat_illustration.tsx rename to webapp/channels/src/components/common/svg_images_components/chat_illustration.tsx diff --git a/webapp/channels/src/components/common/svg_images_components/company_svg.tsx b/webapp/channels/src/components/common/svg_images_components/company_svg.tsx index 8465b73152d0..573f19b97f0e 100644 --- a/webapp/channels/src/components/common/svg_images_components/company_svg.tsx +++ b/webapp/channels/src/components/common/svg_images_components/company_svg.tsx @@ -12,107 +12,434 @@ const CompanySvg = (props: SvgProps) => ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); diff --git a/webapp/channels/src/components/common/svg_images_components/empty_billing_history_svg.tsx b/webapp/channels/src/components/common/svg_images_components/empty_billing_history_svg.tsx index 049ae51188ca..f988389a1d16 100644 --- a/webapp/channels/src/components/common/svg_images_components/empty_billing_history_svg.tsx +++ b/webapp/channels/src/components/common/svg_images_components/empty_billing_history_svg.tsx @@ -12,306 +12,152 @@ const EmptyBillingHistorySvg = (props: SvgProps) => ( - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ); export default EmptyBillingHistorySvg; diff --git a/webapp/channels/src/components/pricing_modal/blank_card_image.svg.tsx b/webapp/channels/src/components/pricing_modal/blank_card_image.svg.tsx deleted file mode 100644 index af07eb2e6ecd..000000000000 --- a/webapp/channels/src/components/pricing_modal/blank_card_image.svg.tsx +++ /dev/null @@ -1,523 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -import React from 'react'; - -const BlankCardImage = () => { - return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ); -}; - -export default BlankCardImage; diff --git a/webapp/channels/src/components/pricing_modal/card.tsx b/webapp/channels/src/components/pricing_modal/card.tsx index 39695995d1de..d2a69e8d1279 100644 --- a/webapp/channels/src/components/pricing_modal/card.tsx +++ b/webapp/channels/src/components/pricing_modal/card.tsx @@ -8,11 +8,11 @@ import {useIntl} from 'react-intl'; import styled from 'styled-components'; import useOpenSalesLink from 'components/common/hooks/useOpenSalesLink'; +import ChatIllustration from 'components/common/svg_images_components/chat_illustration'; import ExternalLink from 'components/external_link'; import {HostedCustomerLinks} from 'utils/constants'; -import BlankCardImage from './blank_card_image.svg'; import BuildingSvg from './building.svg'; import TadaSvg from './tada.svg'; @@ -74,7 +74,7 @@ export function BlankCard() { return (
- + {ChatIllustration}
diff --git a/webapp/channels/src/components/threading/global_threads/global_threads.tsx b/webapp/channels/src/components/threading/global_threads/global_threads.tsx index 0ae5b9d0a345..05467e0a8b7b 100644 --- a/webapp/channels/src/components/threading/global_threads/global_threads.tsx +++ b/webapp/channels/src/components/threading/global_threads/global_threads.tsx @@ -27,6 +27,7 @@ import {getSelectedThreadIdInCurrentTeam} from 'selectors/views/threads'; import {useGlobalState} from 'stores/hooks'; import LocalStorageStore from 'stores/local_storage_store'; +import ChatIllustration from 'components/common/svg_images_components/chat_illustration'; import LoadingScreen from 'components/loading_screen'; import NoResultsIndicator from 'components/no_results_indicator'; @@ -39,7 +40,6 @@ import {LhsItemType, LhsPage} from 'types/store/lhs'; import ThreadList, {ThreadFilter, FILTER_STORAGE_KEY} from './thread_list'; import ThreadPane from './thread_pane'; -import ChatIllustration from '../common/chat_illustration'; import {useThreadRouting} from '../hooks'; import ThreadViewer from '../thread_viewer'; From 3aaa379687bd76dd836880dd86705f71b86558f8 Mon Sep 17 00:00:00 2001 From: Jesse Hallam Date: Fri, 7 Feb 2025 11:16:00 -0400 Subject: [PATCH 011/164] Correctly detect branch to decide if notifying (#30142) I had previously made a change to check `github.ref`, not realizing that this particular workflow always runs from `master` from a workflow completed on a different branch. Use `github.event.workflow_run.head_branch` instead. --- .github/workflows/server-ci-report.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/server-ci-report.yml b/.github/workflows/server-ci-report.yml index 956726f5e1ee..90c2da2d04ed 100644 --- a/.github/workflows/server-ci-report.yml +++ b/.github/workflows/server-ci-report.yml @@ -70,7 +70,7 @@ jobs: check_annotations: true - name: Report retried tests via webhook (master) - if: ${{ steps.report.outputs.flaky_summary != '
TestRetries
' && github.event.workflow_run.name == 'Server CI Master' && github.ref_name == 'master' }} + if: ${{ steps.report.outputs.flaky_summary != '
TestRetries
' && github.event.workflow_run.name == 'Server CI Master' && github.event.workflow_run.head_branch == 'master' }} uses: mattermost/action-mattermost-notify@b7d118e440bf2749cd18a4a8c88e7092e696257a # v2.0.0 with: MATTERMOST_WEBHOOK_URL: ${{ secrets.MM_COMMUNITY_DEVELOPERS_INCOMING_WEBHOOK_FROM_GH_ACTIONS }} From a3f22bd34906d16354cf4303c2a755e95dd346d2 Mon Sep 17 00:00:00 2001 From: Ibrahim Serdar Acikgoz Date: Fri, 7 Feb 2025 16:20:13 +0100 Subject: [PATCH 012/164] [MM-62004] update threads after a channel gets moved (#29624) --- server/channels/app/channel.go | 5 + server/channels/app/channel_test.go | 53 +++++++++ .../channels/store/retrylayer/retrylayer.go | 21 ++++ .../channels/store/sqlstore/thread_store.go | 22 ++++ server/channels/store/store.go | 1 + .../store/storetest/mocks/ThreadStore.go | 18 +++ .../channels/store/storetest/thread_store.go | 111 ++++++++++++++++++ .../channels/store/timerlayer/timerlayer.go | 16 +++ 8 files changed, 247 insertions(+) diff --git a/server/channels/app/channel.go b/server/channels/app/channel.go index b3dcbed304bb..bc5ee9e2d70e 100644 --- a/server/channels/app/channel.go +++ b/server/channels/app/channel.go @@ -3276,6 +3276,11 @@ func (a *App) MoveChannel(c request.CTX, team *model.Team, channel *model.Channe } } + // Update the threads within this channel to the new team + if err := a.Srv().Store().Thread().UpdateTeamIdForChannelThreads(channel.Id, team.Id); err != nil { + c.Logger().Warn("error while updating threads after channel move", mlog.Err(err)) + } + if err := a.RemoveUsersFromChannelNotMemberOfTeam(c, user, channel, team); err != nil { c.Logger().Warn("error while removing non-team member users", mlog.Err(err)) } diff --git a/server/channels/app/channel_test.go b/server/channels/app/channel_test.go index 02bd703a513a..340722ec2afc 100644 --- a/server/channels/app/channel_test.go +++ b/server/channels/app/channel_test.go @@ -247,6 +247,59 @@ func TestMoveChannel(t *testing.T) { require.Equal(t, model.SidebarCategoryChannels, categories.Categories[1].Type) assert.Contains(t, categories.Categories[1].Channels, channel.Id) }) + + t.Run("should update threads when moving channels between teams", func(t *testing.T) { + th := Setup(t).InitBasic() + defer th.TearDown() + + sourceTeam := th.CreateTeam() + targetTeam := th.CreateTeam() + channel := th.CreateChannel(th.Context, sourceTeam) + + th.LinkUserToTeam(th.BasicUser, sourceTeam) + th.LinkUserToTeam(th.BasicUser, targetTeam) + th.AddUserToChannel(th.BasicUser, channel) + + // Create a thread in the channel + post := &model.Post{ + UserId: th.BasicUser.Id, + ChannelId: channel.Id, + Message: "test", + } + post, appErr := th.App.CreatePost(th.Context, post, channel, model.CreatePostFlags{}) + require.Nil(t, appErr) + + // Post a reply to the thread + reply := &model.Post{ + UserId: th.BasicUser.Id, + ChannelId: channel.Id, + RootId: post.Id, + Message: "reply", + } + _, appErr = th.App.CreatePost(th.Context, reply, channel, model.CreatePostFlags{}) + require.Nil(t, appErr) + + // Check that the thread count before move + threads, appErr := th.App.GetThreadsForUser(th.BasicUser.Id, targetTeam.Id, model.GetUserThreadsOpts{}) + require.Nil(t, appErr) + + require.Zero(t, threads.Total) + + // Move the channel to the target team + appErr = th.App.MoveChannel(th.Context, targetTeam, channel, th.BasicUser) + require.Nil(t, appErr) + + // Check that the thread was moved + threads, appErr = th.App.GetThreadsForUser(th.BasicUser.Id, targetTeam.Id, model.GetUserThreadsOpts{}) + require.Nil(t, appErr) + + require.Equal(t, int64(1), threads.Total) + // Check that the thread count after move + threads, appErr = th.App.GetThreadsForUser(th.BasicUser.Id, sourceTeam.Id, model.GetUserThreadsOpts{}) + require.Nil(t, appErr) + + require.Zero(t, threads.Total) + }) } func TestRemoveUsersFromChannelNotMemberOfTeam(t *testing.T) { diff --git a/server/channels/store/retrylayer/retrylayer.go b/server/channels/store/retrylayer/retrylayer.go index 0e29f23f1b2e..1a6f19d3b2cb 100644 --- a/server/channels/store/retrylayer/retrylayer.go +++ b/server/channels/store/retrylayer/retrylayer.go @@ -13545,6 +13545,27 @@ func (s *RetryLayerThreadStore) UpdateMembership(membership *model.ThreadMembers } +func (s *RetryLayerThreadStore) UpdateTeamIdForChannelThreads(channelId string, teamId string) error { + + tries := 0 + for { + err := s.ThreadStore.UpdateTeamIdForChannelThreads(channelId, teamId) + if err == nil { + return nil + } + if !isRepeatableError(err) { + return err + } + tries++ + if tries >= 3 { + err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures") + return err + } + timepkg.Sleep(100 * timepkg.Millisecond) + } + +} + func (s *RetryLayerTokenStore) Cleanup(expiryTime int64) { s.TokenStore.Cleanup(expiryTime) diff --git a/server/channels/store/sqlstore/thread_store.go b/server/channels/store/sqlstore/thread_store.go index 97d293c92ce7..d46a2e73fc44 100644 --- a/server/channels/store/sqlstore/thread_store.go +++ b/server/channels/store/sqlstore/thread_store.go @@ -1139,3 +1139,25 @@ func (s *SqlThreadStore) updateThreadParticipantsForUserTx(trx *sqlxTxWrapper, p return nil } + +// UpdateTeamIdForChannelThreads updates the team id for all threads in a channel. +// Specifically used when a channel is moved to a different team. +// If a user is not member of the new team, the threads will be deleted by the +// channel move process. +func (s *SqlThreadStore) UpdateTeamIdForChannelThreads(channelId, teamId string) error { + query := s.getQueryBuilder(). + Update("Threads"). + Set("ThreadTeamId", teamId). + Where( + sq.And{ + sq.Eq{"ChannelId": channelId}, + sq.Expr("EXISTS(SELECT 1 FROM Teams WHERE Id = ?)", teamId), + }) + + _, err := s.GetMaster().ExecBuilder(query) + if err != nil { + return errors.Wrapf(err, "failed to update threads team id for channel id=%s", channelId) + } + + return nil +} diff --git a/server/channels/store/store.go b/server/channels/store/store.go index eec2b9c4f1ba..7e77e80698fc 100644 --- a/server/channels/store/store.go +++ b/server/channels/store/store.go @@ -360,6 +360,7 @@ type ThreadStore interface { SaveMultipleMemberships(memberships []*model.ThreadMembership) ([]*model.ThreadMembership, error) MaintainMultipleFromImport(memberships []*model.ThreadMembership) ([]*model.ThreadMembership, error) + UpdateTeamIdForChannelThreads(channelId, teamId string) error } type PostStore interface { diff --git a/server/channels/store/storetest/mocks/ThreadStore.go b/server/channels/store/storetest/mocks/ThreadStore.go index 8dde1db6c6d8..85e23756da63 100644 --- a/server/channels/store/storetest/mocks/ThreadStore.go +++ b/server/channels/store/storetest/mocks/ThreadStore.go @@ -721,6 +721,24 @@ func (_m *ThreadStore) UpdateMembership(membership *model.ThreadMembership) (*mo return r0, r1 } +// UpdateTeamIdForChannelThreads provides a mock function with given fields: channelId, teamId +func (_m *ThreadStore) UpdateTeamIdForChannelThreads(channelId string, teamId string) error { + ret := _m.Called(channelId, teamId) + + if len(ret) == 0 { + panic("no return value specified for UpdateTeamIdForChannelThreads") + } + + var r0 error + if rf, ok := ret.Get(0).(func(string, string) error); ok { + r0 = rf(channelId, teamId) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // NewThreadStore creates a new instance of ThreadStore. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewThreadStore(t interface { diff --git a/server/channels/store/storetest/thread_store.go b/server/channels/store/storetest/thread_store.go index 2a7b55d4d770..acc920532b23 100644 --- a/server/channels/store/storetest/thread_store.go +++ b/server/channels/store/storetest/thread_store.go @@ -32,6 +32,7 @@ func TestThreadStore(t *testing.T, rctx request.CTX, ss store.Store, s SqlStore) t.Run("DeleteMembershipsForChannel", func(t *testing.T) { testDeleteMembershipsForChannel(t, rctx, ss) }) t.Run("SaveMultipleMemberships", func(t *testing.T) { testSaveMultipleMemberships(t, ss) }) t.Run("MaintainMultipleFromImport", func(t *testing.T) { testMaintainMultipleFromImport(t, rctx, ss) }) + t.Run("UpdateTeamIdForChannelThreads", func(t *testing.T) { testUpdateTeamIdForChannelThreads(t, rctx, ss) }) } func testThreadStorePopulation(t *testing.T, rctx request.CTX, ss store.Store) { @@ -2016,3 +2017,113 @@ func testMaintainMultipleFromImport(t *testing.T, rctx request.CTX, ss store.Sto require.NoError(t, err) }) } + +func testUpdateTeamIdForChannelThreads(t *testing.T, rctx request.CTX, ss store.Store) { + createThreadMembership := func(userID, postID string, following bool) (*model.ThreadMembership, func()) { + t.Helper() + opts := store.ThreadMembershipOpts{ + Following: following, + IncrementMentions: false, + UpdateFollowing: true, + UpdateViewedTimestamp: false, + UpdateParticipants: true, + } + mem, err := ss.Thread().MaintainMembership(userID, postID, opts) + require.NoError(t, err) + + return mem, func() { + err := ss.Thread().DeleteMembershipForUser(userID, postID) + require.NoError(t, err) + } + } + + postingUserID := model.NewId() + + team1, err := ss.Team().Save(&model.Team{ + DisplayName: "DisplayName", + Name: "team" + model.NewId(), + Email: MakeEmail(), + Type: model.TeamOpen, + }) + require.NoError(t, err) + + team2, err := ss.Team().Save(&model.Team{ + DisplayName: "DisplayNameTwo", + Name: "team" + model.NewId(), + Email: MakeEmail(), + Type: model.TeamOpen, + }) + require.NoError(t, err) + + channel1, err := ss.Channel().Save(rctx, &model.Channel{ + TeamId: team1.Id, + DisplayName: "DisplayName", + Name: "channel1" + model.NewId(), + Type: model.ChannelTypeOpen, + }, -1) + require.NoError(t, err) + + rootPost1, err := ss.Post().Save(rctx, &model.Post{ + ChannelId: channel1.Id, + UserId: postingUserID, + Message: model.NewRandomString(10), + }) + require.NoError(t, err) + + _, err = ss.Post().Save(rctx, &model.Post{ + ChannelId: channel1.Id, + UserId: postingUserID, + Message: model.NewRandomString(10), + RootId: rootPost1.Id, + }) + require.NoError(t, err) + + t.Run("Should move threads to the new team", func(t *testing.T) { + userA, err := ss.User().Save(request.TestContext(t), &model.User{ + Username: model.NewId(), + Email: MakeEmail(), + Password: model.NewId(), + }) + require.NoError(t, err) + + _, clean := createThreadMembership(userA.Id, rootPost1.Id, true) + defer clean() + + err = ss.Thread().UpdateTeamIdForChannelThreads(channel1.Id, team2.Id) + require.NoError(t, err) + + defer func() { + err = ss.Thread().UpdateTeamIdForChannelThreads(channel1.Id, team1.Id) + require.NoError(t, err) + }() + + threads, err := ss.Thread().GetThreadsForUser(userA.Id, team2.Id, model.GetUserThreadsOpts{}) + require.NoError(t, err) + require.Len(t, threads, 1) + }) + + t.Run("Should not move threads to a non existent team", func(t *testing.T) { + userA, err := ss.User().Save(request.TestContext(t), &model.User{ + Username: model.NewId(), + Email: MakeEmail(), + Password: model.NewId(), + }) + require.NoError(t, err) + + newTeamID := model.NewId() + + _, clean := createThreadMembership(userA.Id, rootPost1.Id, true) + t.Cleanup(clean) + + err = ss.Thread().UpdateTeamIdForChannelThreads(channel1.Id, newTeamID) + require.NoError(t, err) + + threads, err := ss.Thread().GetThreadsForUser(userA.Id, newTeamID, model.GetUserThreadsOpts{}) + require.NoError(t, err) + require.Len(t, threads, 0) + + threads, err = ss.Thread().GetThreadsForUser(userA.Id, team1.Id, model.GetUserThreadsOpts{}) + require.NoError(t, err) + require.Len(t, threads, 1) + }) +} diff --git a/server/channels/store/timerlayer/timerlayer.go b/server/channels/store/timerlayer/timerlayer.go index 4c6b2c262fac..51d8d959757b 100644 --- a/server/channels/store/timerlayer/timerlayer.go +++ b/server/channels/store/timerlayer/timerlayer.go @@ -10639,6 +10639,22 @@ func (s *TimerLayerThreadStore) UpdateMembership(membership *model.ThreadMembers return result, err } +func (s *TimerLayerThreadStore) UpdateTeamIdForChannelThreads(channelId string, teamId string) error { + start := time.Now() + + err := s.ThreadStore.UpdateTeamIdForChannelThreads(channelId, teamId) + + elapsed := float64(time.Since(start)) / float64(time.Second) + if s.Root.Metrics != nil { + success := "false" + if err == nil { + success = "true" + } + s.Root.Metrics.ObserveStoreMethodDuration("ThreadStore.UpdateTeamIdForChannelThreads", success, elapsed) + } + return err +} + func (s *TimerLayerTokenStore) Cleanup(expiryTime int64) { start := time.Now() From 50258a6510437a15982256e8d0de886b165d9a8a Mon Sep 17 00:00:00 2001 From: Arya Khochare <91268931+Aryakoste@users.noreply.github.com> Date: Fri, 7 Feb 2025 21:35:29 +0530 Subject: [PATCH 013/164] [MM-62153] Avoid SELECT * in preference_store.go (#30081) * refractored sql queries * GetBuilder to Select Builder as preferences is slice (fix failing test) * test: Add test for GetCategoryAndName preference method * fix s/GetBuilder/SelectBuilder * linting * make mocks * make store-layers --------- Co-authored-by: Jesse Hallam --- .../channels/store/retrylayer/retrylayer.go | 4 +- .../store/sqlstore/preference_store.go | 69 ++++++--------- server/channels/store/store.go | 2 +- .../store/storetest/mocks/PreferenceStore.go | 12 +-- .../store/storetest/preference_store.go | 83 +++++++++++++++++-- .../channels/store/timerlayer/timerlayer.go | 4 +- 6 files changed, 110 insertions(+), 64 deletions(-) diff --git a/server/channels/store/retrylayer/retrylayer.go b/server/channels/store/retrylayer/retrylayer.go index 1a6f19d3b2cb..cd629956cc12 100644 --- a/server/channels/store/retrylayer/retrylayer.go +++ b/server/channels/store/retrylayer/retrylayer.go @@ -8802,11 +8802,11 @@ func (s *RetryLayerPreferenceStore) GetCategory(userID string, category string) } -func (s *RetryLayerPreferenceStore) GetCategoryAndName(category string, nane string) (model.Preferences, error) { +func (s *RetryLayerPreferenceStore) GetCategoryAndName(category string, name string) (model.Preferences, error) { tries := 0 for { - result, err := s.PreferenceStore.GetCategoryAndName(category, nane) + result, err := s.PreferenceStore.GetCategoryAndName(category, name) if err == nil { return result, nil } diff --git a/server/channels/store/sqlstore/preference_store.go b/server/channels/store/sqlstore/preference_store.go index d1dd0e41f205..49d524014d55 100644 --- a/server/channels/store/sqlstore/preference_store.go +++ b/server/channels/store/sqlstore/preference_store.go @@ -14,10 +14,16 @@ import ( type SqlPreferenceStore struct { *SqlStore + preferenceSelectQuery sq.SelectBuilder } func newSqlPreferenceStore(sqlStore *SqlStore) store.PreferenceStore { - s := &SqlPreferenceStore{sqlStore} + s := &SqlPreferenceStore{SqlStore: sqlStore} + + s.preferenceSelectQuery = s.getQueryBuilder(). + Select("UserId", "Category", "Name", "Value"). + From("Preferences") + return s } @@ -121,18 +127,12 @@ func (s SqlPreferenceStore) saveTx(transaction *sqlxTxWrapper, preference *model func (s SqlPreferenceStore) Get(userId string, category string, name string) (*model.Preference, error) { var preference model.Preference - query, args, err := s.getQueryBuilder(). - Select("*"). - From("Preferences"). + query := s.preferenceSelectQuery. Where(sq.Eq{"UserId": userId}). Where(sq.Eq{"Category": category}). - Where(sq.Eq{"Name": name}). - ToSql() + Where(sq.Eq{"Name": name}) - if err != nil { - return nil, errors.Wrap(err, "could not build sql query to get preference") - } - if err = s.GetReplica().Get(&preference, query, args...); err != nil { + if err := s.GetReplica().GetBuilder(&preference, query); err != nil { return nil, errors.Wrapf(err, "failed to find Preference with userId=%s, category=%s, name=%s", userId, category, name) } @@ -141,50 +141,35 @@ func (s SqlPreferenceStore) Get(userId string, category string, name string) (*m func (s SqlPreferenceStore) GetCategoryAndName(category string, name string) (model.Preferences, error) { var preferences model.Preferences - query, args, err := s.getQueryBuilder(). - Select("*"). - From("Preferences"). + query := s.preferenceSelectQuery. Where(sq.Eq{"Category": category}). - Where(sq.Eq{"Name": name}). - ToSql() - if err != nil { - return nil, errors.Wrap(err, "could not build sql query to get preference") - } - if err = s.GetReplica().Select(&preferences, query, args...); err != nil { - return nil, errors.Wrapf(err, "failed to find Preference with category=%s, name=%s", category, name) + Where(sq.Eq{"Name": name}) + + if err := s.GetReplica().SelectBuilder(&preferences, query); err != nil { + return nil, errors.Wrapf(err, "failed to find Preferences with category=%s, name=%s", category, name) } return preferences, nil } func (s SqlPreferenceStore) GetCategory(userId string, category string) (model.Preferences, error) { var preferences model.Preferences - query, args, err := s.getQueryBuilder(). - Select("*"). - From("Preferences"). + query := s.preferenceSelectQuery. Where(sq.Eq{"UserId": userId}). - Where(sq.Eq{"Category": category}). - ToSql() - if err != nil { - return nil, errors.Wrap(err, "could not build sql query to get preference") - } - if err = s.GetReplica().Select(&preferences, query, args...); err != nil { - return nil, errors.Wrapf(err, "failed to find Preference with userId=%s, category=%s", userId, category) + Where(sq.Eq{"Category": category}) + + if err := s.GetReplica().SelectBuilder(&preferences, query); err != nil { + return nil, errors.Wrapf(err, "failed to find Preferences with userId=%s, category=%s", userId, category) } return preferences, nil } func (s SqlPreferenceStore) GetAll(userId string) (model.Preferences, error) { var preferences model.Preferences - query, args, err := s.getQueryBuilder(). - Select("*"). - From("Preferences"). - Where(sq.Eq{"UserId": userId}). - ToSql() - if err != nil { - return nil, errors.Wrap(err, "could not build sql query to get preference") - } - if err = s.GetReplica().Select(&preferences, query, args...); err != nil { - return nil, errors.Wrapf(err, "failed to find Preference with userId=%s", userId) + query := s.preferenceSelectQuery. + Where(sq.Eq{"UserId": userId}) + + if err := s.GetReplica().SelectBuilder(&preferences, query); err != nil { + return nil, errors.Wrapf(err, "failed to find Preferences with userId=%s", userId) } return preferences, nil } @@ -208,7 +193,6 @@ func (s SqlPreferenceStore) Delete(userId, category, name string) error { Where(sq.Eq{"UserId": userId}). Where(sq.Eq{"Category": category}). Where(sq.Eq{"Name": name}).ToSql() - if err != nil { return errors.Wrap(err, "could not build sql query to get delete preference") } @@ -225,7 +209,6 @@ func (s SqlPreferenceStore) DeleteCategory(userId string, category string) error Delete("Preferences"). Where(sq.Eq{"UserId": userId}). Where(sq.Eq{"Category": category}).ToSql() - if err != nil { return errors.Wrap(err, "could not build sql query to get delete preference by category") } @@ -242,7 +225,6 @@ func (s SqlPreferenceStore) DeleteCategoryAndName(category string, name string) Delete("Preferences"). Where(sq.Eq{"Name": name}). Where(sq.Eq{"Category": category}).ToSql() - if err != nil { return errors.Wrap(err, "could not build sql query to get delete preference by category and name") } @@ -299,7 +281,6 @@ func (s SqlPreferenceStore) CleanupFlagsBatch(limit int64) (int64, error) { Where(sq.Eq{"Category": model.PreferenceCategoryFlaggedPost}). Where(sq.Expr("name IN ("+nameInQ+")", nameInArgs...)). ToSql() - if err != nil { return int64(0), errors.Wrap(err, "could not build sql query to delete preference") } diff --git a/server/channels/store/store.go b/server/channels/store/store.go index 7e77e80698fc..4683e52aea12 100644 --- a/server/channels/store/store.go +++ b/server/channels/store/store.go @@ -666,7 +666,7 @@ type CommandWebhookStore interface { type PreferenceStore interface { Save(preferences model.Preferences) error GetCategory(userID string, category string) (model.Preferences, error) - GetCategoryAndName(category string, nane string) (model.Preferences, error) + GetCategoryAndName(category string, name string) (model.Preferences, error) Get(userID string, category string, name string) (*model.Preference, error) GetAll(userID string) (model.Preferences, error) Delete(userID, category, name string) error diff --git a/server/channels/store/storetest/mocks/PreferenceStore.go b/server/channels/store/storetest/mocks/PreferenceStore.go index 719860e85211..81b7173aee8d 100644 --- a/server/channels/store/storetest/mocks/PreferenceStore.go +++ b/server/channels/store/storetest/mocks/PreferenceStore.go @@ -242,9 +242,9 @@ func (_m *PreferenceStore) GetCategory(userID string, category string) (model.Pr return r0, r1 } -// GetCategoryAndName provides a mock function with given fields: category, nane -func (_m *PreferenceStore) GetCategoryAndName(category string, nane string) (model.Preferences, error) { - ret := _m.Called(category, nane) +// GetCategoryAndName provides a mock function with given fields: category, name +func (_m *PreferenceStore) GetCategoryAndName(category string, name string) (model.Preferences, error) { + ret := _m.Called(category, name) if len(ret) == 0 { panic("no return value specified for GetCategoryAndName") @@ -253,10 +253,10 @@ func (_m *PreferenceStore) GetCategoryAndName(category string, nane string) (mod var r0 model.Preferences var r1 error if rf, ok := ret.Get(0).(func(string, string) (model.Preferences, error)); ok { - return rf(category, nane) + return rf(category, name) } if rf, ok := ret.Get(0).(func(string, string) model.Preferences); ok { - r0 = rf(category, nane) + r0 = rf(category, name) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(model.Preferences) @@ -264,7 +264,7 @@ func (_m *PreferenceStore) GetCategoryAndName(category string, nane string) (mod } if rf, ok := ret.Get(1).(func(string, string) error); ok { - r1 = rf(category, nane) + r1 = rf(category, name) } else { r1 = ret.Error(1) } diff --git a/server/channels/store/storetest/preference_store.go b/server/channels/store/storetest/preference_store.go index c9d0280b89a3..97099c6bfebe 100644 --- a/server/channels/store/storetest/preference_store.go +++ b/server/channels/store/storetest/preference_store.go @@ -18,6 +18,7 @@ func TestPreferenceStore(t *testing.T, rctx request.CTX, ss store.Store, s SqlSt t.Run("PreferenceSave", func(t *testing.T) { testPreferenceSave(t, rctx, ss) }) t.Run("PreferenceGet", func(t *testing.T) { testPreferenceGet(t, rctx, ss) }) t.Run("PreferenceGetCategory", func(t *testing.T) { testPreferenceGetCategory(t, rctx, ss) }) + t.Run("PreferenceGetCategoryAndName", func(t *testing.T) { testPreferenceGetCategoryAndName(t, rctx, ss) }) t.Run("PreferenceGetAll", func(t *testing.T) { testPreferenceGetAll(t, rctx, ss) }) t.Run("PreferenceDeleteByUser", func(t *testing.T) { testPreferenceDeleteByUser(t, rctx, ss) }) t.Run("PreferenceDelete", func(t *testing.T) { testPreferenceDelete(t, rctx, ss) }) @@ -27,7 +28,7 @@ func TestPreferenceStore(t *testing.T, rctx request.CTX, ss store.Store, s SqlSt t.Run("PreferenceDeleteInvalidVisibleDmsGms", func(t *testing.T) { testDeleteInvalidVisibleDmsGms(t, rctx, ss, s) }) } -func testPreferenceSave(t *testing.T, rctx request.CTX, ss store.Store) { +func testPreferenceSave(t *testing.T, _ request.CTX, ss store.Store) { id := model.NewId() preferences := model.Preferences{ @@ -63,7 +64,7 @@ func testPreferenceSave(t *testing.T, rctx request.CTX, ss store.Store) { } } -func testPreferenceGet(t *testing.T, rctx request.CTX, ss store.Store) { +func testPreferenceGet(t *testing.T, _ request.CTX, ss store.Store) { userId := model.NewId() category := model.PreferenceCategoryDirectChannelShow name := model.NewId() @@ -103,7 +104,71 @@ func testPreferenceGet(t *testing.T, rctx request.CTX, ss store.Store) { require.Error(t, err, "no error on getting a missing preference") } -func testPreferenceGetCategory(t *testing.T, rctx request.CTX, ss store.Store) { +func testPreferenceGetCategoryAndName(t *testing.T, _ request.CTX, ss store.Store) { + userId := model.NewId() + category := model.PreferenceCategoryGroupChannelShow + name := model.NewId() + + preferences := model.Preferences{ + { + UserId: userId, + Category: category, + Name: name, + Value: "user1", + }, + // same category/name, different user + { + UserId: model.NewId(), + Category: category, + Name: name, + Value: "otherUser", + }, + // same user/name, different category + { + UserId: userId, + Category: model.NewId(), + Name: name, + Value: "", + }, + // same user/category, different name + { + UserId: userId, + Category: category, + Name: model.NewId(), + Value: "", + }, + } + + err := ss.Preference().Save(preferences) + require.NoError(t, err) + + actualPreferences, err := ss.Preference().GetCategoryAndName(category, name) + require.NoError(t, err) + assert.Equal(t, 2, len(actualPreferences), "got the wrong number of preferences") + + for _, preference := range preferences { + // Preferences with empty values aren't expected. + if preference.Value == "" { + continue + } + + found := false + for _, actualPreference := range actualPreferences { + if actualPreference == preference { + found = true + } + } + + assert.True(t, found, "didnt find preference with value %s", preference.Value) + } + + // make sure getting a missing preference category and name doesn't fail + actualPreferences, err = ss.Preference().GetCategoryAndName(model.NewId(), model.NewId()) + require.NoError(t, err) + require.Equal(t, 0, len(actualPreferences), "shouldn't have got any preferences") +} + +func testPreferenceGetCategory(t *testing.T, _ request.CTX, ss store.Store) { userId := model.NewId() category := model.PreferenceCategoryDirectChannelShow name := model.NewId() @@ -152,7 +217,7 @@ func testPreferenceGetCategory(t *testing.T, rctx request.CTX, ss store.Store) { require.Equal(t, 0, len(preferencesByCategory), "shouldn't have got any preferences") } -func testPreferenceGetAll(t *testing.T, rctx request.CTX, ss store.Store) { +func testPreferenceGetAll(t *testing.T, _ request.CTX, ss store.Store) { userId := model.NewId() category := model.PreferenceCategoryDirectChannelShow name := model.NewId() @@ -195,7 +260,7 @@ func testPreferenceGetAll(t *testing.T, rctx request.CTX, ss store.Store) { } } -func testPreferenceDeleteByUser(t *testing.T, rctx request.CTX, ss store.Store) { +func testPreferenceDeleteByUser(t *testing.T, _ request.CTX, ss store.Store) { userId := model.NewId() category := model.PreferenceCategoryDirectChannelShow name := model.NewId() @@ -233,7 +298,7 @@ func testPreferenceDeleteByUser(t *testing.T, rctx request.CTX, ss store.Store) require.NoError(t, err) } -func testPreferenceDelete(t *testing.T, rctx request.CTX, ss store.Store) { +func testPreferenceDelete(t *testing.T, _ request.CTX, ss store.Store) { preference := model.Preference{ UserId: model.NewId(), Category: model.PreferenceCategoryDirectChannelShow, @@ -255,7 +320,7 @@ func testPreferenceDelete(t *testing.T, rctx request.CTX, ss store.Store) { assert.Empty(t, preferences, "should've returned no preferences") } -func testPreferenceDeleteCategory(t *testing.T, rctx request.CTX, ss store.Store) { +func testPreferenceDeleteCategory(t *testing.T, _ request.CTX, ss store.Store) { category := model.NewId() userId := model.NewId() @@ -288,7 +353,7 @@ func testPreferenceDeleteCategory(t *testing.T, rctx request.CTX, ss store.Store assert.Empty(t, preferences, "should've returned no preferences") } -func testPreferenceDeleteCategoryAndName(t *testing.T, rctx request.CTX, ss store.Store) { +func testPreferenceDeleteCategoryAndName(t *testing.T, _ request.CTX, ss store.Store) { category := model.NewId() name := model.NewId() userId := model.NewId() @@ -404,7 +469,7 @@ func testPreferenceDeleteOrphanedRows(t *testing.T, rctx request.CTX, ss store.S assert.NoError(t, nErr, "newer preference should not have been deleted") } -func testDeleteInvalidVisibleDmsGms(t *testing.T, rctx request.CTX, ss store.Store, s SqlStore) { +func testDeleteInvalidVisibleDmsGms(t *testing.T, _ request.CTX, ss store.Store, s SqlStore) { userId1 := model.NewId() userId2 := model.NewId() userId3 := model.NewId() diff --git a/server/channels/store/timerlayer/timerlayer.go b/server/channels/store/timerlayer/timerlayer.go index 51d8d959757b..4d4a8b6580b7 100644 --- a/server/channels/store/timerlayer/timerlayer.go +++ b/server/channels/store/timerlayer/timerlayer.go @@ -6993,10 +6993,10 @@ func (s *TimerLayerPreferenceStore) GetCategory(userID string, category string) return result, err } -func (s *TimerLayerPreferenceStore) GetCategoryAndName(category string, nane string) (model.Preferences, error) { +func (s *TimerLayerPreferenceStore) GetCategoryAndName(category string, name string) (model.Preferences, error) { start := time.Now() - result, err := s.PreferenceStore.GetCategoryAndName(category, nane) + result, err := s.PreferenceStore.GetCategoryAndName(category, name) elapsed := float64(time.Since(start)) / float64(time.Second) if s.Root.Metrics != nil { From 0bdae416032d697233cb08f08ca63e2e056a9f69 Mon Sep 17 00:00:00 2001 From: Agniva De Sarker Date: Fri, 7 Feb 2025 22:12:33 +0530 Subject: [PATCH 014/164] MM-61215: Clarify to use --bypass-upload flag (#29761) https://mattermost.atlassian.net/browse/MM-61215 ```release-note NONE ``` Co-authored-by: Mattermost Build --- server/cmd/mmctl/commands/import.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/cmd/mmctl/commands/import.go b/server/cmd/mmctl/commands/import.go index 94455619c5b9..afdd6f01811e 100644 --- a/server/cmd/mmctl/commands/import.go +++ b/server/cmd/mmctl/commands/import.go @@ -186,7 +186,7 @@ func importUploadCmdF(c client.Client, command *cobra.Command, args []string) er isLocal, _ := command.Flags().GetBool("local") if isLocal { - printer.PrintWarning("In --local mode, you don't need to upload the file to server any more. Directly use the import process command and pass the export file.") + printer.PrintWarning("In --local mode, you don't need to upload the file to server any more. Directly use the import process command with the --bypass-upload flag and pass the export file.") } file, err := os.Open(filepath) From b11d53677473a8bbad6c193660a38a43364826a9 Mon Sep 17 00:00:00 2001 From: Arya Khochare <91268931+Aryakoste@users.noreply.github.com> Date: Sat, 8 Feb 2025 02:08:49 +0530 Subject: [PATCH 015/164] [MM-62152] Avoid SELECT * in oauth_store.go (#30080) * refractored select queries * test fail fix * linting issues * use builder pattern * simplify GetAuthorizedApps * revert to base behaviour --------- Co-authored-by: Jesse Hallam --- server/channels/store/sqlstore/oauth_store.go | 78 ++++++++++++++----- 1 file changed, 60 insertions(+), 18 deletions(-) diff --git a/server/channels/store/sqlstore/oauth_store.go b/server/channels/store/sqlstore/oauth_store.go index eac9fc4e5efc..871845adc3df 100644 --- a/server/channels/store/sqlstore/oauth_store.go +++ b/server/channels/store/sqlstore/oauth_store.go @@ -11,14 +11,34 @@ import ( "github.com/mattermost/mattermost/server/public/model" "github.com/mattermost/mattermost/server/v8/channels/store" + sq "github.com/mattermost/squirrel" ) type SqlOAuthStore struct { *SqlStore + oAuthAppsSelectQuery sq.SelectBuilder + oAuthAccessDataQuery sq.SelectBuilder + oAuthAuthDataQuery sq.SelectBuilder } func newSqlOAuthStore(sqlStore *SqlStore) store.OAuthStore { - return &SqlOAuthStore{sqlStore} + s := SqlOAuthStore{ + SqlStore: sqlStore, + } + + s.oAuthAppsSelectQuery = s.getQueryBuilder(). + Select("o.Id", "o.CreatorId", "o.CreateAt", "o.UpdateAt", "o.ClientSecret", "o.Name", "o.Description", "o.IconURL", "o.CallbackUrls", "o.Homepage", "o.IsTrusted", "o.MattermostAppID"). + From("OAuthApps o") + + s.oAuthAccessDataQuery = s.getQueryBuilder(). + Select("ClientId", "UserId", "Token", "RefreshToken", "RedirectUri", "ExpiresAt", "Scope"). + From("OAuthAccessData") + + s.oAuthAuthDataQuery = s.getQueryBuilder(). + Select("ClientId", "UserId", "Code", "ExpiresIn", "CreateAt", "RedirectUri", "State", "Scope"). + From("OAuthAuthData") + + return &s } func (as SqlOAuthStore) SaveApp(app *model.OAuthApp) (*model.OAuthApp, error) { @@ -48,8 +68,9 @@ func (as SqlOAuthStore) UpdateApp(app *model.OAuthApp) (*model.OAuthApp, error) } var oldApp model.OAuthApp - err := as.GetMaster().Get(&oldApp, `SELECT * FROM OAuthApps - WHERE id=?`, app.Id) + query := as.oAuthAppsSelectQuery.Where(sq.Eq{"o.Id": app.Id}) + + err := as.GetReplica().GetBuilder(&oldApp, query) if err != nil { return nil, errors.Wrapf(err, "failed to get OAuthApp with id=%s", app.Id) } @@ -80,7 +101,9 @@ func (as SqlOAuthStore) UpdateApp(app *model.OAuthApp) (*model.OAuthApp, error) func (as SqlOAuthStore) GetApp(id string) (*model.OAuthApp, error) { var app model.OAuthApp - if err := as.GetReplica().Get(&app, `SELECT * FROM OAuthApps WHERE Id=?`, id); err != nil { + query := as.oAuthAppsSelectQuery.Where(sq.Eq{"o.Id": id}) + + if err := as.GetReplica().GetBuilder(&app, query); err != nil { if err == sql.ErrNoRows { return nil, store.NewErrNotFound("OAuthApp", id) } @@ -95,7 +118,9 @@ func (as SqlOAuthStore) GetApp(id string) (*model.OAuthApp, error) { func (as SqlOAuthStore) GetAppByUser(userId string, offset, limit int) ([]*model.OAuthApp, error) { apps := []*model.OAuthApp{} - if err := as.GetReplica().Select(&apps, "SELECT * FROM OAuthApps WHERE CreatorId = ? LIMIT ? OFFSET ?", userId, limit, offset); err != nil { + query := as.oAuthAppsSelectQuery.Where(sq.Eq{"o.CreatorId": userId}).Limit(uint64(limit)).Offset(uint64(offset)) + + if err := as.GetReplica().SelectBuilder(&apps, query); err != nil { return nil, errors.Wrapf(err, "failed to find OAuthApps with userId=%s", userId) } @@ -105,7 +130,9 @@ func (as SqlOAuthStore) GetAppByUser(userId string, offset, limit int) ([]*model func (as SqlOAuthStore) GetApps(offset, limit int) ([]*model.OAuthApp, error) { apps := []*model.OAuthApp{} - if err := as.GetReplica().Select(&apps, "SELECT * FROM OAuthApps LIMIT ? OFFSET ?", limit, offset); err != nil { + query := as.oAuthAppsSelectQuery.Limit(uint64(limit)).Offset(uint64(offset)) + + if err := as.GetReplica().SelectBuilder(&apps, query); err != nil { return nil, errors.Wrap(err, "failed to find OAuthApps") } @@ -115,9 +142,12 @@ func (as SqlOAuthStore) GetApps(offset, limit int) ([]*model.OAuthApp, error) { func (as SqlOAuthStore) GetAuthorizedApps(userId string, offset, limit int) ([]*model.OAuthApp, error) { apps := []*model.OAuthApp{} - if err := as.GetReplica().Select(&apps, - `SELECT o.* FROM OAuthApps AS o INNER JOIN - Preferences AS p ON p.Name=o.Id AND p.UserId=? LIMIT ? OFFSET ?`, userId, limit, offset); err != nil { + query := as.oAuthAppsSelectQuery. + InnerJoin("Preferences AS p ON p.Name = o.Id AND p.UserId = ?", userId). + Limit(uint64(limit)). + Offset(uint64(offset)) + + if err := as.GetReplica().SelectBuilder(&apps, query); err != nil { return nil, errors.Wrapf(err, "failed to find OAuthApps with userId=%s", userId) } @@ -160,7 +190,9 @@ func (as SqlOAuthStore) SaveAccessData(accessData *model.AccessData) (*model.Acc func (as SqlOAuthStore) GetAccessData(token string) (*model.AccessData, error) { accessData := model.AccessData{} - if err := as.GetReplica().Get(&accessData, "SELECT * FROM OAuthAccessData WHERE Token = ?", token); err != nil { + query := as.oAuthAccessDataQuery.Where(sq.Eq{"Token": token}) + + if err := as.GetReplica().GetBuilder(&accessData, query); err != nil { return nil, errors.Wrapf(err, "failed to get OAuthAccessData with token=%s", token) } return &accessData, nil @@ -169,8 +201,9 @@ func (as SqlOAuthStore) GetAccessData(token string) (*model.AccessData, error) { func (as SqlOAuthStore) GetAccessDataByUserForApp(userID, clientID string) ([]*model.AccessData, error) { accessData := []*model.AccessData{} - if err := as.GetReplica().Select(&accessData, - "SELECT * FROM OAuthAccessData WHERE UserId = ? AND ClientId = ?", userID, clientID); err != nil { + query := as.oAuthAccessDataQuery.Where(sq.Eq{"UserId": userID, "ClientId": clientID}) + + if err := as.GetReplica().SelectBuilder(&accessData, query); err != nil { return nil, errors.Wrapf(err, "failed to delete OAuthAccessData with userId=%s and clientId=%s", userID, clientID) } return accessData, nil @@ -179,7 +212,9 @@ func (as SqlOAuthStore) GetAccessDataByUserForApp(userID, clientID string) ([]*m func (as SqlOAuthStore) GetAccessDataByRefreshToken(token string) (*model.AccessData, error) { accessData := model.AccessData{} - if err := as.GetReplica().Get(&accessData, "SELECT * FROM OAuthAccessData WHERE RefreshToken = ?", token); err != nil { + query := as.oAuthAccessDataQuery.Where(sq.Eq{"RefreshToken": token}) + + if err := as.GetReplica().GetBuilder(&accessData, query); err != nil { return nil, errors.Wrapf(err, "failed to find OAuthAccessData with refreshToken=%s", token) } return &accessData, nil @@ -188,13 +223,16 @@ func (as SqlOAuthStore) GetAccessDataByRefreshToken(token string) (*model.Access func (as SqlOAuthStore) GetPreviousAccessData(userID, clientID string) (*model.AccessData, error) { accessData := model.AccessData{} - if err := as.GetReplica().Get(&accessData, "SELECT * FROM OAuthAccessData WHERE ClientId = ? AND UserId = ?", clientID, userID); err != nil { + query := as.oAuthAccessDataQuery.Where(sq.Eq{"UserId": userID, "ClientId": clientID}). + OrderBy("ExpiresAt DESC").Limit(1) + + if err := as.GetReplica().GetBuilder(&accessData, query); err != nil { if err == sql.ErrNoRows { return nil, nil } - - return nil, errors.Wrapf(err, "failed to get AccessData with clientId=%s and userId=%s", clientID, userID) + return nil, errors.Wrapf(err, "failed to find OAuthAccessData with userId=%s and clientId=%s", userID, clientID) } + return &accessData, nil } @@ -240,16 +278,20 @@ func (as SqlOAuthStore) SaveAuthData(authData *model.AuthData) (*model.AuthData, func (as SqlOAuthStore) GetAuthData(code string) (*model.AuthData, error) { var authData model.AuthData - err := as.GetReplica().Get(&authData, `SELECT * FROM OAuthAuthData WHERE Code=?`, code) - if err != nil { + + query := as.oAuthAuthDataQuery.Where(sq.Eq{"Code": code}) + + if err := as.GetReplica().GetBuilder(&authData, query); err != nil { if err == sql.ErrNoRows { return nil, store.NewErrNotFound("AuthData", fmt.Sprintf("code=%s", code)) } return nil, errors.Wrapf(err, "failed to get AuthData with code=%s", code) } + if authData.Code == "" { return nil, store.NewErrNotFound("AuthData", fmt.Sprintf("code=%s", code)) } + return &authData, nil } From 9f5b13d55d973bf91851702bdf9d2a8132875197 Mon Sep 17 00:00:00 2001 From: Rajat Dabade Date: Sat, 8 Feb 2025 13:07:26 +0530 Subject: [PATCH 016/164] Upgraded boards version to v9.1.1 for prepackaged (#30072) * Upgraded boards version to v9.1.0 * Update server/Makefile Co-authored-by: Maria A Nunez --------- Co-authored-by: Maria A Nunez --- server/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/Makefile b/server/Makefile index 63f6d0d66822..7268c4bc4457 100644 --- a/server/Makefile +++ b/server/Makefile @@ -155,7 +155,7 @@ PLUGIN_PACKAGES += mattermost-plugin-nps-v1.3.3 PLUGIN_PACKAGES += mattermost-plugin-servicenow-v2.3.4 PLUGIN_PACKAGES += mattermost-plugin-zoom-v1.8.0 PLUGIN_PACKAGES += mattermost-plugin-ai-v1.0.0 -PLUGIN_PACKAGES += mattermost-plugin-boards-v9.0.5 +PLUGIN_PACKAGES += mattermost-plugin-boards-v9.1.1 PLUGIN_PACKAGES += mattermost-plugin-msteams-v2.1.0 PLUGIN_PACKAGES += mattermost-plugin-user-survey-v1.1.1 PLUGIN_PACKAGES += mattermost-plugin-mscalendar-v1.3.4 From ffb3a34289f84feae7a37e80733c85726ee10222 Mon Sep 17 00:00:00 2001 From: Antonis Stamatiou Date: Sat, 8 Feb 2025 13:50:28 +0200 Subject: [PATCH 017/164] fix: MM-62714 (#30141) --- .github/workflows/server-ci-report.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/server-ci-report.yml b/.github/workflows/server-ci-report.yml index 90c2da2d04ed..6753ac9871e0 100644 --- a/.github/workflows/server-ci-report.yml +++ b/.github/workflows/server-ci-report.yml @@ -86,12 +86,13 @@ jobs: env: TEST_NAME: "${{ matrix.test.name }}" FLAKY_SUMMARY: "${{ steps.report.outputs.flaky_summary }}" + PR_NUMBER: "${{ steps.incoming-pr.outputs.NUMBER }}" with: script: | const body = `#### ⚠️ One or more flaky tests detected ⚠️\n* Failing job: [github.com/mattermost/mattermost:${process.env.TEST_NAME}](${{ github.event.workflow_run.html_url }})\n* Double check your code to ensure you haven't introduced a flaky test.\n* If this seems to be unrelated to your changes, submit a separate pull request to skip the flaky tests (e.g. [23360](https://github.com/mattermost/mattermost/pull/23360)) and file JIRA ticket (e.g. [MM-52743](https://mattermost.atlassian.net/browse/MM-52743)) for later investigation.\n\n${process.env.FLAKY_SUMMARY}` await github.rest.issues.createComment({ - issue_number: ${{ steps.incoming-pr.outputs.NUMBER }}, + issue_number: process.env.PR_NUMBER, owner: context.repo.owner, repo: context.repo.repo, body: body From 3698808625d0bd9632853f9191c916e73e84e44d Mon Sep 17 00:00:00 2001 From: Agniva De Sarker Date: Sat, 8 Feb 2025 22:28:04 +0530 Subject: [PATCH 018/164] MM-62926: Fix standard logger redirection issue (#30137) We were redirecting any logging happening via the Go standard logger to our own mlog instance. The issue is that all those logs were happening at LvlStdLog level which is higher than LvlDebug. LvlStdLog is at 10, and LvlDebug is at 5. So unless a customer sets the log level specifically to "stdlog" or sets up advanced logging to specifically log for that level, no standard logs will ever get logged. We fix this by logging it at warn level. The reason for warn is that mostly external libraries use this facility to log out-of-band errors that could not be returned using standard error returns. I saw another plugin error which was logged similarly. This was never surfaced before due to this bug. https://mattermost.atlassian.net/browse/MM-62926 ```release-note NONE ``` --- server/channels/app/platform/log.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/channels/app/platform/log.go b/server/channels/app/platform/log.go index 98c93b961b93..1220f743c7e8 100644 --- a/server/channels/app/platform/log.go +++ b/server/channels/app/platform/log.go @@ -54,7 +54,7 @@ func (ps *PlatformService) initLogging() error { } // redirect default Go logger to app logger. - ps.logger.RedirectStdLog(mlog.LvlStdLog) + ps.logger.RedirectStdLog(mlog.LvlWarn) // use the app logger as the global logger (eventually remove all instances of global logging). mlog.InitGlobalLogger(ps.logger) From 55c2d57f70f32626624d881b60366df9652d76c0 Mon Sep 17 00:00:00 2001 From: Rahim Rahman Date: Sun, 9 Feb 2025 21:38:29 -0700 Subject: [PATCH 019/164] fix(MM-60645): missing emoji on mobile (#30001) * fix(MM-60645): missing emoji on mobile * removing the `:` completely from being stored or being sent to the server. * proper formatting? --- server/public/model/channel_bookmark.go | 5 +- server/public/model/channel_bookmark_test.go | 50 ++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/server/public/model/channel_bookmark.go b/server/public/model/channel_bookmark.go index d144476f31bb..f8bb7391bff1 100644 --- a/server/public/model/channel_bookmark.go +++ b/server/public/model/channel_bookmark.go @@ -5,6 +5,7 @@ package model import ( "net/http" + "strings" "unicode/utf8" ) @@ -142,6 +143,7 @@ func (o *ChannelBookmark) PreSave() { } o.DisplayName = SanitizeUnicode(o.DisplayName) + o.Emoji = strings.Trim(o.Emoji, ":") if o.CreateAt == 0 { o.CreateAt = GetMillis() } @@ -151,6 +153,7 @@ func (o *ChannelBookmark) PreSave() { func (o *ChannelBookmark) PreUpdate() { o.UpdateAt = GetMillis() o.DisplayName = SanitizeUnicode(o.DisplayName) + o.Emoji = strings.Trim(o.Emoji, ":") } func (o *ChannelBookmark) ToBookmarkWithFileInfo(f *FileInfo) *ChannelBookmarkWithFileInfo { @@ -167,7 +170,7 @@ func (o *ChannelBookmark) ToBookmarkWithFileInfo(f *FileInfo) *ChannelBookmarkWi SortOrder: o.SortOrder, LinkUrl: o.LinkUrl, ImageUrl: o.ImageUrl, - Emoji: o.Emoji, + Emoji: strings.Trim(o.Emoji, ":"), Type: o.Type, OriginalId: o.OriginalId, ParentId: o.ParentId, diff --git a/server/public/model/channel_bookmark_test.go b/server/public/model/channel_bookmark_test.go index 4a5867778f12..269d7d93f345 100644 --- a/server/public/model/channel_bookmark_test.go +++ b/server/public/model/channel_bookmark_test.go @@ -599,6 +599,56 @@ func TestChannelBookmarkPreUpdate(t *testing.T) { assert.Greater(t, bookmark.UpdateAt, originalBookmark.UpdateAt) } +func TestToBookmarkWithFileInfo(t *testing.T) { + testCases := []struct { + name string + bookmark *ChannelBookmark + fileInfo *FileInfo + expectedEmoji string + }{ + { + name: "emoji with colons", + bookmark: &ChannelBookmark{ + Id: NewId(), + DisplayName: "test bookmark", + Emoji: ":smile:", + Type: ChannelBookmarkLink, + }, + fileInfo: nil, + expectedEmoji: "smile", + }, + { + name: "emoji without colons", + bookmark: &ChannelBookmark{ + Id: NewId(), + DisplayName: "test bookmark", + Emoji: "smile", + Type: ChannelBookmarkLink, + }, + fileInfo: nil, + expectedEmoji: "smile", + }, + { + name: "empty emoji", + bookmark: &ChannelBookmark{ + Id: NewId(), + DisplayName: "test bookmark", + Emoji: "", + Type: ChannelBookmarkLink, + }, + fileInfo: nil, + expectedEmoji: "", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + result := tc.bookmark.ToBookmarkWithFileInfo(tc.fileInfo) + assert.Equal(t, tc.expectedEmoji, result.Emoji) + }) + } +} + func TestChannelBookmarkPatch(t *testing.T) { p := &ChannelBookmarkPatch{ DisplayName: NewPointer(NewId()), From 4de0a2c1e67907abbb8a2a54f5598f76e343634f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Feb 2025 12:21:51 +0200 Subject: [PATCH 020/164] Bump the github-actions-updates group with 5 updates (#30151) Bumps the github-actions-updates group with 5 updates: | Package | From | To | | --- | --- | --- | | [github/codeql-action](https://github.com/github/codeql-action) | `3.28.8` | `3.28.9` | | [tj-actions/changed-files](https://github.com/tj-actions/changed-files) | `45.0.6` | `45.0.7` | | [getsentry/action-release](https://github.com/getsentry/action-release) | `1.9.0` | `1.10.3` | | [sigstore/cosign-installer](https://github.com/sigstore/cosign-installer) | `3.7.0` | `3.8.0` | | [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) | `3.8.0` | `3.9.0` | Updates `github/codeql-action` from 3.28.8 to 3.28.9 - [Release notes](https://github.com/github/codeql-action/releases) - [Commits](https://github.com/github/codeql-action/compare/v3.28.8...v3.28.9) Updates `tj-actions/changed-files` from 45.0.6 to 45.0.7 - [Release notes](https://github.com/tj-actions/changed-files/releases) - [Changelog](https://github.com/tj-actions/changed-files/blob/main/HISTORY.md) - [Commits](https://github.com/tj-actions/changed-files/compare/d6e91a2266cdb9d62096cebf1e8546899c6aa18f...dcc7a0cba800f454d79fff4b993e8c3555bcc0a8) Updates `getsentry/action-release` from 1.9.0 to 1.10.3 - [Release notes](https://github.com/getsentry/action-release/releases) - [Changelog](https://github.com/getsentry/action-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/getsentry/action-release/compare/f6dfa3d84a1c740b94aa45255c5e032b744a095d...12bba0bd9c0f65f9f80d4965db646a1aed373d36) Updates `sigstore/cosign-installer` from 3.7.0 to 3.8.0 - [Release notes](https://github.com/sigstore/cosign-installer/releases) - [Commits](https://github.com/sigstore/cosign-installer/compare/dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da...c56c2d3e59e4281cc41dea2217323ba5694b171e) Updates `docker/setup-buildx-action` from 3.8.0 to 3.9.0 - [Release notes](https://github.com/docker/setup-buildx-action/releases) - [Commits](https://github.com/docker/setup-buildx-action/compare/6524bf65af31da8d45b59e8c27de4bd072b392f5...f7ce87c1d6bead3e36075b2ce75da1f6cc28aaca) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions-updates - dependency-name: tj-actions/changed-files dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions-updates - dependency-name: getsentry/action-release dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions-updates - dependency-name: sigstore/cosign-installer dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions-updates - dependency-name: docker/setup-buildx-action dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions-updates ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 6 +++--- .github/workflows/i18n-ci-template.yml | 2 +- .github/workflows/scorecards-analysis.yml | 2 +- .github/workflows/sentry.yaml | 2 +- .github/workflows/server-ci-artifacts.yml | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index de898fb87fe0..9e1ef56b85a8 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -29,14 +29,14 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v3.28.8 + uses: github/codeql-action/init@v3.28.9 with: languages: ${{ matrix.language }} debug: false config-file: ./.github/codeql/codeql-config.yml - name: Build JavaScript - uses: github/codeql-action/autobuild@v3.28.8 + uses: github/codeql-action/autobuild@v3.28.9 if: ${{ matrix.language == 'javascript' }} - name: Setup go @@ -55,4 +55,4 @@ jobs: # Perform Analysis - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3.28.8 + uses: github/codeql-action/analyze@v3.28.9 diff --git a/.github/workflows/i18n-ci-template.yml b/.github/workflows/i18n-ci-template.yml index f456d0a46b38..51f95c834125 100644 --- a/.github/workflows/i18n-ci-template.yml +++ b/.github/workflows/i18n-ci-template.yml @@ -15,7 +15,7 @@ jobs: - name: Get changed files id: changed-files - uses: tj-actions/changed-files@d6e91a2266cdb9d62096cebf1e8546899c6aa18f # v45.0.6 + uses: tj-actions/changed-files@dcc7a0cba800f454d79fff4b993e8c3555bcc0a8 # v45.0.7 with: files: | server/i18n/*.json diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml index 00c3bf236d1c..0e6e97e56ece 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -56,6 +56,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@0701025a8b1600e416be4f3bb5a830b1aa6af01e # v2.27.0 + uses: github/codeql-action/upload-sarif@0a35e8f6866a39b001e5f7ad1d0daf9836786896 # v2.27.0 with: sarif_file: results.sarif diff --git a/.github/workflows/sentry.yaml b/.github/workflows/sentry.yaml index b9c6beb8fbd8..8077372aeaf2 100644 --- a/.github/workflows/sentry.yaml +++ b/.github/workflows/sentry.yaml @@ -20,5 +20,5 @@ jobs: - name: cd/Checkout mattermost project uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: cd/Create Sentry release - uses: getsentry/action-release@f6dfa3d84a1c740b94aa45255c5e032b744a095d # v1.9.0 + uses: getsentry/action-release@12bba0bd9c0f65f9f80d4965db646a1aed373d36 # v1.10.3 diff --git a/.github/workflows/server-ci-artifacts.yml b/.github/workflows/server-ci-artifacts.yml index 5506be6d0a10..7ee2b230a3a0 100644 --- a/.github/workflows/server-ci-artifacts.yml +++ b/.github/workflows/server-ci-artifacts.yml @@ -83,7 +83,7 @@ jobs: password: ${{ secrets.DOCKERHUB_DEV_TOKEN }} - name: cd/setup-cosign - uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0 + uses: sigstore/cosign-installer@c56c2d3e59e4281cc41dea2217323ba5694b171e # v3.8.0 with: cosign-release: v${{ env.COSIGN_VERSION }} @@ -96,7 +96,7 @@ jobs: path: server/build/ - name: cd/setup-docker-buildx - uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5 # v3.8.0 + uses: docker/setup-buildx-action@f7ce87c1d6bead3e36075b2ce75da1f6cc28aaca # v3.9.0 - name: cd/set-docker-tag id: set_tag From 0927ecb0f19428f9c2d023d60498c436c985545d Mon Sep 17 00:00:00 2001 From: Ben Schumacher Date: Mon, 10 Feb 2025 11:32:04 +0100 Subject: [PATCH 021/164] [MM-62517] Add audit logs to Support Packet (#29844) --- server/channels/app/platform/log.go | 67 +++++++++++-------- server/channels/app/platform/log_test.go | 85 ++++++++++++++++++------ 2 files changed, 106 insertions(+), 46 deletions(-) diff --git a/server/channels/app/platform/log.go b/server/channels/app/platform/log.go index 1220f743c7e8..d07ba5df4eb8 100644 --- a/server/channels/app/platform/log.go +++ b/server/channels/app/platform/log.go @@ -12,6 +12,7 @@ import ( "path" "time" + "github.com/hashicorp/go-multierror" "github.com/pkg/errors" "github.com/mattermost/mattermost/server/public/model" @@ -249,38 +250,50 @@ func (ps *PlatformService) GetNotificationLogFile(_ request.CTX) (*model.FileDat } func (ps *PlatformService) GetAdvancedLogs(_ request.CTX) ([]*model.FileData, error) { - advancedLoggingJSON := ps.Config().LogSettings.AdvancedLoggingJSON - if utils.IsEmptyJSON(advancedLoggingJSON) { - return nil, nil - } - - cfg := make(mlog.LoggerConfiguration) - err := json.Unmarshal(advancedLoggingJSON, &cfg) - if err != nil { - return nil, errors.Wrap(err, "invalid advanced logging configuration") - } - - var ret []*model.FileData - for _, t := range cfg { - if t.Type != "file" { + var ( + rErr *multierror.Error + ret []*model.FileData + ) + + for name, loggingJSON := range map[string]json.RawMessage{ + "LogSettings.AdvancedLoggingJSON": ps.Config().LogSettings.AdvancedLoggingJSON, + "NotificationLogSettings.AdvancedLoggingJSON": ps.Config().NotificationLogSettings.AdvancedLoggingJSON, + "ExperimentalAuditSettings.AdvancedLoggingJSON": ps.Config().ExperimentalAuditSettings.AdvancedLoggingJSON, + } { + if utils.IsEmptyJSON(loggingJSON) { continue } - var fileOption struct { - Filename string `json:"filename"` - } - if err := json.Unmarshal(t.Options, &fileOption); err != nil { - return nil, errors.Wrap(err, "error decoding file target options") - } - data, err := os.ReadFile(fileOption.Filename) + + cfg := make(mlog.LoggerConfiguration) + err := json.Unmarshal(loggingJSON, &cfg) if err != nil { - return nil, errors.Wrapf(err, "failed to read notifcation log file at path %s", fileOption.Filename) + rErr = multierror.Append(rErr, errors.Wrapf(err, "error decoding advanced logging configuration %s", name)) + continue } - fileName := path.Base(fileOption.Filename) - ret = append(ret, &model.FileData{ - Filename: fileName, - Body: data, - }) + for _, t := range cfg { + if t.Type != "file" { + continue + } + var fileOption struct { + Filename string `json:"filename"` + } + if err := json.Unmarshal(t.Options, &fileOption); err != nil { + rErr = multierror.Append(rErr, errors.Wrapf(err, "error decoding file target options in %s", name)) + continue + } + data, err := os.ReadFile(fileOption.Filename) + if err != nil { + rErr = multierror.Append(rErr, errors.Wrapf(err, "failed to read advanced log file at path %s in %s", fileOption.Filename, name)) + continue + } + + fileName := path.Base(fileOption.Filename) + ret = append(ret, &model.FileData{ + Filename: fileName, + Body: data, + }) + } } return ret, nil diff --git a/server/channels/app/platform/log_test.go b/server/channels/app/platform/log_test.go index 3819070212f9..8d7fd1d421dc 100644 --- a/server/channels/app/platform/log_test.go +++ b/server/channels/app/platform/log_test.go @@ -114,7 +114,7 @@ func TestGetAdvancedLogs(t *testing.T) { th := Setup(t) defer th.TearDown() - t.Run("log messanges from std and LDAP level get returned", func(t *testing.T) { + t.Run("log messages from advanced logging settings get returned", func(t *testing.T) { dir, err := os.MkdirTemp("", "logs") require.NoError(t, err) t.Cleanup(func() { @@ -122,6 +122,7 @@ func TestGetAdvancedLogs(t *testing.T) { require.NoError(t, err) }) + // Setup log files for each setting optLDAP := map[string]string{ "filename": path.Join(dir, "ldap.log"), } @@ -134,7 +135,14 @@ func TestGetAdvancedLogs(t *testing.T) { dataStd, err := json.Marshal(optStd) require.NoError(t, err) - cfg := mlog.LoggerConfiguration{ + optNotif := map[string]string{ + "filename": path.Join(dir, "notification.log"), + } + dataNotif, err := json.Marshal(optNotif) + require.NoError(t, err) + + // LogSettings config + logCfg := mlog.LoggerConfiguration{ "ldap-file": mlog.TargetCfg{ Type: "file", Format: "json", @@ -155,34 +163,73 @@ func TestGetAdvancedLogs(t *testing.T) { Options: dataStd, }, } - cfgData, err := json.Marshal(cfg) + logCfgData, err := json.Marshal(logCfg) + require.NoError(t, err) + + // NotificationLogSettings config + notifCfg := mlog.LoggerConfiguration{ + "notification": mlog.TargetCfg{ + Type: "file", + Format: "json", + Levels: []mlog.Level{ + mlog.LvlInfo, + }, + Options: dataNotif, + }, + } + notifCfgData, err := json.Marshal(notifCfg) require.NoError(t, err) th.Service.UpdateConfig(func(c *model.Config) { - c.LogSettings.AdvancedLoggingJSON = cfgData + c.LogSettings.AdvancedLoggingJSON = logCfgData + c.NotificationLogSettings.AdvancedLoggingJSON = notifCfgData + // Audit logs are not testiable as they as part of the server, not the platform }) - th.Service.Logger().LogM([]mlog.Level{mlog.LvlLDAPInfo}, "Some LDAP info") - th.Service.Logger().Error("Some Error") - err = th.Service.Logger().Flush() + + // Write some logs and ensure they're flushed + logger := th.Service.Logger() + notifLogger := th.Service.NotificationsLogger() + + logger.LogM([]mlog.Level{mlog.LvlLDAPInfo}, "Some LDAP info") + logger.Error("Some Error") + notifLogger.Info("Some Notification") + + // Flush both loggers and wait a bit for filesystem + err = logger.Flush() + require.NoError(t, err) + err = notifLogger.Flush() require.NoError(t, err) + // Get and verify logs fileDatas, err := th.Service.GetAdvancedLogs(th.Context) require.NoError(t, err) - require.Len(t, fileDatas, 2) - - // Check the order of the log files - var ldapIndex = 0 - var stdIndex = 1 - if fileDatas[1].Filename == "ldap.log" { - ldapIndex = 1 - stdIndex = 0 + for _, fd := range fileDatas { + t.Log(fd.Filename) } + require.Len(t, fileDatas, 3) + + // Helper to find file data by name + findFile := func(name string) *model.FileData { + for _, fd := range fileDatas { + if fd.Filename == name { + return fd + } + } + return nil + } + + // Check each log file + ldapFile := findFile("ldap.log") + require.NotNil(t, ldapFile) + testlib.AssertLog(t, bytes.NewBuffer(ldapFile.Body), mlog.LvlLDAPInfo.Name, "Some LDAP info") - assert.Equal(t, "ldap.log", fileDatas[ldapIndex].Filename) - testlib.AssertLog(t, bytes.NewBuffer(fileDatas[ldapIndex].Body), mlog.LvlLDAPInfo.Name, "Some LDAP info") + stdFile := findFile("std.log") + require.NotNil(t, stdFile) + testlib.AssertLog(t, bytes.NewBuffer(stdFile.Body), mlog.LvlError.Name, "Some Error") - assert.Equal(t, "std.log", fileDatas[stdIndex].Filename) - testlib.AssertLog(t, bytes.NewBuffer(fileDatas[stdIndex].Body), mlog.LvlError.Name, "Some Error") + notifFile := findFile("notification.log") + require.NotNil(t, notifFile) + testlib.AssertLog(t, bytes.NewBuffer(notifFile.Body), mlog.LvlInfo.Name, "Some Notification") }) // Disable AdvancedLoggingJSON th.Service.UpdateConfig(func(c *model.Config) { From 90814564ff83f36c16526510215b8bb80301963e Mon Sep 17 00:00:00 2001 From: "unified-ci-app[bot]" <121569378+unified-ci-app[bot]@users.noreply.github.com> Date: Mon, 10 Feb 2025 13:50:08 +0200 Subject: [PATCH 022/164] chore: Update NOTICE.txt file with updated dependencies (#30152) Automatic Merge --- NOTICE.txt | 36 ------------------------------------ 1 file changed, 36 deletions(-) diff --git a/NOTICE.txt b/NOTICE.txt index 754e1030425b..71c5d76c632e 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -6966,42 +6966,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---- - -## key-mirror - -This product contains 'key-mirror' by Warren Mira. - -A simple key mirror tool pulled out from react. - -* HOMEPAGE: - * https://github.com/wmira/key-mirror - -* LICENSE: MIT - -The MIT License (MIT) - -Copyright (c) 2015 Warren Mira - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - --- ## klauspost/compress From 9b87970c99642baf5d091a77758a98e5cef9b103 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Mon, 10 Feb 2025 15:43:09 +0100 Subject: [PATCH 023/164] Migrating other 2 files from javascript to typescript (#29435) * feat: Add initial store configuration for webapp channels * refactor: Convert store/index.js to TypeScript with type definitions * test: Add initial test file for store index * refactor: Convert index.test.js to TypeScript with type annotations * Removing old files * Applying linter fixes * Fixing some of the types errors * fix: Type mock implementation of getState in global_actions.test.ts * test: Add missing GlobalState import in global_actions.test.ts * fix: Resolve TypeScript mock implementation error in global_actions.test.ts * Some fixes * Address CI problems * Installing zen-observable types * Addressing PR review comment * Addressing PR review comment * Addressing PR review comment * Addressing PR review comment * Addressing PR review comment * Simpliying things * Fixing CI * Fixing types --- webapp/channels/package.json | 1 + .../src/actions/global_actions.test.ts | 18 +++++---- .../channels/src/actions/user_actions.test.ts | 6 ++- ...channel_with_permissions_provider.test.tsx | 2 +- .../src/store/initial_state.ts | 1 + webapp/channels/src/plugins/index.ts | 8 ++-- .../src/plugins/interactive_dialog.ts | 4 +- .../channels/src/selectors/local_storage.ts | 4 +- .../store/{index.test.js => index.test.ts} | 0 .../channels/src/store/{index.js => index.ts} | 38 ++++++++++++------- .../src/stores/local_storage_store.ts | 4 +- .../src/tests/react_testing_utils.tsx | 2 +- webapp/channels/src/utils/utils.test.tsx | 4 +- webapp/channels/src/utils/utils.tsx | 6 +-- webapp/package-lock.json | 7 ++++ webapp/platform/types/src/integrations.ts | 2 + 16 files changed, 67 insertions(+), 40 deletions(-) rename webapp/channels/src/store/{index.test.js => index.test.ts} (100%) rename webapp/channels/src/store/{index.js => index.ts} (81%) diff --git a/webapp/channels/package.json b/webapp/channels/package.json index 842675757742..9e8a762af52a 100644 --- a/webapp/channels/package.json +++ b/webapp/channels/package.json @@ -140,6 +140,7 @@ "@types/shallow-equals": "1.0.3", "@types/styled-components": "5.1.32", "@types/tinycolor2": "1.4.6", + "@types/zen-observable": "0.8.7", "copy-webpack-plugin": "11.0.0", "emoji-datasource": "6.1.1", "emoji-datasource-apple": "6.1.1", diff --git a/webapp/channels/src/actions/global_actions.test.ts b/webapp/channels/src/actions/global_actions.test.ts index 8dcb1b332713..bd9fa82a8410 100644 --- a/webapp/channels/src/actions/global_actions.test.ts +++ b/webapp/channels/src/actions/global_actions.test.ts @@ -13,6 +13,8 @@ import reduxStore from 'stores/redux_store'; import mockStore from 'tests/test_store'; import {getHistory} from 'utils/browser_history'; +const getState = jest.mocked(reduxStore.getState); + jest.mock('actions/views/rhs', () => ({ closeMenu: jest.fn(), closeRightHandSide: jest.fn(), @@ -64,7 +66,7 @@ describe('actions/global_actions', () => { }, }); - reduxStore.getState.mockImplementation(store.getState); + getState.mockImplementation(store.getState); await redirectUserToDefaultTeam(); expect(getHistory().push).toHaveBeenCalledWith('/select_team'); @@ -138,7 +140,7 @@ describe('actions/global_actions', () => { }, }); - reduxStore.getState.mockImplementation(store.getState); + getState.mockImplementation(store.getState); await redirectUserToDefaultTeam(); expect(getHistory().push).toHaveBeenCalledWith('/team2/channels/channel-in-team-2'); @@ -211,7 +213,7 @@ describe('actions/global_actions', () => { }, }); - reduxStore.getState.mockImplementation(store.getState); + getState.mockImplementation(store.getState); await redirectUserToDefaultTeam(); expect(getHistory().push).toHaveBeenCalledWith('/team2/channels/channel-in-team-2'); @@ -283,7 +285,7 @@ describe('actions/global_actions', () => { }, }); - reduxStore.getState.mockImplementation(store.getState); + getState.mockImplementation(store.getState); await redirectUserToDefaultTeam(); expect(getHistory().push).toHaveBeenCalledWith('/select_team'); @@ -315,7 +317,7 @@ describe('actions/global_actions', () => { }, }); - reduxStore.getState.mockImplementation(store.getState); + getState.mockImplementation(store.getState); await redirectUserToDefaultTeam(); expect(getHistory().push).not.toHaveBeenCalled(); @@ -408,7 +410,7 @@ describe('actions/global_actions', () => { }, }, }); - reduxStore.getState.mockImplementation(store.getState); + getState.mockImplementation(store.getState); LocalStorageStore.setPreviousTeamId(userId, teamId); LocalStorageStore.setPreviousChannelName(userId, teamId, directChannelId); @@ -505,7 +507,7 @@ describe('actions/global_actions', () => { }, }, }); - reduxStore.getState.mockImplementation(store.getState); + getState.mockImplementation(store.getState); LocalStorageStore.setPreviousTeamId(userId, teamId); LocalStorageStore.setPreviousChannelName(userId, teamId, groupChannelId); @@ -567,7 +569,7 @@ describe('actions/global_actions', () => { }, }); - reduxStore.getState.mockImplementation(store.getState); + getState.mockImplementation(store.getState); await redirectUserToDefaultTeam(); expect(getHistory().push).toHaveBeenCalledWith('/team1/channels/channel-in-team-1'); diff --git a/webapp/channels/src/actions/user_actions.test.ts b/webapp/channels/src/actions/user_actions.test.ts index 8f62fd12389f..22aa9a940e40 100644 --- a/webapp/channels/src/actions/user_actions.test.ts +++ b/webapp/channels/src/actions/user_actions.test.ts @@ -1,6 +1,8 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. +import type {Dispatch, AnyAction} from 'redux'; + import type {Channel, ChannelMembership, ChannelMessageCount} from '@mattermost/types/channels'; import type {Post} from '@mattermost/types/posts'; import type {Team, TeamMembership} from '@mattermost/types/teams'; @@ -601,8 +603,8 @@ describe('Actions.User', () => { } as unknown as GlobalState; const testStore = mockStore(state); - store.getState.mockImplementation(testStore.getState); - store.dispatch.mockImplementation(testStore.dispatch); + (store.getState as jest.MockedFunction<() => GlobalState>).mockImplementation(testStore.getState); + (store.dispatch as jest.MockedFunction>).mockImplementation(testStore.dispatch); const actions = testStore.getActions(); await UserActions.loadProfilesForGM(); diff --git a/webapp/channels/src/components/suggestion/search_channel_with_permissions_provider.test.tsx b/webapp/channels/src/components/suggestion/search_channel_with_permissions_provider.test.tsx index 2387b14bc7d8..6e528e7b2ede 100644 --- a/webapp/channels/src/components/suggestion/search_channel_with_permissions_provider.test.tsx +++ b/webapp/channels/src/components/suggestion/search_channel_with_permissions_provider.test.tsx @@ -8,7 +8,7 @@ import {TestHelper} from 'utils/test_helper'; import SearchChannelWithPermissionsProvider from './search_channel_with_permissions_provider'; -const getState = store.getState; +const getState = jest.mocked(store.getState); jest.mock('stores/redux_store', () => ({ dispatch: jest.fn(), diff --git a/webapp/channels/src/packages/mattermost-redux/src/store/initial_state.ts b/webapp/channels/src/packages/mattermost-redux/src/store/initial_state.ts index 42563cc7e5c3..dccc2edcfd3b 100644 --- a/webapp/channels/src/packages/mattermost-redux/src/store/initial_state.ts +++ b/webapp/channels/src/packages/mattermost-redux/src/store/initial_state.ts @@ -133,6 +133,7 @@ const state: GlobalState = { commands: {}, appsBotIDs: [], appsOAuthAppIDs: [], + dialogTriggerId: '', outgoingOAuthConnections: {}, }, files: { diff --git a/webapp/channels/src/plugins/index.ts b/webapp/channels/src/plugins/index.ts index 0a9526db40b5..3e08596407cb 100644 --- a/webapp/channels/src/plugins/index.ts +++ b/webapp/channels/src/plugins/index.ts @@ -3,7 +3,7 @@ import regeneratorRuntime from 'regenerator-runtime'; -import type {PluginManifest} from '@mattermost/types/plugins'; +import type {PluginManifest, ClientPluginManifest} from '@mattermost/types/plugins'; import {Client4} from 'mattermost-redux/client'; import {Preferences} from 'mattermost-redux/constants'; @@ -102,8 +102,8 @@ export async function initializePlugins(): Promise { return; } - await Promise.all(data.map((m: PluginManifest) => { - return loadPlugin(m).catch((loadErr: Error) => { + await Promise.all(data.map(async (m: ClientPluginManifest) => { + return loadPlugin(m as PluginManifest).catch((loadErr: Error) => { console.error(loadErr.message); //eslint-disable-line no-console }); })); @@ -112,7 +112,7 @@ export async function initializePlugins(): Promise { } // getPlugins queries the server for all enabled plugins -export function getPlugins(): ActionFuncAsync { +export function getPlugins(): ActionFuncAsync { return async (dispatch) => { let plugins; try { diff --git a/webapp/channels/src/plugins/interactive_dialog.ts b/webapp/channels/src/plugins/interactive_dialog.ts index 1107644e5f78..56acf615e1f9 100644 --- a/webapp/channels/src/plugins/interactive_dialog.ts +++ b/webapp/channels/src/plugins/interactive_dialog.ts @@ -34,8 +34,8 @@ store.subscribe(() => { previousTriggerId = currentTriggerId; - const dialog = state.entities.integrations.dialog || {}; - if (dialog.trigger_id !== currentTriggerId) { + const dialog = state.entities.integrations.dialog; + if (!dialog || dialog.trigger_id !== currentTriggerId) { return; } diff --git a/webapp/channels/src/selectors/local_storage.ts b/webapp/channels/src/selectors/local_storage.ts index ed9c4b3ed997..383b25c11595 100644 --- a/webapp/channels/src/selectors/local_storage.ts +++ b/webapp/channels/src/selectors/local_storage.ts @@ -1,13 +1,13 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import type {GlobalState} from '@mattermost/types/store'; - import {getCurrentTeamId, getTeamByName} from 'mattermost-redux/selectors/entities/teams'; import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users'; import localStorageStore from 'stores/local_storage_store'; +import type {GlobalState} from 'types/store'; + // getLastViewedChannelName combines data from the Redux store and localStorage to return the // previously selected channel name, returning the default channel if none exists. // diff --git a/webapp/channels/src/store/index.test.js b/webapp/channels/src/store/index.test.ts similarity index 100% rename from webapp/channels/src/store/index.test.js rename to webapp/channels/src/store/index.test.ts diff --git a/webapp/channels/src/store/index.js b/webapp/channels/src/store/index.ts similarity index 81% rename from webapp/channels/src/store/index.js rename to webapp/channels/src/store/index.ts index a223adc0ae7c..21d79958a6a4 100644 --- a/webapp/channels/src/store/index.js +++ b/webapp/channels/src/store/index.ts @@ -3,9 +3,13 @@ import baseLocalForage from 'localforage'; import {extendPrototype} from 'localforage-observable'; +import type {Store} from 'redux'; +import type {Persistor} from 'redux-persist'; import {persistStore, REHYDRATE} from 'redux-persist'; import Observable from 'zen-observable'; +import type {DeepPartial} from '@mattermost/types/utilities'; + import {General, RequestStatus} from 'mattermost-redux/constants'; import configureServiceStore from 'mattermost-redux/store'; @@ -14,15 +18,23 @@ import {clearUserCookie} from 'actions/views/cookie'; import appReducers from 'reducers'; import {getBasePath} from 'selectors/general'; +import type {GlobalState} from 'types/store'; + function getAppReducers() { return require('../reducers'); // eslint-disable-line global-require } +declare global { + interface Window { + Observable: typeof Observable; + } +} + window.Observable = Observable; const localForage = extendPrototype(baseLocalForage); -export default function configureStore(preloadedState, additionalReducers) { +export default function configureStore(preloadedState?: DeepPartial, additionalReducers?: Record): Store { const reducers = additionalReducers ? {...appReducers, ...additionalReducers} : appReducers; const store = configureServiceStore({ appReducers: reducers, @@ -31,7 +43,7 @@ export default function configureStore(preloadedState, additionalReducers) { }); localForage.ready().then(() => { - const persistor = persistStore(store, null, () => { + const persistor: Persistor = persistStore(store, null, () => { store.dispatch({ type: General.STORE_REHYDRATION_COMPLETE, complete: true, @@ -51,23 +63,23 @@ export default function configureStore(preloadedState, additionalReducers) { // Rehydrate redux-persist when another tab changes localForage observable.subscribe({ - next: (args) => { - if (!args.crossTabNotification) { + next: (value) => { + if (!value.crossTabNotification) { // Ignore changes made by this tab return; } const keyPrefix = 'persist:'; - if (!args.key.startsWith(keyPrefix)) { + if (!value.key.startsWith(keyPrefix)) { // Ignore changes that weren't made by redux-persist return; } - const key = args.key.substring(keyPrefix.length); - const newValue = JSON.parse(args.newValue); + const key = value.key.substring(keyPrefix.length); + const newValue = JSON.parse(value.newValue); - const payload = {}; + const payload: Record = {}; for (const reducerKey of Object.keys(newValue)) { if (reducerKey === '_persist') { @@ -110,7 +122,7 @@ export default function configureStore(preloadedState, additionalReducers) { }); } }); - }).catch((e) => { + }).catch((e: Error) => { // eslint-disable-next-line no-console console.error('Failed to initialize localForage', e); }); @@ -121,11 +133,11 @@ export default function configureStore(preloadedState, additionalReducers) { /** * Migrates state.storage from redux-persist@4 to redux-persist@6 */ -function migratePersistedState(store, persistor) { +function migratePersistedState(store: Store, persistor: Persistor): void { const oldKeyPrefix = 'reduxPersist:storage:'; - const restoredState = {}; - localForage.iterate((value, key) => { + const restoredState: Record = {}; + localForage.iterate((value: string, key: string) => { if (key && key.startsWith(oldKeyPrefix)) { restoredState[key.substring(oldKeyPrefix.length)] = value; } @@ -140,7 +152,7 @@ function migratePersistedState(store, persistor) { persistor.pause(); - const persistedState = {}; + const persistedState: Record = {}; for (const [key, value] of Object.entries(restoredState)) { // eslint-disable-next-line no-console diff --git a/webapp/channels/src/stores/local_storage_store.ts b/webapp/channels/src/stores/local_storage_store.ts index ee25ecca37d3..8528a3824889 100644 --- a/webapp/channels/src/stores/local_storage_store.ts +++ b/webapp/channels/src/stores/local_storage_store.ts @@ -91,12 +91,12 @@ class LocalStorageStoreClass { this.removeItem(getPenultimateChannelNameKey(userId, teamId)); } - removePreviousChannelType(userId: string, teamId: string, state = store.getStore()) { + removePreviousChannelType(userId: string, teamId: string, state = store.getState()) { this.setItem(getPreviousViewedTypeKey(userId, teamId), this.getPenultimateViewedType(userId, teamId, state)); this.removeItem(getPenultimateViewedTypeKey(userId, teamId)); } - removePreviousChannel(userId: string, teamId: string, state = store.getStore()) { + removePreviousChannel(userId: string, teamId: string, state = store.getState()) { this.removePreviousChannelName(userId, teamId, state); this.removePreviousChannelType(userId, teamId, state); } diff --git a/webapp/channels/src/tests/react_testing_utils.tsx b/webapp/channels/src/tests/react_testing_utils.tsx index 8693222b553c..dfb3334917c0 100644 --- a/webapp/channels/src/tests/react_testing_utils.tsx +++ b/webapp/channels/src/tests/react_testing_utils.tsx @@ -145,7 +145,7 @@ function replaceGlobalStore(getStore: () => any) { jest.spyOn(globalStore, 'dispatch').mockImplementation((...args) => getStore().dispatch(...args)); jest.spyOn(globalStore, 'getState').mockImplementation(() => getStore().getState()); jest.spyOn(globalStore, 'replaceReducer').mockImplementation((...args) => getStore().replaceReducer(...args)); - jest.spyOn(globalStore, '@@observable').mockImplementation((...args) => getStore()['@@observable'](...args)); + jest.spyOn(globalStore, '@@observable' as any).mockImplementation((...args: any[]) => getStore()['@@observable'](...args)); // This may stop working if getStore starts to return new results jest.spyOn(globalStore, 'subscribe').mockImplementation((...args) => getStore().subscribe(...args)); diff --git a/webapp/channels/src/utils/utils.test.tsx b/webapp/channels/src/utils/utils.test.tsx index e26f15b00245..0d126c664c39 100644 --- a/webapp/channels/src/utils/utils.test.tsx +++ b/webapp/channels/src/utils/utils.test.tsx @@ -172,7 +172,7 @@ describe('Utils.localizeMessage', () => { }, }, }, - }); + } as any); }); test('with translations', () => { @@ -200,7 +200,7 @@ describe('Utils.localizeMessage', () => { translations: {}, }, }, - }); + } as any); }); test('without translations', () => { diff --git a/webapp/channels/src/utils/utils.tsx b/webapp/channels/src/utils/utils.tsx index 0d3487c13dab..62beab83bdd2 100644 --- a/webapp/channels/src/utils/utils.tsx +++ b/webapp/channels/src/utils/utils.tsx @@ -1286,7 +1286,7 @@ export async function handleFormattedTextClick(e: React.MouseEvent, currentRelat let post = getPost(state, postId!); if (!post) { const {data: postData} = await store.dispatch(getPostAction(match.postId!)); - post = postData; + post = postData!; } if (post) { isReply = Boolean(post.root_id); @@ -1303,12 +1303,12 @@ export async function handleFormattedTextClick(e: React.MouseEvent, currentRelat if (!member) { const membership = await store.dispatch(getChannelMember(channel.id, getCurrentUserId(state))); if ('data' in membership) { - member = membership.data; + member = membership.data!; } } if (!member) { const {data} = await store.dispatch(joinPrivateChannelPrompt(team, channel.display_name, false)); - if (data.join) { + if (data!.join) { let error = false; if (!getTeamMemberships(state)[team.id]) { const joinTeamResult = await store.dispatch(addUserToTeam(team.id, user.id)); diff --git a/webapp/package-lock.json b/webapp/package-lock.json index b8f66011b2b0..5bb720e34066 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -193,6 +193,7 @@ "@types/shallow-equals": "1.0.3", "@types/styled-components": "5.1.32", "@types/tinycolor2": "1.4.6", + "@types/zen-observable": "0.8.7", "copy-webpack-plugin": "11.0.0", "emoji-datasource": "6.1.1", "emoji-datasource-apple": "6.1.1", @@ -7310,6 +7311,12 @@ "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", "dev": true }, + "node_modules/@types/zen-observable": { + "version": "0.8.7", + "resolved": "https://registry.npmjs.org/@types/zen-observable/-/zen-observable-0.8.7.tgz", + "integrity": "sha512-LKzNTjj+2j09wAo/vvVjzgw5qckJJzhdGgWHW7j69QIGdq/KnZrMAMIHQiWGl3Ccflh5/CudBAntTPYdprPltA==", + "dev": true + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "7.18.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz", diff --git a/webapp/platform/types/src/integrations.ts b/webapp/platform/types/src/integrations.ts index 582db3096fb4..44141b8263f7 100644 --- a/webapp/platform/types/src/integrations.ts +++ b/webapp/platform/types/src/integrations.ts @@ -134,9 +134,11 @@ export type IntegrationsState = { appsBotIDs: string[]; systemCommands: IDMappedObjects; commands: IDMappedObjects; + dialogTriggerId: string; dialog?: { url: string; dialog: Dialog; + trigger_id: string; }; }; From f6c4bdf365aa960b70321758723008bf0465c4e8 Mon Sep 17 00:00:00 2001 From: Arya Khochare <91268931+Aryakoste@users.noreply.github.com> Date: Mon, 10 Feb 2025 21:32:50 +0530 Subject: [PATCH 024/164] [MM-62149] Avoid SELECT * in emoji_store.go (#30082) * refractored select sql queries * rm unused makeStringArgs * linting * leverage builder pattern --------- Co-authored-by: Jesse Hallam --- server/channels/store/sqlstore/emoji_store.go | 76 ++++++++----------- server/channels/store/sqlstore/utils.go | 11 +-- 2 files changed, 33 insertions(+), 54 deletions(-) diff --git a/server/channels/store/sqlstore/emoji_store.go b/server/channels/store/sqlstore/emoji_store.go index bc1f97d984e2..8c834291e86a 100644 --- a/server/channels/store/sqlstore/emoji_store.go +++ b/server/channels/store/sqlstore/emoji_store.go @@ -6,7 +6,6 @@ package sqlstore import ( "database/sql" "fmt" - "strings" "github.com/pkg/errors" @@ -14,17 +13,26 @@ import ( "github.com/mattermost/mattermost/server/public/shared/request" "github.com/mattermost/mattermost/server/v8/channels/store" "github.com/mattermost/mattermost/server/v8/einterfaces" + sq "github.com/mattermost/squirrel" ) type SqlEmojiStore struct { *SqlStore metrics einterfaces.MetricsInterface + + emojiSelectQuery sq.SelectBuilder } func newSqlEmojiStore(sqlStore *SqlStore, metrics einterfaces.MetricsInterface) store.EmojiStore { + emojiSelectQuery := sqlStore.getQueryBuilder(). + Select("Id", "CreateAt", "UpdateAt", "DeleteAt", "CreatorId", "Name"). + From("Emoji"). + Where(sq.Eq{"DeleteAt": 0}) + return &SqlEmojiStore{ - SqlStore: sqlStore, - metrics: metrics, + SqlStore: sqlStore, + metrics: metrics, + emojiSelectQuery: emojiSelectQuery, } } @@ -53,36 +61,27 @@ func (es SqlEmojiStore) GetByName(c request.CTX, name string, allowFromCache boo } func (es SqlEmojiStore) GetMultipleByName(c request.CTX, names []string) ([]*model.Emoji, error) { - // Creating (?, ?, ?) len(names) number of times. - keys := strings.Join(strings.Fields(strings.Repeat("? ", len(names))), ",") - args := makeStringArgs(names) - - emojis := []*model.Emoji{} - if err := es.DBXFromContext(c.Context()).Select(&emojis, - `SELECT - * - FROM - Emoji - WHERE - Name IN (`+keys+`) - AND DeleteAt = 0`, args...); err != nil { - return nil, errors.Wrapf(err, "error getting emoji by names %v", names) + query := es.emojiSelectQuery.Where(sq.Eq{"Name": names}) + + var emojis []*model.Emoji + if err := es.DBXFromContext(c.Context()).SelectBuilder(&emojis, query); err != nil { + return nil, errors.Wrapf(err, "error getting emojis by names %v", names) } + return emojis, nil } func (es SqlEmojiStore) GetList(offset, limit int, sort string) ([]*model.Emoji, error) { - emojis := []*model.Emoji{} - - query := "SELECT * FROM Emoji WHERE DeleteAt = 0" + var emojis []*model.Emoji + query := es.emojiSelectQuery if sort == model.EmojiSortByName { - query += " ORDER BY Name" + query = query.OrderBy("Name") } - query += " LIMIT ? OFFSET ?" + query = query.Limit(uint64(limit)).Offset(uint64(offset)) - if err := es.GetReplica().Select(&emojis, query, limit, offset); err != nil { + if err := es.GetReplica().SelectBuilder(&emojis, query); err != nil { return nil, errors.Wrap(err, "could not get list of emojis") } return emojis, nil @@ -107,7 +106,7 @@ func (es SqlEmojiStore) Delete(emoji *model.Emoji, time int64) error { } func (es SqlEmojiStore) Search(name string, prefixOnly bool, limit int) ([]*model.Emoji, error) { - emojis := []*model.Emoji{} + var emojis []*model.Emoji name = sanitizeSearchTerm(name, "\\") @@ -115,19 +114,14 @@ func (es SqlEmojiStore) Search(name string, prefixOnly bool, limit int) ([]*mode if !prefixOnly { term = "%" } - term += name + "%" - if err := es.GetReplica().Select(&emojis, - `SELECT - * - FROM - Emoji - WHERE - Name LIKE ? - AND DeleteAt = 0 - ORDER BY Name - LIMIT ?`, term, limit); err != nil { + query := es.emojiSelectQuery. + Where(sq.Like{"Name": term}). + OrderBy("Name"). + Limit(uint64(limit)) + + if err := es.GetReplica().SelectBuilder(&emojis, query); err != nil { return nil, errors.Wrapf(err, "could not search emojis by name %s", name) } return emojis, nil @@ -137,19 +131,13 @@ func (es SqlEmojiStore) Search(name string, prefixOnly bool, limit int) ([]*mode func (es SqlEmojiStore) getBy(c request.CTX, what, key string) (*model.Emoji, error) { var emoji model.Emoji - err := es.DBXFromContext(c.Context()).Get(&emoji, - `SELECT - * - FROM - Emoji - WHERE - `+what+` = ? - AND DeleteAt = 0`, key) + query := es.emojiSelectQuery.Where(sq.Eq{what: key}) + + err := es.DBXFromContext(c.Context()).GetBuilder(&emoji, query) if err != nil { if err == sql.ErrNoRows { return nil, store.NewErrNotFound("Emoji", fmt.Sprintf("%s=%s", what, key)) } - return nil, errors.Wrapf(err, "could not get emoji by %s with value %s", what, key) } diff --git a/server/channels/store/sqlstore/utils.go b/server/channels/store/sqlstore/utils.go index 6ce0dda45b37..4aed34836d2d 100644 --- a/server/channels/store/sqlstore/utils.go +++ b/server/channels/store/sqlstore/utils.go @@ -126,14 +126,6 @@ func constructMySQLJSONArgs(props map[string]string) ([]any, string) { return args, argString } -func makeStringArgs(params []string) []any { - args := make([]any, len(params)) - for i, name := range params { - args[i] = name - } - return args -} - func constructArrayArgs(ids []string) (string, []any) { var placeholder strings.Builder values := make([]any, 0, len(ids)) @@ -160,8 +152,7 @@ func wrapBinaryParamStringMap(ok bool, props model.StringMap) model.StringMap { // morphWriter is a target to pass to the logger instance of morph. // For now, everything is just logged at a debug level. If we need to log // errors/warnings from the library also, that needs to be seen later. -type morphWriter struct { -} +type morphWriter struct{} func (l *morphWriter) Write(in []byte) (int, error) { mlog.Debug(string(in)) From 7efdcda20a91c1fd2b35de7e806dac5a6b32f74b Mon Sep 17 00:00:00 2001 From: Maria A Nunez Date: Mon, 10 Feb 2025 11:55:42 -0500 Subject: [PATCH 025/164] Update User Limits for unlicensed servers (#30134) * Update user soft and hard limits * Fix testt * Fix test --- server/channels/app/limits.go | 4 ++-- server/channels/app/limits_test.go | 2 +- server/channels/app/user_test.go | 2 +- .../src/packages/mattermost-redux/src/actions/limits.test.ts | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/server/channels/app/limits.go b/server/channels/app/limits.go index ff216fd74c86..669715d64753 100644 --- a/server/channels/app/limits.go +++ b/server/channels/app/limits.go @@ -12,8 +12,8 @@ import ( ) const ( - maxUsersLimit = 5_000 - maxUsersHardLimit = 11_000 + maxUsersLimit = 2_500 + maxUsersHardLimit = 5_000 ) func (a *App) GetServerLimits() (*model.ServerLimits, *model.AppError) { diff --git a/server/channels/app/limits_test.go b/server/channels/app/limits_test.go index 852a69b91569..06b6c10ea8f3 100644 --- a/server/channels/app/limits_test.go +++ b/server/channels/app/limits_test.go @@ -20,7 +20,7 @@ func TestGetServerLimits(t *testing.T) { // InitBasic creates 3 users by default require.Equal(t, int64(3), serverLimits.ActiveUserCount) - require.Equal(t, int64(5000), serverLimits.MaxUsersLimit) + require.Equal(t, int64(2500), serverLimits.MaxUsersLimit) }) t.Run("user count should increase on creating new user and decrease on permanently deleting", func(t *testing.T) { diff --git a/server/channels/app/user_test.go b/server/channels/app/user_test.go index eb91bbc10c33..c0b668ff4d14 100644 --- a/server/channels/app/user_test.go +++ b/server/channels/app/user_test.go @@ -2306,7 +2306,7 @@ func TestCreateUserOrGuest(t *testing.T) { defer th.TearDown() id := NewTestId() - userCreationMocks(t, th, id, 11000) + userCreationMocks(t, th, id, 5000) user := &model.User{ Email: "TestCreateUserOrGuest@example.com", diff --git a/webapp/channels/src/packages/mattermost-redux/src/actions/limits.test.ts b/webapp/channels/src/packages/mattermost-redux/src/actions/limits.test.ts index dff50bbeeade..753e00fd1ffe 100644 --- a/webapp/channels/src/packages/mattermost-redux/src/actions/limits.test.ts +++ b/webapp/channels/src/packages/mattermost-redux/src/actions/limits.test.ts @@ -76,7 +76,7 @@ describe('getServerLimits', () => { test('should return data if user is admin', async () => { const userLimits: ServerLimits = { activeUserCount: 600, - maxUsersLimit: 5_000, + maxUsersLimit: 2_500, }; From dc37cf5cf9f40c3ca6ba311d9510d5dfeb30d4f0 Mon Sep 17 00:00:00 2001 From: "Weblate (bot)" Date: Mon, 10 Feb 2025 18:50:10 +0100 Subject: [PATCH 026/164] Translations update from Mattermost Weblate (#30157) Automatic Merge --- server/i18n/cs.json | 16 ++- server/i18n/de.json | 8 +- server/i18n/en-AU.json | 148 ++++++++++++++++++++++-- server/i18n/fr.json | 4 - server/i18n/ja.json | 16 ++- server/i18n/nl.json | 16 ++- server/i18n/pl.json | 4 - server/i18n/pt-BR.json | 4 - server/i18n/ru.json | 4 - server/i18n/sv.json | 4 - server/i18n/tr.json | 8 +- server/i18n/uk.json | 16 ++- server/i18n/zh-CN.json | 16 ++- server/i18n/zh-TW.json | 4 - webapp/channels/src/i18n/be.json | 3 - webapp/channels/src/i18n/bg.json | 3 - webapp/channels/src/i18n/cs.json | 168 +++++++++++++++++++++------- webapp/channels/src/i18n/de.json | 13 ++- webapp/channels/src/i18n/en-AU.json | 99 ++++++++++++++-- webapp/channels/src/i18n/es.json | 3 - webapp/channels/src/i18n/fa.json | 3 - webapp/channels/src/i18n/fr.json | 3 - webapp/channels/src/i18n/fy.json | 3 - webapp/channels/src/i18n/hu.json | 3 - webapp/channels/src/i18n/it.json | 3 - webapp/channels/src/i18n/ja.json | 104 +++++++++++++++-- webapp/channels/src/i18n/ko.json | 3 - webapp/channels/src/i18n/lt.json | 3 - webapp/channels/src/i18n/nb-NO.json | 12 ++ webapp/channels/src/i18n/nl.json | 28 +++-- webapp/channels/src/i18n/pl.json | 3 - webapp/channels/src/i18n/pt-BR.json | 3 - webapp/channels/src/i18n/ro.json | 3 - webapp/channels/src/i18n/ru.json | 50 ++++++++- webapp/channels/src/i18n/sv.json | 3 - webapp/channels/src/i18n/tr.json | 13 ++- webapp/channels/src/i18n/uk.json | 48 ++++++-- webapp/channels/src/i18n/vi.json | 3 - webapp/channels/src/i18n/zh-CN.json | 3 - webapp/channels/src/i18n/zh-TW.json | 3 - 40 files changed, 648 insertions(+), 206 deletions(-) diff --git a/server/i18n/cs.json b/server/i18n/cs.json index 4a081d7370f4..dfc802f5b8a1 100644 --- a/server/i18n/cs.json +++ b/server/i18n/cs.json @@ -7807,10 +7807,6 @@ "id": "app.channel.add_member.deleted_user.app_error", "translation": "Nelze přidat uživatele jako člena kanálu." }, - { - "id": "app.channel.elasticsearch_channel_index.notify_admin.message", - "translation": "Schéma indexu kanálů pro vyhledávání je zastaralé. Doporučujeme znovu vygenerovat index kanálů.\nKlikněte na tlačítko `Rebuild Channels Index` [na stránce Elasticsearch prostřednictvím systémové konzole]({{.ElasticsearchSection}}), abyste tento problém vyřešili.\nDalší informace naleznete v changelogu Mattermost." - }, { "id": "app.channel.bookmark.save.app_error", "translation": "Nepodařilo se uložit záložku." @@ -10394,5 +10390,17 @@ { "id": "model.property_value.is_valid.app_error", "translation": "Neplatná hodnota vlastnosti: {{.FieldName}} ({{.Reason}})." + }, + { + "id": "api.custom_profile_attributes.license_error", + "translation": "Vaše licence nepodporuje vlastní atributy profilu." + }, + { + "id": "api.file.zip_file_reader.app_error", + "translation": "Nelze získat čtečku souborů ZIP." + }, + { + "id": "api.command.execute_command.deleted.error", + "translation": "Nelze spustit příkaz v odstraněném kanálu." } ] diff --git a/server/i18n/de.json b/server/i18n/de.json index 9dd25c2132f8..7f3f44dcb9a7 100644 --- a/server/i18n/de.json +++ b/server/i18n/de.json @@ -9737,10 +9737,6 @@ "id": "api.config.update.elasticsearch.autocomplete_cannot_be_enabled_error", "translation": "Die automatische Vervollständigung von Kanälen kann nicht aktiviert werden, da das Schema des Kanal-Index veraltet ist. Es wird empfohlen, den Kanal-Index neu zu erstellen. Weitere Informationen findest du im Mattermost Changelog" }, - { - "id": "app.channel.elasticsearch_channel_index.notify_admin.message", - "translation": "Das Indexschema deines Suchkanals ist nicht mehr aktuell. Wir empfehlen dir, deinen Kanalindex neu zu erstellen.\nKlicke auf die Schaltfläche \"Index der Kanäle neu erstellen\" auf der [Elasticsearch-Seite über die Systemkonsole] ({{.ElasticsearchSection}}), um dieses Problem zu beheben.\nWeitere Informationen findest du im Mattermost Changelog." - }, { "id": "bleveengine.purge_list.not_implemented", "translation": "Die Funktion Bereinigungsliste ist für Bleve nicht verfügbar." @@ -10398,5 +10394,9 @@ { "id": "api.file.zip_file_reader.app_error", "translation": "Es ist nicht möglich, einen Zip-Datei-Leser zu bekommen." + }, + { + "id": "api.command.execute_command.deleted.error", + "translation": "Kommando kann nicht in einem gelöschten Kanal ausgeführt werden." } ] diff --git a/server/i18n/en-AU.json b/server/i18n/en-AU.json index f9c3408f2180..2b77e755a52a 100644 --- a/server/i18n/en-AU.json +++ b/server/i18n/en-AU.json @@ -1137,11 +1137,11 @@ }, { "id": "model.config.is_valid.elastic_search.enable_searching.app_error", - "translation": "Search EnableIndexing setting must be set to true when Elasticsearch EnableSearching is set to true" + "translation": "{{.EnableIndexing}} setting must be set to true when {{.Searching}} is set to true" }, { "id": "model.config.is_valid.elastic_search.enable_autocomplete.app_error", - "translation": "Search EnableIndexing setting must be set to true when Elasticsearch EnableAutocomplete is set to true" + "translation": "{{.EnableIndexing}} setting must be set to true when {{.Autocomplete}} is set to true" }, { "id": "model.config.is_valid.elastic_search.connection_url.app_error", @@ -3125,11 +3125,11 @@ }, { "id": "ent.elasticsearch.test_config.reenter_password", - "translation": "The Elasticsearch Server URL or Username has changed. Please re-enter the Elasticsearch password to test connection." + "translation": "The Search Server URL or Username has changed. Please re-enter the password to test connection." }, { "id": "ent.elasticsearch.test_config.license.error", - "translation": "Your licence does not support Elasticsearch." + "translation": "Your Mattermost licence doesn't support indexed search." }, { "id": "ent.elasticsearch.test_config.indexing_disabled.error", @@ -3197,7 +3197,7 @@ }, { "id": "ent.elasticsearch.refresh_indexes.refresh_failed", - "translation": "Failed to refresh Elasticsearch indexes" + "translation": "Failed to refresh search indexes" }, { "id": "ent.elasticsearch.post.get_posts_batch_for_indexing.error", @@ -9574,10 +9574,6 @@ "id": "ent.elasticsearch.purge_indexes.unknown_index", "translation": "Failed to delete an unknown index specified" }, - { - "id": "app.channel.elasticsearch_channel_index.notify_admin.message", - "translation": "Your Elasticsearch channel index schema is out of date. It is recommended to regenerate your channel index.\nClick the `Rebuild Channels Index` button in [Elasticsearch section in System Console]({{.ElasticsearchSection}}) to fix the issue.\nSee Mattermost changelog for more information." - }, { "id": "model.channel_bookmark.is_valid.link_file.app_error", "translation": "Cannot set a link and a file in the same bookmark." @@ -9728,7 +9724,7 @@ }, { "id": "ent.elasticsearch.purge_index.delete_failed", - "translation": "Failed to delete an Elasticsearch index" + "translation": "Failed to delete a search index" }, { "id": "model.channel_bookmark.is_valid.image_url.app_error", @@ -10267,5 +10263,137 @@ { "id": "model.post.is_valid.message_length.app_error", "translation": "Post Message property is longer than the maximum permitted length." + }, + { + "id": "api.command.execute_command.deleted.error", + "translation": "Command can't be executed in a deleted channel." + }, + { + "id": "api.filter_config_error", + "translation": "Unable to filter the configuration." + }, + { + "id": "app.export.export_custom_emoji.mkdir.error", + "translation": "Unable to create a directory for custom emoji images" + }, + { + "id": "app.custom_profile_attributes.cpa_group_id.app_error", + "translation": "Cannot register Custom Profile Attributes property group" + }, + { + "id": "app.custom_profile_attributes.get_property_field.app_error", + "translation": "Unable to get Custom Profile Attribute field" + }, + { + "id": "app.custom_profile_attributes.create_property_field.app_error", + "translation": "Unable to create Custom Profile Attribute field" + }, + { + "id": "app.custom_profile_attributes.limit_reached.app_error", + "translation": "Custom Profile Attributes field limit reached" + }, + { + "id": "app.custom_profile_attributes.list_property_values.app_error", + "translation": "Unable to get custom profile attribute values" + }, + { + "id": "app.custom_profile_attributes.property_value_creation.app_error", + "translation": "Cannot create property value" + }, + { + "id": "app.custom_profile_attributes.property_value_list.app_error", + "translation": "Unable to retrieve property values" + }, + { + "id": "app.custom_profile_attributes.property_field_delete.app_error", + "translation": "Unable to delete Custom Profile Attribute field" + }, + { + "id": "app.custom_profile_attributes.property_field_not_found.app_error", + "translation": "Custom Profile Attribute field not found" + }, + { + "id": "app.custom_profile_attributes.property_field_update.app_error", + "translation": "Unable to update Custom Profile Attribute field" + }, + { + "id": "app.custom_profile_attributes.search_property_fields.app_error", + "translation": "Unable to search Custom Profile Attribute fields" + }, + { + "id": "app.file_info.delete_for_post_ids.app_error", + "translation": "Failed to remove the requested files from database" + }, + { + "id": "app.custom_profile_attributes.property_value_update.app_error", + "translation": "Cannot update property value" + }, + { + "id": "app.file_info.undelete_for_post_ids.app_error", + "translation": "Failed to restore post file attachments." + }, + { + "id": "app.import.validate_user_import_data.guest_roles_conflict.error", + "translation": "User roles are not consistent with guest status." + }, + { + "id": "app.post.restore_post_version.not_allowed.app_error", + "translation": "You do not have the appropriate permissions." + }, + { + "id": "app.post.restore_post_version.get_single.app_error", + "translation": "Failed to get the old post version." + }, + { + "id": "app.post.restore_post_version.not_an_history_item.app_error", + "translation": "The provided post history ID does not correspond to any history item for the specified post." + }, + { + "id": "app.post.restore_post_version.not_valid_post_history_item.app_error", + "translation": "The provided post history ID does not correspond to a post history item." + }, + { + "id": "app.role.delete.app_error", + "translation": "Unable to delete role." + }, + { + "id": "ent.message_export.actiance_export.calculate_channel_exports.channel_message", + "translation": "Exporting channel information for {{.NumChannels}} channels." + }, + { + "id": "ent.message_export.calculate_channel_exports.app_error", + "translation": "Failed to calculate channel export data." + }, + { + "id": "ent.message_export.job_data_conversion.app_error", + "translation": "Failed to convert a value from the job's data field." + }, + { + "id": "ent.message_export.actiance_export.calculate_channel_exports.activity_message", + "translation": "Calculating channel activity: {{.NumCompleted}}/{{.NumChannels}} channels completed." + }, + { + "id": "model.property_field.is_valid.app_error", + "translation": "Invalid property field: {{.FieldName}} ({{.Reason}})." + }, + { + "id": "model.property_value.is_valid.app_error", + "translation": "Invalid property value: {{.FieldName}} ({{.Reason}})." + }, + { + "id": "api.custom_profile_attributes.license_error", + "translation": "Your licence does not support Custom Profile Attributes." + }, + { + "id": "api.file.zip_file_reader.app_error", + "translation": "Unable to get a zip file reader." + }, + { + "id": "app.file_info.get_by_ids.app_error", + "translation": "Unable to get the file infos by ids for post edit history." + }, + { + "id": "model.link_metadata.is_valid.url_length.app_error", + "translation": "Length of link metadata URL is {{ .Length }} characters long, which exceeds the maximum limit of {{ .MaxLength }} characters." } ] diff --git a/server/i18n/fr.json b/server/i18n/fr.json index 33aaaca0833e..7b442ec26176 100644 --- a/server/i18n/fr.json +++ b/server/i18n/fr.json @@ -8851,10 +8851,6 @@ "id": "api.oauth.get_access_token.bad_request.app_error", "translation": "invalid_request : Bad request." }, - { - "id": "app.channel.elasticsearch_channel_index.notify_admin.message", - "translation": "Votre schéma d'index de canal n'est pas à jour sur Elasticsearch. Il est recommandé de régénérer votre index de canal.\nCliquez sur le bouton `Reconstruire l'index des canaux` de la [section Elasticsearch dans la console système]({{.ElasticsearchSection}}) pour régler ce problème.\nConsultez le journal des modifications pour plus de détails." - }, { "id": "model.outgoing_oauth_connection.is_valid.oauth_token_url.error", "translation": "URL du jeton OAuth (OAuth Token URL) invalide." diff --git a/server/i18n/ja.json b/server/i18n/ja.json index cd8b0fa1f2df..86511f19b581 100644 --- a/server/i18n/ja.json +++ b/server/i18n/ja.json @@ -9569,10 +9569,6 @@ "id": "app.channel.bookmark.update_sort.app_error", "translation": "ブックマークをソートできませんでした。" }, - { - "id": "app.channel.elasticsearch_channel_index.notify_admin.message", - "translation": "検索チャネルインデックススキーマが古くなっています。チャンネルインデックスの再生成を推奨します。\nこの問題を解決するには、[システムコンソールの Elasticsearch ページ]({{.ElasticsearchSection}}) にある `チャンネルインデックスの再構築` ボタンをクリックしてください。\n詳しくは Mattermostの変更履歴を参照してください。" - }, { "id": "app.import.import_line.null_role.error", "translation": "インポートデータ行に \"role\" タイプがありますが、roleオブジェクトがnullです。" @@ -10380,5 +10376,17 @@ { "id": "model.property_value.is_valid.app_error", "translation": "不正なプロパティ値: {{.FieldName}} {{.Reason}}。" + }, + { + "id": "api.command.execute_command.deleted.error", + "translation": "削除されたチャンネルではコマンドを実行できません。" + }, + { + "id": "api.custom_profile_attributes.license_error", + "translation": "あなたのライセンスはカスタムプロフィール属性をサポートしていません。" + }, + { + "id": "api.file.zip_file_reader.app_error", + "translation": "ZIPファイルリーダーを取得できませんでした。" } ] diff --git a/server/i18n/nl.json b/server/i18n/nl.json index f3d01d841d07..4435ac30f30a 100644 --- a/server/i18n/nl.json +++ b/server/i18n/nl.json @@ -9733,10 +9733,6 @@ "id": "api.config.update.elasticsearch.autocomplete_cannot_be_enabled_error", "translation": "Kanalen automatisch aanvullen kan niet worden ingeschakeld omdat het schema van de kanaalindex verouderd is. Het wordt aanbevolen om je kanaalindex te regenereren. Zie de Mattermost changelog voor meer informatie" }, - { - "id": "app.channel.elasticsearch_channel_index.notify_admin.message", - "translation": "Het schema van de index van je zoekkanalen is verouderd. We raden aan om je kanaalindex te regenereren.\nKlik op de `Rebuild Channels Index` knop in de [Elasticsearch pagina via de System Console]({{.ElasticsearchSection}}) om dit probleem op te lossen.\nZie de Mattermost changelog voor meer informatie." - }, { "id": "bleveengine.purge_list.not_implemented", "translation": "De functie lijst leegmaken is niet beschikbaar voor Bleve." @@ -10402,5 +10398,17 @@ { "id": "app.custom_profile_attributes.property_value_update.app_error", "translation": "Kan de waarde van de eigenschap niet bijwerken" + }, + { + "id": "api.command.execute_command.deleted.error", + "translation": "Kan geen opdracht uitvoeren in verwijderde chatroom." + }, + { + "id": "api.file.zip_file_reader.app_error", + "translation": "Kon geen zip-bestandslezer vinden." + }, + { + "id": "api.custom_profile_attributes.license_error", + "translation": "Jouw licentie ondersteunt geen Aangepaste Profielattributen." } ] diff --git a/server/i18n/pl.json b/server/i18n/pl.json index bc6413e5e570..4d43f891da8d 100644 --- a/server/i18n/pl.json +++ b/server/i18n/pl.json @@ -9739,10 +9739,6 @@ "id": "api.config.update.elasticsearch.autocomplete_cannot_be_enabled_error", "translation": "Nie można włączyć autouzupełniania kanałów, ponieważ schemat indeksu kanałów jest nieaktualny. Zaleca się regenerację indeksu kanałów. Zobacz dziennik zmian Mattermost, aby uzyskać więcej informacji" }, - { - "id": "app.channel.elasticsearch_channel_index.notify_admin.message", - "translation": "Twój schemat indeksu kanałów wyszukiwania jest nieaktualny. Zalecamy regenerację indeksu kanałów.\nKliknij przycisk `Przebuduj Indeks Kanałów` na stronie [Elasticsearch via the System Console]({{.ElasticsearchSection}}), aby naprawić ten błąd.\nZobacz logi Mattermost, aby uzyskać więcej informacji." - }, { "id": "bleveengine.purge_list.not_implemented", "translation": "Funkcja listy czyszczenia nie jest dostępna dla Bleve." diff --git a/server/i18n/pt-BR.json b/server/i18n/pt-BR.json index 6ac32bb27b9b..1fb516d4ba1d 100644 --- a/server/i18n/pt-BR.json +++ b/server/i18n/pt-BR.json @@ -9303,10 +9303,6 @@ "id": "app.acknowledgement.getforpost.get.app_error", "translation": "Não foi possível obter confirmação de recebimento da publicação." }, - { - "id": "app.channel.elasticsearch_channel_index.notify_admin.message", - "translation": "O esquema do índice de canais do Elasticsearch está desatualizado. É recomendável que você gere novamente o índice de canais.\nClique no botão `Reconstruir índices de Canais` na [seção Elasticsearch no System Console]({{.ElasticsearchSection}}) para corrigir o problema.\nConsulte o registro de alterações do Mattermost para obter mais informações." - }, { "id": "app.compile_report_chunks.unsupported_format", "translation": "Formato de relatório não suportado." diff --git a/server/i18n/ru.json b/server/i18n/ru.json index 3e6e88a3bd66..2ff97e3b1b44 100644 --- a/server/i18n/ru.json +++ b/server/i18n/ru.json @@ -9803,10 +9803,6 @@ "id": "api.user.auth_switch.not_available.login_disabled.app_error", "translation": "Передача аутентификации недоступна, так как не включен ни вход по электронной почте, ни вход по имени пользователя." }, - { - "id": "app.channel.elasticsearch_channel_index.notify_admin.message", - "translation": "Схема поискового индекса ваших каналов устарела. Мы рекомендуем регенерировать индекс ваших каналов.\nНажмите кнопку `Ребилд индексов каналов` на [странице Elasticsearch через Системную консоль]({{.ElasticsearchSection}}), чтобы исправить эту проблему.\nБолее подробную информацию смотри в журнале изменений Mattermost." - }, { "id": "api.post.check_for_out_of_team_mentions.message.multiple", "translation": "@{{.Usernames}} и @{{.LastUsername}} не получили уведомлений об этом упоминании, потому что они не являются участниками этой команды." diff --git a/server/i18n/sv.json b/server/i18n/sv.json index 38f6e37ac4af..42aaeb291e86 100644 --- a/server/i18n/sv.json +++ b/server/i18n/sv.json @@ -9733,10 +9733,6 @@ "id": "model.channel_bookmark.is_valid.update_at.app_error", "translation": "\"Update at\" måste vara en giltig tid." }, - { - "id": "app.channel.elasticsearch_channel_index.notify_admin.message", - "translation": "Ditt Elasticsearch-indexschema för kanaler är föråldrat. En omindexering av kanal-index rekommenderas.\nKlicka på knappen `Rebuild Channels Index` i [Elasticsearch-avsnittet i systemkonsolen] ({{.ElasticsearchSection}}) för att åtgärda problemet.\nSe Mattermost changelog för mer information." - }, { "id": "api.config.update.elasticsearch.autocomplete_cannot_be_enabled_error", "translation": "Autokomplettering av kanaler kan inte aktiveras eftersom indexschemat för kanaler är föråldrat. Vi rekommenderar att du omindexerar ditt kanalindex. Se Mattermost changelog för mer information" diff --git a/server/i18n/tr.json b/server/i18n/tr.json index cfe50a5e1b37..609ad67218aa 100644 --- a/server/i18n/tr.json +++ b/server/i18n/tr.json @@ -9705,10 +9705,6 @@ "id": "api.templates.ip_filters_changed_footer.title", "translation": "Çalışma alanınıza erişmekte sorun mu yaşıyorsunuz?" }, - { - "id": "app.channel.elasticsearch_channel_index.notify_admin.message", - "translation": "Arama kanal dizini şemanız güncel değil. Kanal dizininizi yeniden oluşturmanız önerilir.\nSorunu çözmek için [Sistem panosundaki Elasticsearch bölümünden] ({{.ElasticsearchSection}}) `Kanal dizinini yeniden oluştur` düğmesine tıklayın.\nAyrıntılı bilgi almak için Mattermost değişiklik günlüğüne bakın." - }, { "id": "app.user.update_active.user_limit.exceeded", "translation": "Kullanıcı etkinleştirilemedi. Sunucu güvenli kullanıcı sayısı sınırını aşıyor. Yöneticiniz ile görüşün: ERROR_SAFETY_LIMITS_EXCEEDED." @@ -10398,5 +10394,9 @@ { "id": "model.property_value.is_valid.app_error", "translation": "Özellik değeri geçersiz: {{.FieldName}} ({{.Reason}})." + }, + { + "id": "api.command.execute_command.deleted.error", + "translation": "Silinmiş bir kanalda komut yürütülemez." } ] diff --git a/server/i18n/uk.json b/server/i18n/uk.json index d89a76b14e8c..84a55b303060 100644 --- a/server/i18n/uk.json +++ b/server/i18n/uk.json @@ -8279,10 +8279,6 @@ "id": "app.channel.autofollow.app_error", "translation": "Не вдалося оновити участь у обговореннях для згаданого користувача" }, - { - "id": "app.channel.elasticsearch_channel_index.notify_admin.message", - "translation": "Схема індексу каналу пошуку застаріла. Ми рекомендуємо регенерувати індекс вашого каналу.\nНатисніть кнопку `Перебудувати індекс каналів` на [сторінці Elasticsearch через Системну консоль]({{.ElasticsearchSection}}), щоб вирішити цю проблему.\nДетальнішу інформацію ви можете переглянути у журналі змін Mattermost." - }, { "id": "api.user.create_user.user_limits.exceeded", "translation": "Неможливо створити користувача. Сервер перевищує ліміт безпечних користувачів. Зверніться до свого адміністратора з повідомленням: ERROR_SAFETY_LIMITS_EXCEEDED." @@ -10342,5 +10338,17 @@ { "id": "app.custom_profile_attributes.limit_reached.app_error", "translation": "Досягнуто ліміт поля \"Атрибути профілю користувача\"" + }, + { + "id": "api.command.execute_command.deleted.error", + "translation": "Неможливо виконати команду у видаленому каналі." + }, + { + "id": "app.custom_profile_attributes.property_field_not_found.app_error", + "translation": "Користувацьке поле атрибуту профілю не знайдено" + }, + { + "id": "app.custom_profile_attributes.property_field_update.app_error", + "translation": "Не вдається оновити поле атрибуту користувацького профілю" } ] diff --git a/server/i18n/zh-CN.json b/server/i18n/zh-CN.json index 4b1e9b84b2d1..934d8b946298 100644 --- a/server/i18n/zh-CN.json +++ b/server/i18n/zh-CN.json @@ -9709,10 +9709,6 @@ "id": "app.channel.bookmark.update_sort.app_error", "translation": "无法排序书签。" }, - { - "id": "app.channel.elasticsearch_channel_index.notify_admin.message", - "translation": "您的 Elasticsearch 频道索引表已过期。推荐您重新生成频道索引。\n在[系统控制台中的 Elasticsearch 部分]({{.ElasticsearchSection}})点击`重建频道索引`以解决问题。\n查看 Mattermost 更新日志以了解更多信息。" - }, { "id": "ent.elasticsearch.purge_index.delete_failed", "translation": "删除一条 Elasticsearch 索引失败" @@ -10380,5 +10376,17 @@ { "id": "ent.message_export.job_data_conversion.app_error", "translation": "无法转换任务数据字段中的值。" + }, + { + "id": "api.command.execute_command.deleted.error", + "translation": "不能在已删除的频道中执行命令。" + }, + { + "id": "api.custom_profile_attributes.license_error", + "translation": "您的授权不支持自定义个人资料属性。" + }, + { + "id": "api.file.zip_file_reader.app_error", + "translation": "无法获取 zip 文件读取器。" } ] diff --git a/server/i18n/zh-TW.json b/server/i18n/zh-TW.json index b46891fd3f83..f17c4d56018b 100644 --- a/server/i18n/zh-TW.json +++ b/server/i18n/zh-TW.json @@ -9683,10 +9683,6 @@ "id": "api.user.create_user.user_limits.exceeded", "translation": "無法建立使用者。使用者數量超過伺服器安全限制。請聯絡您的管理員,並附上錯誤代碼:ERROR_SAFETY_LIMITS_EXCEEDED。" }, - { - "id": "app.channel.elasticsearch_channel_index.notify_admin.message", - "translation": "您的 Elasticsearch 頻道索引架構已過期,建議重新生成您的頻道索引。\n請點ㄧ下 [系統管理後台的 Elasticsearch]{{.ElasticsearchSection}} 中的「重建頻道索引」按鈕來修復此問題。 \n有關更多資訊,請參閱 Mattermost 變更記錄。" - }, { "id": "ent.elasticsearch.purge_index.delete_failed", "translation": "刪除一條 Elasticsearch 索引失敗" diff --git a/webapp/channels/src/i18n/be.json b/webapp/channels/src/i18n/be.json index 8db2bc1f8c8e..354c53b5fc9a 100644 --- a/webapp/channels/src/i18n/be.json +++ b/webapp/channels/src/i18n/be.json @@ -2251,13 +2251,10 @@ "analytics.system.privateGroups": "Прыватныя каналы", "analytics.system.publicChannels": "Публічныя каналы", "analytics.system.skippedIntensiveQueries": "Для забеспячэння максімальнай прадукцыйнасці некаторыя статыстычныя даныя адключаны. Вы можаце [паўторна ўключыць іх у config.json](!https://docs.mattermost.com/administration/statistics.html).", - "analytics.system.textPosts": "Толькі тэкставыя паведамленні", "analytics.system.title": "Статыстыка сістэмы", "analytics.system.totalBotPosts": "Усяго паведамленняў ад ботаў", "analytics.system.totalChannels": "Усяго каналаў", "analytics.system.totalCommands": "Усяго каманд", - "analytics.system.totalFilePosts": "Паведамленні з файламі", - "analytics.system.totalHashtagPosts": "Паведамленні з хэштэгамі", "analytics.system.totalIncomingWebhooks": "Уваходныя вэбхукі", "analytics.system.totalMasterDbConnections": "Падлучэнняў да галоўнай БД", "analytics.system.totalOutgoingWebhooks": "Выходныя вэбхукі", diff --git a/webapp/channels/src/i18n/bg.json b/webapp/channels/src/i18n/bg.json index b7197dfa2332..0d3e054ee50f 100644 --- a/webapp/channels/src/i18n/bg.json +++ b/webapp/channels/src/i18n/bg.json @@ -2833,13 +2833,10 @@ "analytics.system.privateGroups": "Частни канали", "analytics.system.publicChannels": "Публични канали", "analytics.system.skippedIntensiveQueries": "За да се увеличи ефективността, някои статистически данни са деактивирани. Можете да ги активирате отново в config.json.", - "analytics.system.textPosts": "Публикации само с текст", "analytics.system.title": "Статистика за системата", "analytics.system.totalBotPosts": "Общо публикации на ботове", "analytics.system.totalChannels": "Общо канали", "analytics.system.totalCommands": "Общо команди", - "analytics.system.totalFilePosts": "Публикации с файлове", - "analytics.system.totalHashtagPosts": "Публикации с хаштагове", "analytics.system.totalIncomingWebhooks": "Входящи webhook", "analytics.system.totalMasterDbConnections": "Връзки с главна БД", "analytics.system.totalOutgoingWebhooks": "Изходящи webhook", diff --git a/webapp/channels/src/i18n/cs.json b/webapp/channels/src/i18n/cs.json index 923fe21fe65b..935bde80f2a5 100644 --- a/webapp/channels/src/i18n/cs.json +++ b/webapp/channels/src/i18n/cs.json @@ -246,6 +246,7 @@ "add_teams_to_scheme.confirmation.message": "Tento tým je již vybrán v jiném týmovém schématu, jste si jisti, že ho chcete přesunout do tohoto týmového schématu?", "add_teams_to_scheme.confirmation.title": "Změna schématu přepsání pro tým?", "add_teams_to_scheme.modalTitle": "Přidat týmy do seznamu pro výběr týmu", + "add_teams_to_scheme.select_team.label": "Zvolte tým {label}", "add_user_to_channel_modal.add": "Přidat", "add_user_to_channel_modal.cancel": "Zrušit", "add_user_to_channel_modal.help": "Zadejte pro vyhledání kanálu. Použijte ↑↓ pro procházení, ↵ pro výběr, ESC pro zavření.", @@ -580,17 +581,17 @@ "admin.complianceExport.createJob.title": "Spustit Export souladu s pravidly", "admin.complianceExport.exportFormat.actiance": "Actiance XML", "admin.complianceExport.exportFormat.csv": "CSV", - "admin.complianceExport.exportFormat.globalrelay": "EML globálního přenositelného zákaznického účtu", + "admin.complianceExport.exportFormat.globalrelay": "Global Relay EML", "admin.complianceExport.exportFormat.title": "Exportní formát:", "admin.complianceExport.exportFormatDetail.details": "Pro Actiance XML jsou soubory exportu pro dodržování předpisů ukládány do podadresáře exports v nastaveném Místním úložišti. Pro Global Relay EML jsou soubory zasílány na nastavenou e-mailovou adresu.", "admin.complianceExport.exportFormatDetail.intro": "„Formát exportu pro dodržování předpisů. Odpovídá systému, do kterého chcete data importovat.", "admin.complianceExport.exportJobStartTime.description": "Nastavte počáteční čas pravidelného denního exportu. Zvolte dobu, kdy bude váš systém používat nejméně lidí. Formát musí být 24 hodinový ve tvaru HH: MM.", "admin.complianceExport.exportJobStartTime.example": "Např.: \"02:00\"", "admin.complianceExport.exportJobStartTime.title": "Čas exportu zpráv o shodě:", - "admin.complianceExport.globalRelayCustomSMTPPort.description": "Port SMTP serveru, který obdrží vaše EML globálního přenositelného zákaznického účtu.", + "admin.complianceExport.globalRelayCustomSMTPPort.description": "SMTP serverový port, který přijme váš Global Relay EML.", "admin.complianceExport.globalRelayCustomSMTPPort.example": "Např.: \"25\"", "admin.complianceExport.globalRelayCustomSMTPPort.title": "Port SMTP serveru:", - "admin.complianceExport.globalRelayCustomSMTPServerName.description": "Název SMTP serveru, který obdrží vaše EML globálního přenositelného zákaznického účtu.", + "admin.complianceExport.globalRelayCustomSMTPServerName.description": "Název SMTP serveru, který přijme váš Global Relay EML.", "admin.complianceExport.globalRelayCustomSMTPServerName.example": "Např.: \"feeds.globalrelay.com\"", "admin.complianceExport.globalRelayCustomSMTPServerName.title": "Název SMTP serveru:", "admin.complianceExport.globalRelayCustomerType.a10.description": "A10/Typ 10", @@ -598,7 +599,7 @@ "admin.complianceExport.globalRelayCustomerType.custom.description": "Vlastní", "admin.complianceExport.globalRelayCustomerType.description": "Typ globálního přenositelného zákaznického účtu, který má vaše organizace k dispozici.", "admin.complianceExport.globalRelayCustomerType.title": "Globálně přenositelný zákaznický účet:", - "admin.complianceExport.globalRelayEmailAddress.description": "E-mailová adresa, kterou váš globální přenosový server sleduje pro příchozí exporty souladu.", + "admin.complianceExport.globalRelayEmailAddress.description": "E-mailová adresa, kterou váš server Global Relay sleduje pro příchozí exporty souladu.", "admin.complianceExport.globalRelayEmailAddress.example": "Např.: \"globalrelay@mattermost.com\"", "admin.complianceExport.globalRelayEmailAddress.title": "Email globálně přenositelného zákaznického účtu:", "admin.complianceExport.globalRelaySMTPPassword.description": "Heslo, které se používá k ověření proti serveru SMTP GlobalRelay.", @@ -609,8 +610,8 @@ "admin.complianceExport.globalRelaySMTPUsername.title": "Uživatelské jméno SMTP:", "admin.complianceExport.messagesExportedCount": "{count} zpráv vyexportováno.", "admin.complianceExport.title": "Export zprávy o shodě", - "admin.complianceExport.warningCount": "{count} narazil(y) na varování, podrobnosti v souboru warning.txt", - "admin.complianceExport.warningCount.globalrelay": "{count} narazil(y) na varování, podrobnosti v logu", + "admin.complianceExport.warningCount": "{count} varování zjištěno, podrobnosti naleznete v souboru warning.txt", + "admin.complianceExport.warningCount.globalrelay": "{count} varování zjištěno, podrobnosti naleznete v protokolu", "admin.complianceMonitoring.userActivityLogsTitle": "Záznamy aktivit uživatelů", "admin.compliance_export_feature_discovery.copy": "Spouštějte každodenní zprávy o compliance a exportujte je do různých formátů, které mohou být použity nástroji pro integraci třetích stran, jako je Smarsh (Actiance).", "admin.compliance_export_feature_discovery.title": "Provádění exportů v souladu s předpisy pomocí Mattermost Enterprise", @@ -640,9 +641,9 @@ "admin.compliance_table.type": "Typ", "admin.compliance_table.userId": "Požadováno kým", "admin.connectionSecurityNone": "Žádný", - "admin.connectionSecurityNoneDescription": "Mattermost se bude připojovat přes nezabezpečené připojení.", + "admin.connectionSecurityNoneDescription": "Mattermost se připojí přes nezabezpečené připojení.", "admin.connectionSecurityStart": "STARTTLS", - "admin.connectionSecurityStartDescription": "Vezme stávající nezabezpečené připojení a pokusí se ho povýšit na zabezpečené připojení pomocí TLS.", + "admin.connectionSecurityStartDescription": "Převede stávající nezabezpečené připojení a pokusí se jej upgradovat na zabezpečené připojení pomocí TLS.", "admin.connectionSecurityTitle": "Zabezpečení připojení:", "admin.connectionSecurityTls": "TLS", "admin.connectionSecurityTlsDescription": "Šifruje komunikaci mezi Mattermost a vaším serverem.", @@ -652,8 +653,8 @@ "admin.customization.allowSyncedDraftsDesc": "Pokud je tato funkce povolena, budou se návrhy zpráv uživatelů synchronizovat se serverem, aby k nim bylo možné přistupovat z jakéhokoli zařízení. Uživatelé mohou toto chování zrušit v nastavení účtu.", "admin.customization.androidAppDownloadLinkDesc": "Přidání odkazu ke stažení aplikace pro Android. Uživatelům, kteří přistupují k webu v mobilním webovém prohlížeči, bude zobrazena stránka, která jim umožní stáhnout aplikaci. Ponechte toto pole prázdné, abyste zabránili zobrazování stránky.", "admin.customization.androidAppDownloadLinkTitle": "Odkaz na Android aplikaci:", - "admin.customization.announcement.allowBannerDismissalDesc": "Pokud je povoleno, mohou uživatelé odmítnout banner až do příští aktualizace. Pokud je zakázáno, banner je trvale viditelný, dokud ho systémový administrátor nevypne.", - "admin.customization.announcement.allowBannerDismissalTitle": "Povolit schování banneru:", + "admin.customization.announcement.allowBannerDismissalDesc": "Pokud je nastavena hodnota „true“, uživatelé mohou banner zavřít až do jeho další aktualizace. Pokud je nastavena hodnota „false“, banner zůstane trvale viditelný, dokud jej nevypne správce systému.", + "admin.customization.announcement.allowBannerDismissalTitle": "Povolit zavření banneru:", "admin.customization.announcement.bannerColorTitle": "Barva banneru:", "admin.customization.announcement.bannerTextColorTitle": "Barva textu banneru:", "admin.customization.announcement.bannerTextDesc": "Text, který se objeví v oznamovacím banneru.", @@ -663,20 +664,20 @@ "admin.customization.appDownloadLinkDesc": "Přidat odkaz na stránku stažení Mattermost aplikací. Pokud odkaz existuje, možnost \"Stáhnout Mattermost aplikace\" bude přidána do nabídky produktů tak, že uživatelé budou moci nalézt stránku stažení. Nechte toto pole prázdné, aby možnost byla schována z nabídky produktů.", "admin.customization.appDownloadLinkTitle": "Odkaz na stránku s aplikacemi Mattermost:", "admin.customization.customUrlSchemes": "Vlastní schéma URL adresy:", - "admin.customization.customUrlSchemesDesc": "Umožňuje z textu udělat odkaz, pokud začíná některým z uvedených schémat URL oddělených čárkou. Ve výchozím nastavení budou odkazy vytvářeny na následující schémata: \"http\", \"https\", \"ftp\", \"tel\" a \"mailto\".", + "admin.customization.customUrlSchemesDesc": "Povolí textu zprávy vytvořit odkaz, pokud začíná některým z uvedených URL schémat oddělených čárkami. Ve výchozím nastavení budou následující schémata vytvářet odkazy: \"http\", \"https\", \"ftp\", \"tel\" a \"mailto\".", "admin.customization.customUrlSchemesPlaceholder": "Např.: \"git,smtp\"", - "admin.customization.enableCustomEmojiDesc": "Umožnit uživatelům vytvářet vlastní emodži pro použití ve zprávách. Pokud je tato funkce povolena, lze k nastavení vlastních emodži přistupovat v kanálech prostřednictvím nástroje pro výběr emodži.", + "admin.customization.enableCustomEmojiDesc": "Povolit uživatelům vytvářet vlastní emoji pro použití v zprávách. Po povolení budou nastavení vlastních emoji přístupná v kanálech prostřednictvím výběru emoji.", "admin.customization.enableCustomEmojiTitle": "Povolit vlastní emoji:", - "admin.customization.enableDesktopLandingPageDesc": "Jestli chcete nebo nechcete nabízet uživateli použití Desktopové aplikace při prvním užití Mattermostu.", + "admin.customization.enableDesktopLandingPageDesc": "Zda vyzvat uživatele k použití desktopové aplikace, když poprvé použije Mattermost.", "admin.customization.enableDesktopLandingPageTitle": "Povolit úvodní stránku desktopové aplikace:", "admin.customization.enableEmojiPickerDesc": "Výběr emodži umožňuje uživatelům vybrat emodži, které chtějí přidat jako reakci nebo použít ve zprávách. Povolení výběru emodži s velkým počtem vlastních emodži může zpomalit výkon.", "admin.customization.enableEmojiPickerTitle": "Povolit nabídku emotikonů:", "admin.customization.enableGifPickerDesc": "Povolit uživatelům volit GIFy z emodži pickeru.", "admin.customization.enableGifPickerTitle": "Povolit nabídku GIFů:", "admin.customization.enableInlineLatexDesc": "Povolení vykreslování inline kódu Latexu. Pokud je hodnota false, lze Latex vykreslovat pouze v bloku kódu pomocí zvýraznění syntaxe. Podrobnosti o formátování textu naleznete v naší dokumentaci k webu .", - "admin.customization.enableInlineLatexTitle": "Povolte vykreslování Latex inline:", + "admin.customization.enableInlineLatexTitle": "Povolit zobrazení inline LaTeXu:", "admin.customization.enableLatexDesc": "Povolení vykreslování Latexu v blocích kódu. Pokud je false, bude kód Latex pouze zvýrazněn.\n\nPovolení vykreslování Latexu není doporučeno v prostředích, kde nejsou všichni uživatelé věrohodní.", - "admin.customization.enableLatexTitle": "Povolit vykreslování Latex:", + "admin.customization.enableLatexTitle": "Povolit zobrazení LaTeXu:", "admin.customization.enableLinkPreviewsDesc": "Zobrazení náhledu obsahu webové stránky, odkazů na obrázky a odkazů na YouTube pod zprávou, pokud jsou k dispozici. Server musí být připojen k internetu a mít přístup přes bránu firewall (pokud je to možné) k webovým stránkám, od kterých se očekávají náhledy. Uživatelé mohou tyto náhledy zakázat v nabídce Nastavení > Zobrazení > Náhledy odkazů na webové stránky.", "admin.customization.enableLinkPreviewsTitle": "Povolení náhledů odkazů na webové stránky:", "admin.customization.enablePermalinkPreviewsDesc": "Pokud je tato funkce povolena, odkazy na zprávy Mattermostu vygenerují náhled pro všechny uživatele, kteří mají přístup k původní zprávě. Podrobnosti naleznete v naší dokumentaci na adrese .", @@ -685,47 +686,47 @@ "admin.customization.enableSVGsTitle": "Povolte SVG:", "admin.customization.iosAppDownloadLinkDesc": "Přidání odkazu ke stažení iOS aplikace. Uživatelé, kteří navštíví stránku v mobilním prohlížeči budou odkázáni na stránku, která jim umožní stáhnout aplikaci. Ponechte toto pole prázdné, abyste zabránili zobrazování stránky.", "admin.customization.iosAppDownloadLinkTitle": "Odkaz ke stažení iOS aplikace:", - "admin.customization.maxMarkdownNodesDesc": "Při renderování Markdown textu v mobilní aplikaci, kontrolovat maximální počet Markdown elementů (např. emodži, odkazy, tabulky, apod.) které mohou být v jednom kusu textu. Pokud je nastaveno 0, nebude limit použit.", - "admin.customization.maxMarkdownNodesTitle": "Maximální počet Markdown nodů:", + "admin.customization.maxMarkdownNodesDesc": "Při renderování textu v Markdown formátu v mobilní aplikaci určuje maximální počet prvků Markdownu (např. emoji, odkazy, buňky tabulek atd.), které mohou být v jednom textu. Pokud je nastaveno na 0, použije se výchozí limit.", + "admin.customization.maxMarkdownNodesTitle": "Maximální počet uzlů Markdownu:", "admin.customization.restrictLinkPreviewsDesc": "Pro výše uvedený seznam domén oddělených čárkou se nezobrazí náhledy odkazů a náhledy obrázků.", "admin.customization.restrictLinkPreviewsExample": "Např.: \"internal.mycompany.com, images.example.com\"", "admin.customization.restrictLinkPreviewsTitle": "Zakázat náhledy odkazů na webové stránky z těchto domén:", - "admin.customization.uniqueEmojiReactionLimitPerPost": "Počet reakčních unikátních emodži:", - "admin.customization.uniqueEmojiReactionLimitPerPost.maxValue": "Hodnotu nelze zvýšit nad 500.", - "admin.customization.uniqueEmojiReactionLimitPerPost.minValue": "Hodnotu nelze nastavit níž, než 0.", - "admin.customization.uniqueEmojiReactionLimitPerPostDesc": "Počet jedinečných reakcí emodži, které lze přidat k příspěvku. Zvýšení tohoto limitu by mohlo vést ke špatnému výkonu klienta. Maximum je 500.", + "admin.customization.uniqueEmojiReactionLimitPerPost": "Limit pro jedinečné reakce emoji:", + "admin.customization.uniqueEmojiReactionLimitPerPost.maxValue": "Nelze zvýšit limit na hodnotu vyšší než 500.", + "admin.customization.uniqueEmojiReactionLimitPerPost.minValue": "Nelze snížit limit pod 0.", + "admin.customization.uniqueEmojiReactionLimitPerPostDesc": "Počet unikátních emoji reakcí, které mohou být přidány k příspěvku. Zvýšení tohoto limitu může vést k horší výkonnosti klienta. Maximální hodnota je 500.", "admin.customization.uniqueEmojiReactionLimitPerPostPlaceholder": "Např.: 25", "admin.data_grid.empty": "Nebyly nalezeny žádné položky", "admin.data_grid.loading": "Nahrávám", "admin.data_grid.paginatorCount": "{startCount, number} - {endCount, number} z {total, number}", "admin.data_retention.channel_team_counts": "{team_count} {team_count, plural, one {tým} other {týmy}}, {channel_count} {channel_count, plural, one {kanál} other {kanály}}", - "admin.data_retention.channel_team_counts_empty": "NEUPLATŇUJE SE", - "admin.data_retention.createJob.instructions": "Denní čas pro kontrolu zásad a spuštění úlohy mazání:", - "admin.data_retention.createJob.title": "Spustit úlohu odstranění", + "admin.data_retention.channel_team_counts_empty": "N/A", + "admin.data_retention.createJob.instructions": "Denní čas pro kontrolu zásad a spuštění úkolu pro smazání:", + "admin.data_retention.createJob.title": "Spustit úkol pro smazání nyní", "admin.data_retention.customPolicies.addPolicy": "Přidat zásady", "admin.data_retention.customPolicies.subTitle": "Přizpůsobte si, jak dlouho budou konkrétní týmy a kanály uchovávat zprávy.", - "admin.data_retention.customPolicies.title": "Vlastní zásady uchovávání dat", - "admin.data_retention.customPoliciesTable.appliedTo": "Aplikováno na", + "admin.data_retention.customPolicies.title": "Vlastní zásady uchovávání", + "admin.data_retention.customPoliciesTable.appliedTo": "Použito na", "admin.data_retention.customPoliciesTable.channelMessages": "Zprávy kanálu", "admin.data_retention.customPoliciesTable.description": "Popis", - "admin.data_retention.customTitle": "Vlastní zásady uchovávání dat", + "admin.data_retention.customTitle": "Vlastní Zásady Uchovávání", "admin.data_retention.custom_policy.cancel": "Zrušit", - "admin.data_retention.custom_policy.channel_selector.addChannels": "Přidání kanálů", - "admin.data_retention.custom_policy.channel_selector.subTitle": "Přidejte kanály, které se budou řídit touto zásadou uchovávání.", + "admin.data_retention.custom_policy.channel_selector.addChannels": "Přidat kanály", + "admin.data_retention.custom_policy.channel_selector.subTitle": "Přidat kanály, které budou následovat tuto politiku uchovávání dat.", "admin.data_retention.custom_policy.channel_selector.title": "Přiřazené kanály", - "admin.data_retention.custom_policy.form.durationInput.error": "Chybné zpracování zprávy o uchování.", + "admin.data_retention.custom_policy.form.durationInput.error": "Chyba při analýze uchovávání zpráv.", "admin.data_retention.custom_policy.form.input": "Název politiky", - "admin.data_retention.custom_policy.form.input.error": "Název zásady nemůže být prázdný.", - "admin.data_retention.custom_policy.form.subTitle": "Pojmenujte zásady a nakonfigurujte nastavení uchovávání.", - "admin.data_retention.custom_policy.form.teamsError": "Do zásady je třeba přidat tým nebo kanál.", + "admin.data_retention.custom_policy.form.input.error": "Název politiky nemůže být prázdný.", + "admin.data_retention.custom_policy.form.subTitle": "Dejte své politice název a nastavte pravidla uchovávání dat.", + "admin.data_retention.custom_policy.form.teamsError": "Musíte přidat tým nebo kanál do této politiky.", "admin.data_retention.custom_policy.form.title": "Jméno a uchovávání", "admin.data_retention.custom_policy.save": "Uložit", "admin.data_retention.custom_policy.serverError": "Ve formuláři výše se vyskytly chyby", "admin.data_retention.custom_policy.team_selector.addTeams": "Přidat týmy", "admin.data_retention.custom_policy.team_selector.subTitle": "Přidejte kanály, které se budou řídit touto zásadou uchovávání.", - "admin.data_retention.custom_policy.team_selector.title": "Přidělené týmy", + "admin.data_retention.custom_policy.team_selector.title": "Přiřazené týmy", "admin.data_retention.custom_policy.teams.remove": "Odstranit", - "admin.data_retention.form.channelAndDirectMessageRetention": "Udržování kanálů a přímých zpráv", + "admin.data_retention.form.channelAndDirectMessageRetention": "Retence kanálů a přímých zpráv", "admin.data_retention.form.days": "Dny", "admin.data_retention.form.fileRetention": "Uchovávání souborů", "admin.data_retention.form.hours": "Hodiny", @@ -750,7 +751,7 @@ "admin.data_retention.retention_years": "{count} {count, plural, one {rok} other {roky}}", "admin.data_retention.settings.title": "Zásady uchovávání údajů", "admin.data_retention.title": "Zásady uchovávání údajů", - "admin.data_retention_feature_discovery.copy": "Data uchovávejte jen tak dlouho, jak potřebujete. Vytvořte úlohy pro uchovávání dat pro vybrané kanály a týmy, abyste automaticky odstranili jednorázová data.", + "admin.data_retention_feature_discovery.copy": "Uchovávejte svá data pouze tak dlouho, jak je potřebujete. Vytvořte úkoly pro uchovávání dat pro vybrané kanály a týmy, které automaticky odstraní nepotřebná data.", "admin.data_retention_feature_discovery.title": "Vytváření plánů uchovávání dat pomocí Mattermost Enterprise", "admin.database.migrations_table.help_text": "Seznam migrací schémat použitých na váš datový sklad.", "admin.database.migrations_table.name": "Jméno", @@ -1494,6 +1495,7 @@ "admin.logs.showErrors": "Zobrazit posledních {n} chyb", "admin.logs.title": "Serverové záznamy", "admin.manage_roles.additionalRoles": "Vyberte další oprávnění pro účet. Dozvědět se více o rolích a oprávněních.", + "admin.manage_roles.additionalRoles_warning": "Poznámka: Výše udělená oprávnění platí pro celý účet, bez ohledu na to, zda je ověřen pomocí relace cookie nebo osobního přístupového tokenu. Například výběr post:all umožní účtu přispívat do kanálů, jejichž není členem, i bez použití osobního přístupového tokenu.", "admin.manage_roles.allowUserAccessTokens": "Povolit tomuto účtu generovat osobní přístupové tokeny.", "admin.manage_roles.allowUserAccessTokensDesc": "Zrušení tohoto oprávnění neodstraní existující tokeny. K jejich odstranění musíte přejít na stránku Správa tokenů uživatele.", "admin.manage_roles.botAdditionalRoles": "Vyberte další oprávnění pro účet. Dozvědět se více o rolích a oprávněních.", @@ -1594,8 +1596,10 @@ "admin.password.preview": "Náhled chybové zprávy", "admin.password.symbol": "Alespoň jeden symbol (např. \"~!@#$%^&*()\")", "admin.password.uppercase": "Alespoň jedno velké písmeno", + "admin.permissions.group.convert_private_channel_to_public.description": "Převést soukromé kanály na veřejné", + "admin.permissions.group.convert_private_channel_to_public.name": "Převést na veřejné", "admin.permissions.group.convert_public_channel_to_private.description": "Převést veřejné kanály na privátní", - "admin.permissions.group.convert_public_channel_to_private.name": "Převést kanály", + "admin.permissions.group.convert_public_channel_to_private.name": "Převést na soukromé", "admin.permissions.group.custom_groups.description": "Vytvoření, úprava, odstranění a správa členů vlastních skupin.", "admin.permissions.group.custom_groups.name": "Vlastní skupiny", "admin.permissions.group.delete_posts.description": "Smazat příspěvky vlastní a ostatních.", @@ -1655,9 +1659,9 @@ "admin.permissions.permission.assign_system_admin_role.description": "Přiřadit roli správce systému", "admin.permissions.permission.assign_system_admin_role.name": "Přiřadit roli správce systému", "admin.permissions.permission.convert_private_channel_to_public.description": "Převést soukromé kanály na veřejné", - "admin.permissions.permission.convert_private_channel_to_public.name": "Převést kanály", + "admin.permissions.permission.convert_private_channel_to_public.name": "Převést na veřejné", "admin.permissions.permission.convert_public_channel_to_private.description": "Převést veřejné kanály na soukromé", - "admin.permissions.permission.convert_public_channel_to_private.name": "Převést kanály", + "admin.permissions.permission.convert_public_channel_to_private.name": "Převést na soukromé", "admin.permissions.permission.create_custom_group.description": "Vytvořit vlastní skupiny.", "admin.permissions.permission.create_custom_group.name": "Vytvořit", "admin.permissions.permission.create_direct_channel.description": "Vytvořit přímou zprávu", @@ -1883,7 +1887,8 @@ "admin.permissions.sysconsole_section_user_management_channels.name": "Kanály", "admin.permissions.sysconsole_section_user_management_groups.name": "Skupiny", "admin.permissions.sysconsole_section_user_management_permissions.name": "Oprávnění", - "admin.permissions.sysconsole_section_user_management_system_roles.name": "Systémové role", + "admin.permissions.sysconsole_section_user_management_system_roles.description": "Nastavení „Žádný přístup“ omezuje pouze rozhraní Systémové konzole. Základní API koncové body zůstávají přístupné všem uživatelům v režimu pouze pro čtení pro základní funkčnost produktu.", + "admin.permissions.sysconsole_section_user_management_system_roles.name": "Delegovaná granulární správa", "admin.permissions.sysconsole_section_user_management_teams.name": "Týmy", "admin.permissions.sysconsole_section_user_management_users.description": "Nemohu resetovat hesla adminům", "admin.permissions.sysconsole_section_user_management_users.name": "Uživatelé", @@ -2486,6 +2491,7 @@ "admin.sidebar.smtp": "SMTP", "admin.sidebar.subscription": "Předplatné", "admin.sidebar.systemRoles": "Delegovaná podrobná administrace", + "admin.sidebar.system_properties": "Systémové vlastnosti", "admin.sidebar.teamStatistics": "Statistiky týmu", "admin.sidebar.teams": "Týmy", "admin.sidebar.userManagement": "Správa uživatelů", @@ -2562,6 +2568,23 @@ "admin.systemUserDetail.teamList.teamType.groupSync": "Synchronizace skupiny", "admin.systemUserDetail.teamList.teamType.inviteOnly": "Pouze na pozvání", "admin.systemUserDetail.title": "Nastavení uživatele", + "admin.system_properties.confirm.delete.button": "Smazat", + "admin.system_properties.confirm.delete.text": "Smazání této vlastnosti odstraní všechny hodnoty definované uživatelem, které jsou s ní spojeny.", + "admin.system_properties.confirm.delete.title": "Smazat vlastnost {name}", + "admin.system_properties.details.saving_changes": "Ukládám nastavení…", + "admin.system_properties.details.saving_changes_error": "Při ukládání nastavení se vyskytla chyba", + "admin.system_properties.user_properties.add_property": "Přidat vlastnost", + "admin.system_properties.user_properties.subtitle": "Přizpůsobte vlastnosti, které se zobrazí v uživatelských profilech", + "admin.system_properties.user_properties.table.actions": "Úkony", + "admin.system_properties.user_properties.table.actions.delete": "Smazat", + "admin.system_properties.user_properties.table.property": "Vlastnost", + "admin.system_properties.user_properties.table.property_name.input.name": "Název vlastnosti", + "admin.system_properties.user_properties.table.type": "Typ", + "admin.system_properties.user_properties.table.type.text": "Text", + "admin.system_properties.user_properties.table.validation.name_required": "Prosím, zadejte název vlastnosti.", + "admin.system_properties.user_properties.table.validation.name_taken": "Název vlastnosti již existuje.", + "admin.system_properties.user_properties.table.validation.name_unique": "Název vlastnosti musí být jedinečný.", + "admin.system_properties.user_properties.title": "Vlastnosti uživatele", "admin.system_roles_feature_discovery.copy": "Přiřaďte přizpůsobitelné administrátorské role, abyste vybraným uživatelům poskytli přístup pro čtení/ zápis k vybraným sekcím systémové konzole.", "admin.system_roles_feature_discovery.title": "Poskytněte kontrolovaný přístup do konzoly systému s Mattermost Enterprise", "admin.system_users.column_toggler.dropdownAriaLabel": "Nabídka viditelnosti sloupců", @@ -2885,13 +2908,10 @@ "analytics.system.publicChannels": "Veřejné kanály", "analytics.system.seatsPurchased": "Licencovaná místa", "analytics.system.skippedIntensiveQueries": "Pro maximalizaci výkonu jsou některé statistiky vypnuty. Můžete znovu povolit v konfiguračním souboru config.json.", - "analytics.system.textPosts": "Pouze textové příspěvky", "analytics.system.title": "Statistiky systemu", "analytics.system.totalBotPosts": "Množstvý zpráv od botů", "analytics.system.totalChannels": "Celkový počet kanálů", "analytics.system.totalCommands": "Celkový počet příkazů", - "analytics.system.totalFilePosts": "Příspěvky se soubory", - "analytics.system.totalHashtagPosts": "Zprávy s hashtagamy", "analytics.system.totalIncomingWebhooks": "Příchozí webhooky", "analytics.system.totalMasterDbConnections": "Celkový počet připojení k hlavní databázi", "analytics.system.totalOutgoingWebhooks": "Odchozí webové háčky", @@ -3211,9 +3231,11 @@ "channel_header.closeChannelInfo": "Skrýt informace", "channel_header.convert": "Převést na soukromý kanál", "channel_header.delete": "Archivovat kanál", + "channel_header.directchannel": "{displayName} (vy) Menu kanálu", "channel_header.directchannel.you": "{displayname} (já) ", "channel_header.flagged": "Uložené zprávy", "channel_header.groupMessageHasGuests": "Tato skupinová zpráva má návštěvníky", + "channel_header.headerText.addNewButton": "Přidat záhlaví kanálu", "channel_header.lastActive": "Aktivní {timestamp}", "channel_header.lastOnline": "Naposledy online {timestamp}", "channel_header.leave": "Opustit kanál", @@ -3222,6 +3244,7 @@ "channel_header.mute": "Ztlumit kanál", "channel_header.muteConversation": "Ztlumit konverzaci", "channel_header.openChannelInfo": "Zobrazit info", + "channel_header.otherchannel": "{displayName} Menu kanálu", "channel_header.pinnedPosts": "Připnuté zprávy", "channel_header.recentMentions": "Nedávné zmínky", "channel_header.rename": "Přejmenovat kanál", @@ -3259,6 +3282,7 @@ "channel_info_rhs.menu.members": "Členové", "channel_info_rhs.menu.notification_preferences": "Předvolby oznámení", "channel_info_rhs.menu.pinned": "Připnuté zprávy", + "channel_info_rhs.menu.title": "Akce informací o kanálu", "channel_info_rhs.top_buttons.add_people": "Přidat lidi", "channel_info_rhs.top_buttons.add_people.tooltip": "Přidat člena týmu do tohoto kanálu", "channel_info_rhs.top_buttons.copied": "Zkopírováno", @@ -3683,6 +3707,8 @@ "email_verify.return": "Vraťte se k přihlášení", "email_verify.sending": "Odesílání e-mailu…", "email_verify.sent": "Ověřovací e-mail odeslán", + "emoji_gif_picker.dialog.emojis": "Výběr emoji", + "emoji_gif_picker.dialog.gifs": "Výběr GIF", "emoji_gif_picker.tabs.emojis": "Emodži", "emoji_gif_picker.tabs.gifs": "GIF obrázky", "emoji_list.actions": "Akce", @@ -4329,6 +4355,7 @@ "mobile.set_status.dnd.icon": "Ikona nerušit", "mobile.set_status.offline.icon": "Offline ikona", "mobile.set_status.online.icon": "Ikona připojen", + "modal.header_close": "Zavřít", "modal.manual_status.ask": "Příště se neptat", "modal.manual_status.auto_responder.message_away": "Chceš nastavit svůj status na \"Pryč\" a zrušit automatické odpovědi?", "modal.manual_status.auto_responder.message_dnd": "Chceš nastavit svůj status na \"Nerušit\" a vypnout automatické odpovědi?", @@ -4377,6 +4404,7 @@ "more_channels.view": "Zobrazit", "more_direct_channels.directchannel.deactivated": "{displayname} – Deaktivováno", "more_direct_channels.directchannel.you": "{displayname} (Vy)", + "more_direct_channels.new_convo_add.label": "Přidat možnost {label}", "more_direct_channels.new_convo_note": "Tím zahájíte novou konverzaci. Pokud přidáváte hodně lidí, zvažte vytvoření soukromého kanálu.", "more_direct_channels.new_convo_note.full": "Dosáhli jste maximálního počtu lidí pro tuto konverzaci. Zvažte místo toho vytvoření soukromého kanálu.", "more_direct_channels.title": "Přímé zprávy", @@ -4516,6 +4544,7 @@ "onboardingTask.checklist.main_subtitle": "Pořádně se pusťme do toho.", "onboardingTask.checklist.no_thanks": "Ne, děkuji", "onboardingTask.checklist.start_enterprise_now": "Vyzkoušejte si Enterprise edici zdarma!", + "onboardingTask.checklist.start_onboarding_process": "Zahájit proces onboardingu.", "onboardingTask.checklist.task_complete_your_profile": "Vyplňte svůj profil.", "onboardingTask.checklist.task_download_mm_apps": "Stáhněte si desktopovou a mobilní aplikaci.", "onboardingTask.checklist.task_invite_team_members": "Pozvěte členy týmu do pracovního prostoru.", @@ -4628,6 +4657,9 @@ "post.ariaLabel.replyMessage": "{authorName} odpověděl(a), {message}, dne {date} v {time}", "post.reminder.acknowledgement": "V {reminderTime}, {reminderDate} vám bude připomenuta tato zpráva od uživatele {username}: {permaLink}", "post.reminder.systemBot": "Dobrý den, zde je vaše připomenutí ohledně této zprávy od uživatele {username}: {permaLink}", + "post.renderError.message": "Došlo k chybě při vykreslování tohoto příspěvku.", + "post.renderError.retry": "Opakovat", + "post.renderError.retryLabel": "Opakovat vykreslení tohoto příspěvku", "post_body.check_for_out_of_channel_groups_mentions.message": "nebyli notifikováni tímto zmiňováním, protože nejsou v tomto kanálu. Nemohou být přidáni do kanálu, protože nejsou členy propojených skupin. Pro přidání jich do tohoto kanálu musí být přidáni do jedné z propojených skupin.", "post_body.check_for_out_of_channel_mentions.link.and": " a ", "post_body.check_for_out_of_channel_mentions.link.private": "přidejte je k tomuto soukromému kanálu", @@ -4784,6 +4816,8 @@ "pricing_modal.title": "Vybrat plán", "pricing_modal.wantToTry": "Chcete zkusit? ", "pricing_modal.wantToUpgrade": "Chcete upgradovat? ", + "profile_popover.aria_label.with_username": "Vyskakovací okno profilu {userName}", + "profile_popover.aria_label.without_username": "vyskakovací okno profilu", "promote_to_user_modal.desc": "Tato akce povýší hosta {username} na člena. To umožní uživateli připojit se k veřejným kanálům a komunikovat s uživateli mimo kanály, kterých je aktuálně členem. Jste si jisti, že chcete povýšit hosta {username} na člena?", "promote_to_user_modal.promote": "Povýšit", "promote_to_user_modal.title": "Povýšit hosta {username} na člena", @@ -4937,6 +4971,7 @@ "search_hint.enter_to_search": "Stiskněte Enter pro vyhledávání", "search_hint.enter_to_select": "Stiskněte Enter pro výběr", "search_hint.filter": "Filtrovat hledání pomocí:", + "search_hint.reset_filters": "Filtry byly nastaveny na výchozí hodnoty, protože jste vybrali jiný tým", "search_item.channelArchived": "Archivováno", "search_item.direct": "Přímá zpráva (od {username})", "search_item.file_tag.direct_message": "Přímá zpráva", @@ -4952,6 +4987,9 @@ "search_list_option.on": "Příspěvky v datu", "search_list_option.phrases": "Příspěvky s frázemi", "search_results.channel-files-header": "Nedávné soubory", + "search_teams_selector.all_teams": "Všechny Týmy", + "search_teams_selector.search_teams": "Vyhledat týmy", + "search_teams_selector.your_teams": "Vaše týmy", "sectionNotice.dismiss": "Skrýt oznámení", "select_team.icon": "Ikona Výběr týmu", "select_team.join.icon": "Ikona Připojit se k týmu", @@ -5120,6 +5158,14 @@ "sidebar.types.favorites": "OBLÍBENÉ", "sidebar.types.unreads": "NEPŘEČTENÉ", "sidebar.unreads": "Více nepřečtených", + "sidebarLeft.browserOrCreateChannelMenu.browseChannelsMenuItem.primaryLabel": "Procházet Kanály", + "sidebarLeft.browserOrCreateChannelMenu.createCategoryMenuItem.primaryLabel": "Vytvořit Novou Kategorii", + "sidebarLeft.browserOrCreateChannelMenu.createNewChannelMenuItem.primaryLabel": "Vytvořit Nový Kanál", + "sidebarLeft.browserOrCreateChannelMenu.createUserGroupMenuItem.primaryLabel": "Vytvořit novou skupinu uživatelů", + "sidebarLeft.browserOrCreateChannelMenu.invitePeopleMenuItem.primaryLabel": "Pozvat Lidi", + "sidebarLeft.browserOrCreateChannelMenu.invitePeopleMenuItem.secondaryLabel": "Přidat lidi do týmu", + "sidebarLeft.browserOrCreateChannelMenu.openDirectMessageMenuItem.primaryLabel": "Otevřít přímou zprávu", + "sidebarLeft.browserOrCreateChannelMenuButton.label": "Procházet nebo vytvořit kanály", "sidebar_left.addChannelsCta": "Přidat kanály", "sidebar_left.add_channel_cta_dropdown.dropdownAriaLabel": "Rozbalovací nabídka Přidat kanál", "sidebar_left.add_channel_dropdown.browseChannels": "Prohlížet kanály", @@ -5535,6 +5581,7 @@ "user.settings.display.theme.title": "Téma", "user.settings.display.timezone": "Časové pásmo", "user.settings.display.title": "Nastavení zobrazení", + "user.settings.general.attributeExtra": "Toto se zobrazí v náhledu vašeho profilu.", "user.settings.general.close": "Zavřít", "user.settings.general.confirmEmail": "Potvrdit e-mail", "user.settings.general.currentEmail": "Aktuální e-mail", @@ -5549,6 +5596,7 @@ "user.settings.general.emailOffice365CantUpdate": "Přihlášení probíhá prostřednictvím Entra ID. E-mail nelze změnit. E-mailová adresa používaná pro upozornění je {email}.", "user.settings.general.emailOpenIdCantUpdate": "Přihlášení se děje přes OpenID Connect. Email nelze změnit. Emailová adresa použita pro notifikace je {email}.", "user.settings.general.emailSamlCantUpdate": "Přihlášení probíhá prostřednictvím služby GitLab. E-mail nelze zmněnit. E-mailová adresa používaná pro upozornění je {email}.", + "user.settings.general.emptyAttribute": "Klikněte na 'Upravit' a přidejte svůj vlastní atribut", "user.settings.general.emptyName": "Klikněte pro přidání svého celého jména", "user.settings.general.emptyNickname": "Klepněte na tlačítko \"Upravit\" pro přidání přezdívky", "user.settings.general.emptyPassword": "Prosím zadejte své aktuální heslo.", @@ -5565,6 +5613,7 @@ "user.settings.general.loginLdap": "Přihlášení se provádí prostřednictvím AD/LDAP ({email})", "user.settings.general.loginOffice365": "Přihlášeno přes Entra ID ({email})", "user.settings.general.loginSaml": "Přihlášení se provádí prostřednictvím Office 365 ({email})", + "user.settings.general.mobile.emptyAttribute": "Klikněte pro přidání vlastního atributu", "user.settings.general.mobile.emptyName": "Klikněte pro přidání svého celého jména", "user.settings.general.mobile.emptyNickname": "Klepněte pro přidání přezdívky", "user.settings.general.mobile.emptyPosition": "Klikněte pro přidání svého pracovního titulu / pozice", @@ -5824,6 +5873,39 @@ "user.settings.tokens.tokenId": "ID tokenu: ", "user.settings.tokens.tokenLoading": "Nahrávám...", "user.settings.tokens.userAccessTokensNone": "Žádné osobní přístupové tokeny.", + "userAccountMenu.awayMenuItem.label": "Pryč", + "userAccountMenu.dndMenuItem.primaryLabel": "Režim nerušit", + "userAccountMenu.dndMenuItem.secondaryLabel": "Vypnout všechna upozornění", + "userAccountMenu.dndMenuItem.secondaryLabel.doNotClear": "Do nekonečna", + "userAccountMenu.dndMenuItem.secondaryLabel.untilLaterSomeTime": "Do {time}", + "userAccountMenu.dndMenuItem.secondaryLabel.untilTodaySomeTime": "Do {time}", + "userAccountMenu.dndMenuItem.secondaryLabel.untilTomorrowSomeTime": "Do zítra {time}", + "userAccountMenu.dndSubMenu.title": "Smazat po:", + "userAccountMenu.dndSubMenuItem.1Hour": "1 hodina", + "userAccountMenu.dndSubMenuItem.2Hours": "2 hodiny", + "userAccountMenu.dndSubMenuItem.30Minutes": "30 min", + "userAccountMenu.dndSubMenuItem.custom": "Zvolte datum a čas", + "userAccountMenu.dndSubMenuItem.doNotClear": "Nemazat", + "userAccountMenu.dndSubMenuItem.tomorrow": "Zítra", + "userAccountMenu.dndSubMenuItem.tomorrowsDateTime": "{shortDay}, {shortTime}", + "userAccountMenu.logoutMenuItem.label": "Odhlásit Se", + "userAccountMenu.menuButton.ariaDescription.away": "Stav je \"Pryč\".", + "userAccountMenu.menuButton.ariaDescription.dnd": "Stav je \"Nerušit\".", + "userAccountMenu.menuButton.ariaDescription.offline": "Stav je \"Offline\".", + "userAccountMenu.menuButton.ariaDescription.online": "Stav je \"Online\".", + "userAccountMenu.menuButton.ariaDescription.ooo": "Stav je \"Mimo kancelář\".", + "userAccountMenu.menuButton.ariaLabel": "Menu účtu uživatele", + "userAccountMenu.offlineMenuItem.label": "Offline", + "userAccountMenu.onlineMenuItem.label": "Online", + "userAccountMenu.oooMenuItem.primaryLabel": "Mimo kancelář", + "userAccountMenu.oooMenuItem.secondaryLabel": "Automatické odpovědi jsou zapnuty", + "userAccountMenu.profileMenuItem.label": "Profil", + "userAccountMenu.setCustomStatusMenuItem.clearTooltip": "Smazat vlastní stav", + "userAccountMenu.setCustomStatusMenuItem.hasStatusWithExpiryAndNoText.ariaDescription": "Stav exspiruje v {time}. Nastavit vlastní stav.", + "userAccountMenu.setCustomStatusMenuItem.hasStatusWithTextAndExpiry.ariaDescription": "Stav je \"{text}\" a vyprší v {time}. Nastavte vlastní status.", + "userAccountMenu.setCustomStatusMenuItem.hasStatusWithTextAndNoExpiry.ariaDescription": "Stav je \"{text}\". Nastavte vlastní status.", + "userAccountMenu.setCustomStatusMenuItem.noStatusSet": "Nastavte vlastní stav", + "userAccountMenu.setCustomStatusMenuItem.noStatusTextSet": "Nastavte text vlastního stavu", "userGuideHelp.askTheCommunity": "Zeptejte se komunity", "userGuideHelp.helpResources": "Zdroje nápovědy", "userGuideHelp.keyboardShortcuts": "Klávesové zkratky", diff --git a/webapp/channels/src/i18n/de.json b/webapp/channels/src/i18n/de.json index c2c4afc62bee..f51d9b28a43d 100644 --- a/webapp/channels/src/i18n/de.json +++ b/webapp/channels/src/i18n/de.json @@ -1887,7 +1887,8 @@ "admin.permissions.sysconsole_section_user_management_channels.name": "Kanäle", "admin.permissions.sysconsole_section_user_management_groups.name": "Gruppen", "admin.permissions.sysconsole_section_user_management_permissions.name": "Berechtigungen", - "admin.permissions.sysconsole_section_user_management_system_roles.name": "Systemrollen", + "admin.permissions.sysconsole_section_user_management_system_roles.description": "Die Einstellung \"Kein Zugriff\" schränkt nur die Oberfläche der Systemkonsole ein. Die zugrundeliegenden API-Endpunkte sind für alle Benutzer in einem Nur-Lese-Status für grundlegende Produktfunktionen zugänglich.", + "admin.permissions.sysconsole_section_user_management_system_roles.name": "Delegierte differenzierte Verwaltung", "admin.permissions.sysconsole_section_user_management_teams.name": "Teams", "admin.permissions.sysconsole_section_user_management_users.description": "Kann Admin Passwörter nicht zurücksetzen", "admin.permissions.sysconsole_section_user_management_users.name": "Benutzer", @@ -2577,9 +2578,11 @@ "admin.system_properties.user_properties.table.actions": "Aktionen", "admin.system_properties.user_properties.table.actions.delete": "Löschen", "admin.system_properties.user_properties.table.property": "Eigenschaft", + "admin.system_properties.user_properties.table.property_name.input.name": "Eigenschaftsname", "admin.system_properties.user_properties.table.type": "Typ", "admin.system_properties.user_properties.table.type.text": "Text", "admin.system_properties.user_properties.table.validation.name_required": "Bitte gib einen Eigenschaftsnamen ein.", + "admin.system_properties.user_properties.table.validation.name_taken": "Eigenschaftsname ist bereits vergeben.", "admin.system_properties.user_properties.table.validation.name_unique": "Eigenschaftsnamen müssen eindeutig sein.", "admin.system_properties.user_properties.title": "Benutzer-Eigenschaften", "admin.system_roles_feature_discovery.copy": "Verwende anpassbare Verwaltungsrollen um ausgesuchten Benutzern Lese- und/oder Schreibzugriff auf ausgewählte Sektionen der Systemkonsole zu geben.", @@ -2905,13 +2908,10 @@ "analytics.system.publicChannels": "Öffentliche Kanäle", "analytics.system.seatsPurchased": "Lizensierte Sitze", "analytics.system.skippedIntensiveQueries": "Um die Performance zu maximieren, sind einige Statistiken deaktiviert. Du kannst sie in der config.json reaktivieren.", - "analytics.system.textPosts": "Nur-Text Beiträge", "analytics.system.title": "Systemstatistiken", "analytics.system.totalBotPosts": "Anzahl der Nachrichten von Bots", "analytics.system.totalChannels": "Kanäle Gesamt", "analytics.system.totalCommands": "Befehle Gesamt", - "analytics.system.totalFilePosts": "Beiträge mit Dateien", - "analytics.system.totalHashtagPosts": "Beiträge mit Hashtags", "analytics.system.totalIncomingWebhooks": "Eingehende Webhooks", "analytics.system.totalMasterDbConnections": "Master DB Verbindungen", "analytics.system.totalOutgoingWebhooks": "Ausgehende Webhooks", @@ -3282,6 +3282,7 @@ "channel_info_rhs.menu.members": "Mitglieder", "channel_info_rhs.menu.notification_preferences": "Benachrichtigungseinstellungen", "channel_info_rhs.menu.pinned": "Angeheftete Nachrichten", + "channel_info_rhs.menu.title": "Aktionen Kanalinfo", "channel_info_rhs.top_buttons.add_people": "Benutzer hinzufügen", "channel_info_rhs.top_buttons.add_people.tooltip": "Teammitglieder zu diesem Kanal hinzufügen", "channel_info_rhs.top_buttons.copied": "Kopiert", @@ -4970,6 +4971,7 @@ "search_hint.enter_to_search": "Drücke Enter zum Suchen", "search_hint.enter_to_select": "Drücke Enter, um auszuwählen", "search_hint.filter": "Filtere deine Suche mit:", + "search_hint.reset_filters": "Deine Filter wurden zurückgesetzt, weil du ein anderes Team gewählt hast", "search_item.channelArchived": "Archiviert", "search_item.direct": "Direktnachricht (mit {username})", "search_item.file_tag.direct_message": "Direktnachricht", @@ -4985,6 +4987,9 @@ "search_list_option.on": "Nachrichten am", "search_list_option.phrases": "Nachrichten mit Phrasen", "search_results.channel-files-header": "Aktuellste Dateien", + "search_teams_selector.all_teams": "Alle Teams", + "search_teams_selector.search_teams": "Teams suchen", + "search_teams_selector.your_teams": "Deine Teams", "sectionNotice.dismiss": "Mitteilung entfernen", "select_team.icon": "\"Team auswählen\"-Symbol", "select_team.join.icon": "\"Team beitreten\"-Symbol", diff --git a/webapp/channels/src/i18n/en-AU.json b/webapp/channels/src/i18n/en-AU.json index d0df27481c4e..43ac4e8d4fa9 100644 --- a/webapp/channels/src/i18n/en-AU.json +++ b/webapp/channels/src/i18n/en-AU.json @@ -246,6 +246,7 @@ "add_teams_to_scheme.confirmation.message": "This team is already selected in another team scheme, are you sure you want to move it to this team scheme?", "add_teams_to_scheme.confirmation.title": "Team Override Scheme Change?", "add_teams_to_scheme.modalTitle": "Add Teams to Team Selection List", + "add_teams_to_scheme.select_team.label": "Select team {label}", "add_user_to_channel_modal.add": "Add", "add_user_to_channel_modal.cancel": "Cancel", "add_user_to_channel_modal.help": "Type to find a channel. Use ↑↓ to browse, ↵ to select, ESC to dismiss.", @@ -482,7 +483,7 @@ "admin.channel_settings.channel_details.archiveChannel": "Archive Channel", "admin.channel_settings.channel_details.isDefaultDescr": "This default channel cannot be converted into a private channel.", "admin.channel_settings.channel_details.isPublic": "Public channel or private channel", - "admin.channel_settings.channel_details.isPublicDescr": "If `public` the channel is discoverable and any user can join, or if `private` invitations are required. Toggle to convert public channels to private. When Group Sync is enabled, private channels cannot be converted to public.", + "admin.channel_settings.channel_details.isPublicDescr": "Select Public for a channel any user can find and join. {br}Select Private to require channel invitations to join. {br}Use this switch to change this channel from public to private or from private to public.", "admin.channel_settings.channel_details.syncGroupMembers": "Sync Group Members", "admin.channel_settings.channel_details.syncGroupMembersDescr": "When enabled, adding and removing users from groups will add or remove them from this channel. The only way of inviting members to this channel is by adding the groups they belong to. Learn More", "admin.channel_settings.channel_details.unarchiveChannel": "Unarchive Channel", @@ -1494,6 +1495,7 @@ "admin.logs.showErrors": "Show last {n} errors", "admin.logs.title": "Server Logs", "admin.manage_roles.additionalRoles": "Select additional permissions for the account. Read more about roles and permissions.", + "admin.manage_roles.additionalRoles_warning": "Note:The permissions granted above apply to the account as a whole, regardless of whether it is authenticated using a session cookie or a personal access token. For example, selecting post:all will allow the account to post to channels it is not a member of, even without using a personal access token.", "admin.manage_roles.allowUserAccessTokens": "Allow this account to generate personal access tokens.", "admin.manage_roles.allowUserAccessTokensDesc": "Removing this permission doesn't delete existing tokens. To delete them, go to the user's Manage Tokens menu.", "admin.manage_roles.botAdditionalRoles": "Select additional permissions for the account. Read more about roles and permissions.", @@ -1594,8 +1596,10 @@ "admin.password.preview": "Error message preview", "admin.password.symbol": "At least one symbol (e.g. '~!@#$%^&*()')", "admin.password.uppercase": "At least one uppercase letter", + "admin.permissions.group.convert_private_channel_to_public.description": "Convert private channels to public", + "admin.permissions.group.convert_private_channel_to_public.name": "Convert to public", "admin.permissions.group.convert_public_channel_to_private.description": "Convert public channels to private", - "admin.permissions.group.convert_public_channel_to_private.name": "Convert Channels", + "admin.permissions.group.convert_public_channel_to_private.name": "Convert to private", "admin.permissions.group.custom_groups.description": "Create, edit, delete and manage the members of custom groups.", "admin.permissions.group.custom_groups.name": "Custom Groups", "admin.permissions.group.delete_posts.description": "Delete own and other user's posts.", @@ -1655,9 +1659,9 @@ "admin.permissions.permission.assign_system_admin_role.description": "Assign system admin role", "admin.permissions.permission.assign_system_admin_role.name": "Assign system admin role", "admin.permissions.permission.convert_private_channel_to_public.description": "Convert private channels to public", - "admin.permissions.permission.convert_private_channel_to_public.name": "Convert Channels", + "admin.permissions.permission.convert_private_channel_to_public.name": "Convert to public", "admin.permissions.permission.convert_public_channel_to_private.description": "Convert public channels to private", - "admin.permissions.permission.convert_public_channel_to_private.name": "Convert Channels", + "admin.permissions.permission.convert_public_channel_to_private.name": "Convert to private", "admin.permissions.permission.create_custom_group.description": "Create custom groups.", "admin.permissions.permission.create_custom_group.name": "Create", "admin.permissions.permission.create_direct_channel.description": "Create direct channel", @@ -1883,7 +1887,8 @@ "admin.permissions.sysconsole_section_user_management_channels.name": "Channels", "admin.permissions.sysconsole_section_user_management_groups.name": "Groups", "admin.permissions.sysconsole_section_user_management_permissions.name": "Permissions", - "admin.permissions.sysconsole_section_user_management_system_roles.name": "System Roles", + "admin.permissions.sysconsole_section_user_management_system_roles.description": "Setting 'No Access' restricts the System Console interface only. The underlying API endpoints are accessible to all users in a read-only state for basic product functionality.", + "admin.permissions.sysconsole_section_user_management_system_roles.name": "Delegated Granular Administration", "admin.permissions.sysconsole_section_user_management_teams.name": "Teams", "admin.permissions.sysconsole_section_user_management_users.description": "Cannot reset admin passwords", "admin.permissions.sysconsole_section_user_management_users.name": "Users", @@ -2486,6 +2491,7 @@ "admin.sidebar.smtp": "SMTP", "admin.sidebar.subscription": "Subscription", "admin.sidebar.systemRoles": "Delegated Granular Administration", + "admin.sidebar.system_properties": "System Properties", "admin.sidebar.teamStatistics": "Team Statistics", "admin.sidebar.teams": "Teams", "admin.sidebar.userManagement": "User Management", @@ -2562,6 +2568,23 @@ "admin.systemUserDetail.teamList.teamType.groupSync": "Group Sync", "admin.systemUserDetail.teamList.teamType.inviteOnly": "Invite Only", "admin.systemUserDetail.title": "User Configuration", + "admin.system_properties.confirm.delete.button": "Delete", + "admin.system_properties.confirm.delete.text": "Deleting this property will remove all associated user-defined values.", + "admin.system_properties.confirm.delete.title": "Delete {name} property", + "admin.system_properties.details.saving_changes": "Saving configuration…", + "admin.system_properties.details.saving_changes_error": "An error occurred while saving the configuration", + "admin.system_properties.user_properties.add_property": "Add property", + "admin.system_properties.user_properties.subtitle": "Customise the properties to show in user profiles", + "admin.system_properties.user_properties.table.actions": "Actions", + "admin.system_properties.user_properties.table.actions.delete": "Delete", + "admin.system_properties.user_properties.table.property": "Property", + "admin.system_properties.user_properties.table.property_name.input.name": "Property Name", + "admin.system_properties.user_properties.table.type": "Type", + "admin.system_properties.user_properties.table.type.text": "Text", + "admin.system_properties.user_properties.table.validation.name_required": "Please enter a property name.", + "admin.system_properties.user_properties.table.validation.name_taken": "Property name already taken.", + "admin.system_properties.user_properties.table.validation.name_unique": "Property names must be unique.", + "admin.system_properties.user_properties.title": "User Properties", "admin.system_roles_feature_discovery.copy": "Assign customisable admin roles to give designated users read and/or write access to select sections of System Console.", "admin.system_roles_feature_discovery.title": "Provide controlled access to the System Console with Mattermost Enterprise", "admin.system_users.column_toggler.dropdownAriaLabel": "Columns visibility menu", @@ -2885,13 +2908,10 @@ "analytics.system.publicChannels": "Public Channels", "analytics.system.seatsPurchased": "Licensed Seats", "analytics.system.skippedIntensiveQueries": "To maximise performance, some statistics are disabled. You can re-enable them in config.json.", - "analytics.system.textPosts": "Posts with Text-only", "analytics.system.title": "System Statistics", "analytics.system.totalBotPosts": "Total Posts from Bots", "analytics.system.totalChannels": "Total Channels", "analytics.system.totalCommands": "Total Commands", - "analytics.system.totalFilePosts": "Posts with Files", - "analytics.system.totalHashtagPosts": "Posts with Hashtags", "analytics.system.totalIncomingWebhooks": "Incoming Webhooks", "analytics.system.totalMasterDbConnections": "Master DB Conns", "analytics.system.totalOutgoingWebhooks": "Outgoing Webhooks", @@ -3211,9 +3231,11 @@ "channel_header.closeChannelInfo": "Close Info", "channel_header.convert": "Convert to Private Channel", "channel_header.delete": "Archive Channel", + "channel_header.directchannel": "{displayName} (you) Channel Menu", "channel_header.directchannel.you": "{displayname} (you) ", "channel_header.flagged": "Saved messages", "channel_header.groupMessageHasGuests": "This group message has guests", + "channel_header.headerText.addNewButton": "Add a channel header", "channel_header.lastActive": "Last online {timestamp}", "channel_header.lastOnline": "Last online {timestamp}", "channel_header.leave": "Leave Channel", @@ -3222,6 +3244,7 @@ "channel_header.mute": "Mute Channel", "channel_header.muteConversation": "Mute Conversation", "channel_header.openChannelInfo": "View Info", + "channel_header.otherchannel": "{displayName} Channel Menu", "channel_header.pinnedPosts": "Pinned messages", "channel_header.recentMentions": "Recent mentions", "channel_header.rename": "Rename Channel", @@ -3259,6 +3282,7 @@ "channel_info_rhs.menu.members": "Members", "channel_info_rhs.menu.notification_preferences": "Notification Preferences", "channel_info_rhs.menu.pinned": "Pinned messages", + "channel_info_rhs.menu.title": "Channel Info Actions", "channel_info_rhs.top_buttons.add_people": "Add People", "channel_info_rhs.top_buttons.add_people.tooltip": "Add team members to this channel", "channel_info_rhs.top_buttons.copied": "Copied", @@ -3683,6 +3707,8 @@ "email_verify.return": "Return to log in", "email_verify.sending": "Sending email…", "email_verify.sent": "Verification email sent", + "emoji_gif_picker.dialog.emojis": "Emoji Picker", + "emoji_gif_picker.dialog.gifs": "GIF Picker", "emoji_gif_picker.tabs.emojis": "Emojis", "emoji_gif_picker.tabs.gifs": "GIFs", "emoji_list.actions": "Actions", @@ -3965,6 +3991,7 @@ "globalThreads.searchGuidance.title": "That’s the end of the list.", "globalThreads.sidebarLink": "Threads", "globalThreads.threadList.noUnreadThreads": "No unread threads", + "globalThreads.threadList.noUnreadThreads.subtitle": "You're all caught up", "globalThreads.threadPane.unreadMessageLink": "You have {numUnread, plural, =0 {no unread threads} =1 {{numUnread} thread} other {{numUnread} threads}} {numUnread, plural, =0 {} other {with unread messages}}", "globalThreads.threadPane.unselectedTitle": "{numUnread, plural, =0 {Looks like you’re all caught up} other {Catch up on your threads}}", "globalThreads.title": "{prefix}Threads - {displayName} {siteName}", @@ -4328,6 +4355,7 @@ "mobile.set_status.dnd.icon": "Do Not Disturb Icon", "mobile.set_status.offline.icon": "Offline Icon", "mobile.set_status.online.icon": "Online Icon", + "modal.header_close": "Close", "modal.manual_status.ask": "Do not ask me again", "modal.manual_status.auto_responder.message_away": "Would you like to switch your status to 'Away' and disable automatic replies?", "modal.manual_status.auto_responder.message_dnd": "Would you like to switch your status to 'Do Not Disturb' and disable automatic replies?", @@ -4376,6 +4404,7 @@ "more_channels.view": "View", "more_direct_channels.directchannel.deactivated": "{displayname} - Deactivated", "more_direct_channels.directchannel.you": "{displayname} (you)", + "more_direct_channels.new_convo_add.label": "Add option {label}", "more_direct_channels.new_convo_note": "This will start a new conversation. If you're adding a lot of people, consider creating a private channel instead.", "more_direct_channels.new_convo_note.full": "You've reached the maximum number of people for this conversation. Consider creating a private channel instead.", "more_direct_channels.title": "Direct Messages", @@ -4515,6 +4544,7 @@ "onboardingTask.checklist.main_subtitle": "Let's get up and running.", "onboardingTask.checklist.no_thanks": "No thanks", "onboardingTask.checklist.start_enterprise_now": "Start your free Enterprise trial now!", + "onboardingTask.checklist.start_onboarding_process": "Start the onboarding process.", "onboardingTask.checklist.task_complete_your_profile": "Complete your profile", "onboardingTask.checklist.task_download_mm_apps": "Download the Desktop and Mobile Apps", "onboardingTask.checklist.task_invite_team_members": "Invite team members to the workspace", @@ -4627,6 +4657,9 @@ "post.ariaLabel.replyMessage": "At {time} {date}, {authorName} replied, {message}", "post.reminder.acknowledgement": "You will be reminded at {reminderTime}, {reminderDate} about this message from {username}: {permaLink}", "post.reminder.systemBot": "Hi there, here's your reminder about this message from {username}: {permaLink}", + "post.renderError.message": "An error occurred while rendering this post.", + "post.renderError.retry": "Retry", + "post.renderError.retryLabel": "Retry rendering this post", "post_body.check_for_out_of_channel_groups_mentions.message": "did not get notified by this mention because they are not in the channel. They cannot be added to the channel because they are not a member of the linked groups. To add them to this channel, they must be added to the linked groups.", "post_body.check_for_out_of_channel_mentions.link.and": " and ", "post_body.check_for_out_of_channel_mentions.link.private": "add them to this private channel", @@ -4783,6 +4816,8 @@ "pricing_modal.title": "Select a plan", "pricing_modal.wantToTry": "Want to try? ", "pricing_modal.wantToUpgrade": "Want to upgrade? ", + "profile_popover.aria_label.with_username": "{userName}'s profile popover", + "profile_popover.aria_label.without_username": "profile popover", "promote_to_user_modal.desc": "This action promotes the guest {username} to a member. It will allow the user to join public channels and interact with users outside of the channels they are currently members of. Are you sure you want to promote guest {username} to member?", "promote_to_user_modal.promote": "Promote", "promote_to_user_modal.title": "Promote guest {username} to member", @@ -4936,6 +4971,7 @@ "search_hint.enter_to_search": "Press Enter to search", "search_hint.enter_to_select": "Press Enter to select", "search_hint.filter": "Filter your search with:", + "search_hint.reset_filters": "Your filters were reset because you chose a different team", "search_item.channelArchived": "Archived", "search_item.direct": "Direct Message (with {username})", "search_item.file_tag.direct_message": "Direct Message", @@ -4951,6 +4987,9 @@ "search_list_option.on": "Messages on a date", "search_list_option.phrases": "Messages with phrases", "search_results.channel-files-header": "Recent files", + "search_teams_selector.all_teams": "All Teams", + "search_teams_selector.search_teams": "Search teams", + "search_teams_selector.your_teams": "Your teams", "sectionNotice.dismiss": "Dismiss notice", "select_team.icon": "Select Team Icon", "select_team.join.icon": "Join Team Icon", @@ -5119,6 +5158,14 @@ "sidebar.types.favorites": "FAVOURITES", "sidebar.types.unreads": "UNREADS", "sidebar.unreads": "More unreads", + "sidebarLeft.browserOrCreateChannelMenu.browseChannelsMenuItem.primaryLabel": "Browse channels", + "sidebarLeft.browserOrCreateChannelMenu.createCategoryMenuItem.primaryLabel": "Create new category", + "sidebarLeft.browserOrCreateChannelMenu.createNewChannelMenuItem.primaryLabel": "Create new channel", + "sidebarLeft.browserOrCreateChannelMenu.createUserGroupMenuItem.primaryLabel": "Create new user group", + "sidebarLeft.browserOrCreateChannelMenu.invitePeopleMenuItem.primaryLabel": "Invite people", + "sidebarLeft.browserOrCreateChannelMenu.invitePeopleMenuItem.secondaryLabel": "Add people to the team", + "sidebarLeft.browserOrCreateChannelMenu.openDirectMessageMenuItem.primaryLabel": "Open a direct message", + "sidebarLeft.browserOrCreateChannelMenuButton.label": "Browse or create channels", "sidebar_left.addChannelsCta": "Add channels", "sidebar_left.add_channel_cta_dropdown.dropdownAriaLabel": "Add Channel Dropdown", "sidebar_left.add_channel_dropdown.browseChannels": "Browse channels", @@ -5145,6 +5192,7 @@ "sidebar_left.sidebar_category_menu.viewCategory": "Mark category as read", "sidebar_left.sidebar_channel.selectedCount": "{count} selected", "sidebar_left.sidebar_channel_menu.addMembers": "Add Members", + "sidebar_left.sidebar_channel_menu.bookmarks": "Bookmarks Bar", "sidebar_left.sidebar_channel_menu.channels": "Channels", "sidebar_left.sidebar_channel_menu.copyLink": "Copy Link", "sidebar_left.sidebar_channel_menu.dropdownAriaLabel": "Edit channel menu", @@ -5533,6 +5581,7 @@ "user.settings.display.theme.title": "Theme", "user.settings.display.timezone": "Timezone", "user.settings.display.title": "Display Settings", + "user.settings.general.attributeExtra": "This will be shown in your profile popover.", "user.settings.general.close": "Close", "user.settings.general.confirmEmail": "Confirm Email", "user.settings.general.currentEmail": "Current Email", @@ -5547,6 +5596,7 @@ "user.settings.general.emailOffice365CantUpdate": "Login is handled through Entra ID and can't be updated. The email address used for notifications is {email}.", "user.settings.general.emailOpenIdCantUpdate": "Login occurs through OpenID Connect. Email cannot be updated. Email address used for notifications is {email}.", "user.settings.general.emailSamlCantUpdate": "Login occurs through SAML. Email cannot be updated. Email address used for notifications is {email}.", + "user.settings.general.emptyAttribute": "Click 'Edit' to add your custom attribute", "user.settings.general.emptyName": "Click 'Edit' to add your full name", "user.settings.general.emptyNickname": "Click 'Edit' to add a nickname", "user.settings.general.emptyPassword": "Please enter your current password.", @@ -5563,6 +5613,7 @@ "user.settings.general.loginLdap": "Login done through AD/LDAP ({email})", "user.settings.general.loginOffice365": "Login handled by Entra ID ({email})", "user.settings.general.loginSaml": "Login done through SAML ({email})", + "user.settings.general.mobile.emptyAttribute": "Click to add your custom attribute", "user.settings.general.mobile.emptyName": "Click to add your full name", "user.settings.general.mobile.emptyNickname": "Click to add a nickname", "user.settings.general.mobile.emptyPosition": "Click to add your job title / position", @@ -5822,6 +5873,38 @@ "user.settings.tokens.tokenId": "Token ID: ", "user.settings.tokens.tokenLoading": "Loading...", "user.settings.tokens.userAccessTokensNone": "No personal access tokens.", + "userAccountMenu.awayMenuItem.label": "Away", + "userAccountMenu.dndMenuItem.primaryLabel": "Do not disturb", + "userAccountMenu.dndMenuItem.secondaryLabel": "Disables all notifications", + "userAccountMenu.dndMenuItem.secondaryLabel.untilLaterSomeTime": "Until {time}", + "userAccountMenu.dndMenuItem.secondaryLabel.untilTodaySomeTime": "Until {time}", + "userAccountMenu.dndMenuItem.secondaryLabel.untilTomorrowSomeTime": "Until tomorrow {time}", + "userAccountMenu.dndSubMenu.title": "Clear after:", + "userAccountMenu.dndSubMenuItem.1Hour": "1 hour", + "userAccountMenu.dndSubMenuItem.2Hours": "2 hours", + "userAccountMenu.dndSubMenuItem.30Minutes": "30 minutes", + "userAccountMenu.dndSubMenuItem.custom": "Choose date and time", + "userAccountMenu.dndSubMenuItem.doNotClear": "Don't clear", + "userAccountMenu.dndSubMenuItem.tomorrow": "Tomorrow", + "userAccountMenu.dndSubMenuItem.tomorrowsDateTime": "{shortDay}, {shortTime}", + "userAccountMenu.logoutMenuItem.label": "Log out", + "userAccountMenu.menuButton.ariaDescription.away": "Status is 'Away'.", + "userAccountMenu.menuButton.ariaDescription.dnd": "Status is 'Do not disturb'.", + "userAccountMenu.menuButton.ariaDescription.offline": "Status is 'Offline'.", + "userAccountMenu.menuButton.ariaDescription.online": "Status is 'Online'.", + "userAccountMenu.menuButton.ariaDescription.ooo": "Status is 'Out of office'.", + "userAccountMenu.menuButton.ariaLabel": "User's account menu", + "userAccountMenu.offlineMenuItem.label": "Offline", + "userAccountMenu.onlineMenuItem.label": "Online", + "userAccountMenu.oooMenuItem.primaryLabel": "Out of office", + "userAccountMenu.oooMenuItem.secondaryLabel": "Automatic replies are enabled", + "userAccountMenu.profileMenuItem.label": "Profile", + "userAccountMenu.setCustomStatusMenuItem.clearTooltip": "Clear custom status", + "userAccountMenu.setCustomStatusMenuItem.hasStatusWithExpiryAndNoText.ariaDescription": "Status expires at {time}. Set a custom status.", + "userAccountMenu.setCustomStatusMenuItem.hasStatusWithTextAndExpiry.ariaDescription": "Status is '\\{text}'\\ and expires at {time}. Set a custom status.", + "userAccountMenu.setCustomStatusMenuItem.hasStatusWithTextAndNoExpiry.ariaDescription": "Status is '\\{text}'\\. Set a custom status.", + "userAccountMenu.setCustomStatusMenuItem.noStatusSet": "Set custom status", + "userAccountMenu.setCustomStatusMenuItem.noStatusTextSet": "Set custom status text", "userGuideHelp.askTheCommunity": "Ask the community", "userGuideHelp.helpResources": "Help resources", "userGuideHelp.keyboardShortcuts": "Keyboard shortcuts", diff --git a/webapp/channels/src/i18n/es.json b/webapp/channels/src/i18n/es.json index 463a61d8db38..6ca5ceee0f01 100644 --- a/webapp/channels/src/i18n/es.json +++ b/webapp/channels/src/i18n/es.json @@ -2455,13 +2455,10 @@ "analytics.system.publicChannels": "Canales Públicos", "analytics.system.seatsPurchased": "Licencias para Usuarios", "analytics.system.skippedIntensiveQueries": "Para maximizar el desempeño, algunas estadísticas están desactivadas. Puedes re-activarlas en el config.json.", - "analytics.system.textPosts": "Mensajes de sólo Texto", "analytics.system.title": "Estadísticas del Sistema", "analytics.system.totalBotPosts": "Total de Mensajes de Bots", "analytics.system.totalChannels": "Total de Canales", "analytics.system.totalCommands": "Total de Comandos", - "analytics.system.totalFilePosts": "Mensajes con Archivos", - "analytics.system.totalHashtagPosts": "Mensajes con Hashtags", "analytics.system.totalIncomingWebhooks": "Webhooks de Entrada", "analytics.system.totalMasterDbConnections": "Conexiones a BD Maestra", "analytics.system.totalOutgoingWebhooks": "Webhooks de Salida", diff --git a/webapp/channels/src/i18n/fa.json b/webapp/channels/src/i18n/fa.json index 337c5a7b4931..80df6fc31d40 100644 --- a/webapp/channels/src/i18n/fa.json +++ b/webapp/channels/src/i18n/fa.json @@ -2175,13 +2175,10 @@ "analytics.system.privateGroups": "کانال های خصوصی", "analytics.system.publicChannels": "کانال های عمومی", "analytics.system.skippedIntensiveQueries": "برای به حداکثر رساندن عملکرد، برخی از آمارها غیرفعال هستند. می‌توانید آنها را دوباره در config.json فعال کنید.", - "analytics.system.textPosts": "پست هایی با متن فقط", "analytics.system.title": "آمار سیستم", "analytics.system.totalBotPosts": "مجموع پست های ربات ها", "analytics.system.totalChannels": "مجموع کانال ها", "analytics.system.totalCommands": "کل دستورات", - "analytics.system.totalFilePosts": "پست هایی با فایل", - "analytics.system.totalHashtagPosts": "پست هایی با هشتگ", "analytics.system.totalIncomingWebhooks": "وب هوک های ورودی", "analytics.system.totalMasterDbConnections": "Master DB Conns", "analytics.system.totalOutgoingWebhooks": "وب هوک های خروجی", diff --git a/webapp/channels/src/i18n/fr.json b/webapp/channels/src/i18n/fr.json index 7039c0122439..2ae179eb7501 100644 --- a/webapp/channels/src/i18n/fr.json +++ b/webapp/channels/src/i18n/fr.json @@ -2502,13 +2502,10 @@ "analytics.system.privateGroups": "Canaux privés", "analytics.system.publicChannels": "Canaux publics", "analytics.system.skippedIntensiveQueries": "Pour maximiser les performances, certaines statistiques sont désactivées. Vous pouvez les réactiver via le fichier de configuration config.json.", - "analytics.system.textPosts": "Messages avec texte uniquement", "analytics.system.title": "Statistiques du serveur", "analytics.system.totalBotPosts": "Nombre total de messages des bots", "analytics.system.totalChannels": "Nombre de canaux", "analytics.system.totalCommands": "Nombre total de commandes", - "analytics.system.totalFilePosts": "Messages avec fichiers", - "analytics.system.totalHashtagPosts": "Messages avec hashtags", "analytics.system.totalIncomingWebhooks": "Webhooks entrants", "analytics.system.totalMasterDbConnections": "Connexions bases de données maîtres", "analytics.system.totalOutgoingWebhooks": "Webhooks sortants", diff --git a/webapp/channels/src/i18n/fy.json b/webapp/channels/src/i18n/fy.json index f6fd249c0476..12e2bcc6855c 100644 --- a/webapp/channels/src/i18n/fy.json +++ b/webapp/channels/src/i18n/fy.json @@ -2316,13 +2316,10 @@ "analytics.system.privateGroups": "Privé-kanalen", "analytics.system.publicChannels": "Publieke kanalen", "analytics.system.skippedIntensiveQueries": "Om de prestaties te maximaliseren, zijn sommige statistieken uitgeschakeld. Je kan deze opnieuw inschakelen in config.json .", - "analytics.system.textPosts": "Berichten met enkel tekst", "analytics.system.title": "Systeem-statistieken", "analytics.system.totalBotPosts": "Totaal aantal berichten van Bots", "analytics.system.totalChannels": "Totaal aantal kanalen", "analytics.system.totalCommands": "Totaal aantal opdrachten", - "analytics.system.totalFilePosts": "Berichten met bestanden", - "analytics.system.totalHashtagPosts": "Berichten met hashtags", "analytics.system.totalIncomingWebhooks": "Inkomende webhooks", "analytics.system.totalMasterDbConnections": "Meester DB Conns", "analytics.system.totalOutgoingWebhooks": "Uitgaande webhooks", diff --git a/webapp/channels/src/i18n/hu.json b/webapp/channels/src/i18n/hu.json index 9a548000147e..cdbbefd4eeb8 100644 --- a/webapp/channels/src/i18n/hu.json +++ b/webapp/channels/src/i18n/hu.json @@ -2305,13 +2305,10 @@ "analytics.system.privateGroups": "Privát csatornák", "analytics.system.publicChannels": "Nyilvános csatornák", "analytics.system.skippedIntensiveQueries": "A teljesítmény maximalizálása érdekében egyes statisztikák le vannak tiltva. Újra engedélyezheti őket a config.json fájlban.", - "analytics.system.textPosts": "Bejegyzések csak szöveggel", "analytics.system.title": "Rendszerstatisztikák", "analytics.system.totalBotPosts": "Összes bejegyzés botoktól", "analytics.system.totalChannels": "Összes csatorna", "analytics.system.totalCommands": "Összes parancs", - "analytics.system.totalFilePosts": "Bejegyzések fájlokkal", - "analytics.system.totalHashtagPosts": "Bejegyzések ujjlenyomatokkal", "analytics.system.totalIncomingWebhooks": "Bejövő webhookok", "analytics.system.totalMasterDbConnections": "Mester DB csatlakozások", "analytics.system.totalOutgoingWebhooks": "Kimenő webhookok", diff --git a/webapp/channels/src/i18n/it.json b/webapp/channels/src/i18n/it.json index bcfc0d6b024b..451fcf51db7c 100644 --- a/webapp/channels/src/i18n/it.json +++ b/webapp/channels/src/i18n/it.json @@ -1876,13 +1876,10 @@ "analytics.system.privateGroups": "Canali Privati", "analytics.system.publicChannels": "Canali Pubblici", "analytics.system.skippedIntensiveQueries": "Per massimizzare le prestazioni alcune statistiche sono disattivate. Si possono riattivare nel file config.json.", - "analytics.system.textPosts": "Pubblicazioni con solo testo", "analytics.system.title": "Statistiche di Sistema", "analytics.system.totalBotPosts": "Pubblicazioni totali dai Bot", "analytics.system.totalChannels": "Canali totali", "analytics.system.totalCommands": "Comandi totali", - "analytics.system.totalFilePosts": "Pubblicazioni con file", - "analytics.system.totalHashtagPosts": "Pubblicazioni con Hashtags", "analytics.system.totalIncomingWebhooks": "Webhooks in ingresso", "analytics.system.totalMasterDbConnections": "Connessioni al database principale", "analytics.system.totalOutgoingWebhooks": "Webhooks in uscita", diff --git a/webapp/channels/src/i18n/ja.json b/webapp/channels/src/i18n/ja.json index 44c3a8df4359..3d3a838f3b7f 100644 --- a/webapp/channels/src/i18n/ja.json +++ b/webapp/channels/src/i18n/ja.json @@ -246,6 +246,7 @@ "add_teams_to_scheme.confirmation.message": "このチームは別のチームスキームで既に選択されています。本当にこのチームスキームに移動しますか?", "add_teams_to_scheme.confirmation.title": "チーム上書きスキームを変更しますか?", "add_teams_to_scheme.modalTitle": "チームを チーム選択リストに追加してください", + "add_teams_to_scheme.select_team.label": "チーム {label} を選択", "add_user_to_channel_modal.add": "追加", "add_user_to_channel_modal.cancel": "キャンセル", "add_user_to_channel_modal.help": "チャンネルを検索するために文字を入力してください。↑↓で閲覧、 ↵で選択、 ESCでキャンセル。", @@ -1494,6 +1495,7 @@ "admin.logs.showErrors": "最新の {n} エラーを表示", "admin.logs.title": "サーバーログ", "admin.manage_roles.additionalRoles": "アカウントに対する追加の権限を選択してください。ロールと権限について詳しく。", + "admin.manage_roles.additionalRoles_warning": "注:上記で付与された権限は、セッションクッキーやパーソナルアクセストークンを使って認証されているかどうかに関わらず、アカウント全体に適用されます。例えば、投稿:全て を選択すると、パーソナルアクセストークンを使わなくても、そのアカウントがメンバーでないチャンネルに投稿できるようになります。", "admin.manage_roles.allowUserAccessTokens": "このアカウントがパーソナルアクセストークンを生成できるようにする。", "admin.manage_roles.allowUserAccessTokensDesc": "この権限を削除しても既存のトークンは削除されません。それらのトークンを削除するには、ユーザーのトークン管理メニューから操作してください。", "admin.manage_roles.botAdditionalRoles": "アカウントに対する追加の権限を選択してください。ロールと権限について詳しく。", @@ -1594,8 +1596,10 @@ "admin.password.preview": "エラーメッセージのプレビュー", "admin.password.symbol": "少なくとも1つの記号が必要です(例: \"~!@#$%^&*()\")", "admin.password.uppercase": "少なくとも1つの英大文字が必要です", + "admin.permissions.group.convert_private_channel_to_public.description": "非公開チャンネルを公開チャンネルに変換する", + "admin.permissions.group.convert_private_channel_to_public.name": "公開に変換する", "admin.permissions.group.convert_public_channel_to_private.description": "公開チャンネルを非公開チャンネルに変換する", - "admin.permissions.group.convert_public_channel_to_private.name": "チャンネルを変換する", + "admin.permissions.group.convert_public_channel_to_private.name": "非公開に変換する", "admin.permissions.group.custom_groups.description": "カスタムグループのメンバーを作成、編集、削除、管理する。", "admin.permissions.group.custom_groups.name": "カスタムグループ", "admin.permissions.group.delete_posts.description": "自分と他者の投稿を削除する。", @@ -1655,9 +1659,9 @@ "admin.permissions.permission.assign_system_admin_role.description": "システム管理者にする", "admin.permissions.permission.assign_system_admin_role.name": "システム管理者にする", "admin.permissions.permission.convert_private_channel_to_public.description": "非公開チャンネルを公開チャンネルに変換する", - "admin.permissions.permission.convert_private_channel_to_public.name": "チャンネルを変換する", + "admin.permissions.permission.convert_private_channel_to_public.name": "公開に変換する", "admin.permissions.permission.convert_public_channel_to_private.description": "公開チャンネルを非公開チャンネルに変換する", - "admin.permissions.permission.convert_public_channel_to_private.name": "チャンネルを変換する", + "admin.permissions.permission.convert_public_channel_to_private.name": "非公開に変換する", "admin.permissions.permission.create_custom_group.description": "カスタムグループを作成する。", "admin.permissions.permission.create_custom_group.name": "作成", "admin.permissions.permission.create_direct_channel.description": "ダイレクトチャンネルを作成する", @@ -1883,7 +1887,8 @@ "admin.permissions.sysconsole_section_user_management_channels.name": "チャンネル", "admin.permissions.sysconsole_section_user_management_groups.name": "グループ", "admin.permissions.sysconsole_section_user_management_permissions.name": "権限", - "admin.permissions.sysconsole_section_user_management_system_roles.name": "システムロール", + "admin.permissions.sysconsole_section_user_management_system_roles.description": "「アクセス権なし」を設定すると、システムコンソールのインタフェースのみが制限されます。基礎となるAPIエンドポイントへのアクセスはプロダクトの基本機能のため、すべてのユーザーが読み取り専用の状態でアクセスできます。", + "admin.permissions.sysconsole_section_user_management_system_roles.name": "委任された詳細な管理", "admin.permissions.sysconsole_section_user_management_teams.name": "チーム", "admin.permissions.sysconsole_section_user_management_users.description": "管理者のパスワードはリセットできません", "admin.permissions.sysconsole_section_user_management_users.name": "ユーザー", @@ -2486,6 +2491,7 @@ "admin.sidebar.smtp": "SMTP", "admin.sidebar.subscription": "サブスクリプション", "admin.sidebar.systemRoles": "委任された詳細な管理", + "admin.sidebar.system_properties": "システム・プロパティ", "admin.sidebar.teamStatistics": "チームの統計", "admin.sidebar.teams": "チーム", "admin.sidebar.userManagement": "ユーザー管理", @@ -2562,6 +2568,23 @@ "admin.systemUserDetail.teamList.teamType.groupSync": "グループ同期", "admin.systemUserDetail.teamList.teamType.inviteOnly": "招待のみ", "admin.systemUserDetail.title": "ユーザー設定", + "admin.system_properties.confirm.delete.button": "削除", + "admin.system_properties.confirm.delete.text": "このプロパティを削除すると、このプロパティに設定されたすべてのユーザー定義値が削除されます。", + "admin.system_properties.confirm.delete.title": "{name} プロパティを削除する", + "admin.system_properties.details.saving_changes": "設定を保存しています…", + "admin.system_properties.details.saving_changes_error": "設定の保存中にエラーが発生しました", + "admin.system_properties.user_properties.add_property": "プロパティを追加", + "admin.system_properties.user_properties.subtitle": "ユーザープロフィールに表示するプロパティをカスタマイズします", + "admin.system_properties.user_properties.table.actions": "アクション", + "admin.system_properties.user_properties.table.actions.delete": "削除", + "admin.system_properties.user_properties.table.property": "プロパティ", + "admin.system_properties.user_properties.table.property_name.input.name": "プロパティ名", + "admin.system_properties.user_properties.table.type": "種別", + "admin.system_properties.user_properties.table.type.text": "テキスト", + "admin.system_properties.user_properties.table.validation.name_required": "プロパティ名を入力してください。", + "admin.system_properties.user_properties.table.validation.name_taken": "その名前はすでに使われています。", + "admin.system_properties.user_properties.table.validation.name_unique": "プロパティ名は一意でなくてはなりません。", + "admin.system_properties.user_properties.title": "ユーザープロパティ", "admin.system_roles_feature_discovery.copy": "指定したユーザーにシステムコンソールの特定のセクションに対する読み取りおよび書き込みのアクセス権を与えるには、カスタマイズ可能な管理者ロールを設定してください。", "admin.system_roles_feature_discovery.title": "Mattermost Enterpriseでシステムコンソールへの制御されたアクセスを提供する", "admin.system_users.column_toggler.dropdownAriaLabel": "列の表示メニュー", @@ -2885,13 +2908,10 @@ "analytics.system.publicChannels": "公開チャンネル", "analytics.system.seatsPurchased": "ライセンス済みのシート", "analytics.system.skippedIntensiveQueries": "パフォーマンスを最大化するため無効化された統計情報があります。 config.jsonから、それらを再度有効にすることができます。", - "analytics.system.textPosts": "テキストのみの投稿数", "analytics.system.title": "システムの使用統計", "analytics.system.totalBotPosts": "Botによる総投稿数", "analytics.system.totalChannels": "総チャンネル数", "analytics.system.totalCommands": "総コマンド数", - "analytics.system.totalFilePosts": "ファイル付きの投稿数", - "analytics.system.totalHashtagPosts": "ハッシュタグ付きの投稿数", "analytics.system.totalIncomingWebhooks": "内向きのウェブフック", "analytics.system.totalMasterDbConnections": "マスターDB接続", "analytics.system.totalOutgoingWebhooks": "外向きのウェブフック", @@ -3211,9 +3231,11 @@ "channel_header.closeChannelInfo": "情報を閉じる", "channel_header.convert": "非公開チャンネルに変更する", "channel_header.delete": "チャンネルをアーカイブする", + "channel_header.directchannel": "{displayName} (you) チャンネルメニュー", "channel_header.directchannel.you": "{displayname} (あなた) ", "channel_header.flagged": "保存されたメッセージ", "channel_header.groupMessageHasGuests": "このグループメッセージにはゲストがいます", + "channel_header.headerText.addNewButton": "チャンネルヘッダーを追加", "channel_header.lastActive": "最終オンライン {timestamp}", "channel_header.lastOnline": "最終オンライン {timestamp}", "channel_header.leave": "チャンネルから脱退する", @@ -3222,6 +3244,7 @@ "channel_header.mute": "チャンネルをミュート", "channel_header.muteConversation": "会話をミュートする", "channel_header.openChannelInfo": "情報を表示する", + "channel_header.otherchannel": "{displayName} チャンネルメニュー", "channel_header.pinnedPosts": "ピン留めされたメッセージ", "channel_header.recentMentions": "最近のメンションされた投稿", "channel_header.rename": "チャンネル名を変更する", @@ -3259,6 +3282,7 @@ "channel_info_rhs.menu.members": "メンバー", "channel_info_rhs.menu.notification_preferences": "通知の設定", "channel_info_rhs.menu.pinned": "ピン留めされたメッセージ", + "channel_info_rhs.menu.title": "チャンネル情報アクション", "channel_info_rhs.top_buttons.add_people": "ユーザーを追加する", "channel_info_rhs.top_buttons.add_people.tooltip": "このチャンネルにチームメンバーを追加", "channel_info_rhs.top_buttons.copied": "コピーしました", @@ -3402,7 +3426,7 @@ "claim.oauth_to_email.switchTo": "{type}を電子メールアドレスとパスワードでのログインに切り替える", "claim.oauth_to_email.title": "{type}アカウントを電子メールアドレスに切り替える", "cloud.fetch_error": "請求データの取得中にエラーが発生しました。後でもう一度試してみてください。", - "cloud.fetch_error.retry": "再試行", + "cloud.fetch_error.retry": "リトライ", "cloud.invoice_pdf_preview.download": "記録用にこのページをダウンロードする", "cloud_archived.error.access": "このリンクは {planName} の制限によりアーカイブされたメッセージに属しています。再度メッセージにアクセスするにはアップグレードしてください。", "cloud_archived.error.title": "アーカイブ済みメッセージ", @@ -3537,8 +3561,8 @@ "custom_emoji.header": "カスタム絵文字", "custom_status.expiry.time_picker.title": "時刻", "custom_status.expiry.until": "{time} まで", - "custom_status.expiry_dropdown.choose_date_and_time": "日付と時間の選択", - "custom_status.expiry_dropdown.clear_after": "クリア予定", + "custom_status.expiry_dropdown.choose_date_and_time": "日時の選択", + "custom_status.expiry_dropdown.clear_after": "解除予定", "custom_status.expiry_dropdown.date_and_time": "任意の日付と時間", "custom_status.expiry_dropdown.dont_clear": "クリアしない", "custom_status.expiry_dropdown.four_hours": "4時間", @@ -3683,6 +3707,8 @@ "email_verify.return": "ログインに戻る", "email_verify.sending": "電子メールを送信しています…", "email_verify.sent": "確認メールを送信しました", + "emoji_gif_picker.dialog.emojis": "絵文字選択機能", + "emoji_gif_picker.dialog.gifs": "GIF選択機能", "emoji_gif_picker.tabs.emojis": "絵文字", "emoji_gif_picker.tabs.gifs": "GIF", "emoji_list.actions": "アクション", @@ -4329,6 +4355,7 @@ "mobile.set_status.dnd.icon": "取り込み中アイコン", "mobile.set_status.offline.icon": "オフラインアイコン", "mobile.set_status.online.icon": "オンラインアイコン", + "modal.header_close": "閉じる", "modal.manual_status.ask": "次回からこのメッセージを表示しない", "modal.manual_status.auto_responder.message_away": "あなたのステータスを \"離席中\" に変更し、自動返信を無効化してもよろしいですか?", "modal.manual_status.auto_responder.message_dnd": "あなたのステータスを \"取り込み中\" に変更し、自動返信を無効化してもよろしいですか?", @@ -4377,6 +4404,7 @@ "more_channels.view": "閲覧", "more_direct_channels.directchannel.deactivated": "{displayname} - 無効", "more_direct_channels.directchannel.you": "{displayname} (あなた)", + "more_direct_channels.new_convo_add.label": "オプション {label} を追加", "more_direct_channels.new_convo_note": "新しい会話を始めます。多くの人々を追加する場合、非公開チャンネルの作成を検討してください。", "more_direct_channels.new_convo_note.full": "この会話に参加できる人数の最大数に達しました。代わりに非公開チャンネルを作成することを検討してください。", "more_direct_channels.title": "ダイレクトメッセージ", @@ -4516,6 +4544,7 @@ "onboardingTask.checklist.main_subtitle": "さっそく起動してみましょう。", "onboardingTask.checklist.no_thanks": "いいえ、結構です", "onboardingTask.checklist.start_enterprise_now": "今すぐEnterprise版の無料トライアルを開始!", + "onboardingTask.checklist.start_onboarding_process": "オンボーディングプロセスを開始します。", "onboardingTask.checklist.task_complete_your_profile": "プロフィールの入力を完了する。", "onboardingTask.checklist.task_download_mm_apps": "デスクトップアプリとモバイルアプリをダウンロードする。", "onboardingTask.checklist.task_invite_team_members": "チームメンバーをワークスペースに招待する。", @@ -4595,7 +4624,7 @@ "payment_form.country": "国/地域", "payment_form.zipcode": "郵便番号", "pending_post_actions.cancel": "キャンセル", - "pending_post_actions.retry": "再試行", + "pending_post_actions.retry": "リトライ", "permalink.error.access": "削除されたメッセージ、もしくはアクセス権限のないチャンネルへのパーマリンクです。", "permalink.error.title": "メッセージが見付かりません", "permalink.show_dialog_warn.description": "チャンネル管理者からの明示的な追加なしに、{channel}に参加しようとしています。本当にこの非公開チャンネルに参加しますか?", @@ -4628,6 +4657,9 @@ "post.ariaLabel.replyMessage": "{time} {date}, {authorName} が, {message} と返信しました", "post.reminder.acknowledgement": "{username} からのこのメッセージについて、{reminderDate}, {reminderTime} にリマインドされます: {permaLink}", "post.reminder.systemBot": "{username} からのこのメッセージについてのリマインドです: {permaLink}", + "post.renderError.message": "この投稿のレンダリング中にエラーが発生しました。", + "post.renderError.retry": "リトライ", + "post.renderError.retryLabel": "この投稿のレンダリングをリトライする", "post_body.check_for_out_of_channel_groups_mentions.message": "彼らはチャンネルにいないため、このメンションによる通知は行われませんでした。また、彼らはリンクされたグループのメンバーではないため、チャンネルに追加することもできません。彼らをこのチャンネルに追加するには、リンクされたグループに追加しなければなりません。", "post_body.check_for_out_of_channel_mentions.link.and": " と ", "post_body.check_for_out_of_channel_mentions.link.private": "彼らを非公開チャンネルに追加しますか", @@ -4784,6 +4816,8 @@ "pricing_modal.title": "プランを選択する", "pricing_modal.wantToTry": "試してみますか? ", "pricing_modal.wantToUpgrade": "アップグレードに興味がありますか? ", + "profile_popover.aria_label.with_username": "{userName}のプロフィールポップオーバー", + "profile_popover.aria_label.without_username": "プロフィールポップオーバー", "promote_to_user_modal.desc": "これにより、ゲスト {username} がメンバーへ昇格されます。昇格したゲストは、公開チャンネルへの参加や、メンバーとして参加しているチャンネル外のユーザーとのやりとりが許可されます。本当に {username} をメンバーに昇格しますか?", "promote_to_user_modal.promote": "昇格する", "promote_to_user_modal.title": "ゲスト {username} をメンバーに昇格する", @@ -4937,6 +4971,7 @@ "search_hint.enter_to_search": "Enterキーを押して検索", "search_hint.enter_to_select": "Enterキーを押して選択", "search_hint.filter": "検索フィルター:", + "search_hint.reset_filters": "別のチームを選択したためフィルターがリセットされました", "search_item.channelArchived": "アーカイブ", "search_item.direct": "ダイレクトメッセージ({username}を参照)", "search_item.file_tag.direct_message": "ダイレクトメッセージ", @@ -4952,6 +4987,9 @@ "search_list_option.on": "日付のメッセージ", "search_list_option.phrases": "フレーズを含むメッセージ", "search_results.channel-files-header": "最近のファイル", + "search_teams_selector.all_teams": "全チーム", + "search_teams_selector.search_teams": "チームを検索", + "search_teams_selector.your_teams": "あなたのチーム", "sectionNotice.dismiss": "通知を却下する", "select_team.icon": "チーム選択アイコン", "select_team.join.icon": "チーム参加アイコン", @@ -5120,6 +5158,14 @@ "sidebar.types.favorites": "お気に入り", "sidebar.types.unreads": "未読", "sidebar.unreads": "未読をもっと見る", + "sidebarLeft.browserOrCreateChannelMenu.browseChannelsMenuItem.primaryLabel": "チャンネルを閲覧", + "sidebarLeft.browserOrCreateChannelMenu.createCategoryMenuItem.primaryLabel": "新しいカテゴリを作成する", + "sidebarLeft.browserOrCreateChannelMenu.createNewChannelMenuItem.primaryLabel": "新しいチャンネルを作成する", + "sidebarLeft.browserOrCreateChannelMenu.createUserGroupMenuItem.primaryLabel": "新しいユーザーグループを作成する", + "sidebarLeft.browserOrCreateChannelMenu.invitePeopleMenuItem.primaryLabel": "メンバーを招待する", + "sidebarLeft.browserOrCreateChannelMenu.invitePeopleMenuItem.secondaryLabel": "チームにメンバーを追加する", + "sidebarLeft.browserOrCreateChannelMenu.openDirectMessageMenuItem.primaryLabel": "ダイレクトメッセージを開く", + "sidebarLeft.browserOrCreateChannelMenuButton.label": "チャンネルを閲覧または作成する", "sidebar_left.addChannelsCta": "チャンネルを追加する", "sidebar_left.add_channel_cta_dropdown.dropdownAriaLabel": "チャンネルドロップダウンを追加", "sidebar_left.add_channel_dropdown.browseChannels": "チャンネルを閲覧する", @@ -5535,6 +5581,7 @@ "user.settings.display.theme.title": "テーマ", "user.settings.display.timezone": "タイムゾーン", "user.settings.display.title": "表示の設定", + "user.settings.general.attributeExtra": "これはあなたのプロフィールポップオーバーに表示されます。", "user.settings.general.close": "閉じる", "user.settings.general.confirmEmail": "電子メールアドレスを確認する", "user.settings.general.currentEmail": "現在の電子メールアドレス", @@ -5549,6 +5596,7 @@ "user.settings.general.emailOffice365CantUpdate": "Entra IDからログインしました。電子メールアドレスは更新できません。通知に使われる電子メールアドレスは{email}です。", "user.settings.general.emailOpenIdCantUpdate": "OpenID Connectを通じてログインしました。電子メールアドレスは更新できません。通知に使用される電子メールアドレスは {email} です。", "user.settings.general.emailSamlCantUpdate": "SAMLでログインしました。電子メールアドレスは更新できません。通知に使われる電子メールアドレスは{email}です。", + "user.settings.general.emptyAttribute": "カスタム属性を追加するには「編集」をクリックしてください", "user.settings.general.emptyName": "フルネームを追加するには「編集する」をクリックしてください", "user.settings.general.emptyNickname": "ニックネームを追加するには「編集する」をクリックしてください", "user.settings.general.emptyPassword": "現在のパスワードを入力してください。", @@ -5565,6 +5613,7 @@ "user.settings.general.loginLdap": "AD/LDAPでログインしました({email})", "user.settings.general.loginOffice365": "Entra IDでログインしました({email})", "user.settings.general.loginSaml": "SAMLでログインしました({email})", + "user.settings.general.mobile.emptyAttribute": "カスタム属性を追加するにはクリックしてください", "user.settings.general.mobile.emptyName": "フルネームを追加するためにクリックしてください", "user.settings.general.mobile.emptyNickname": "ニックネームを追加するためにクリックしてください", "user.settings.general.mobile.emptyPosition": "職業/役職を追加するためにクリックしてください", @@ -5824,6 +5873,39 @@ "user.settings.tokens.tokenId": "トークンID: ", "user.settings.tokens.tokenLoading": "読み込み中です...", "user.settings.tokens.userAccessTokensNone": "パーソナルアクセストークンが存在しません。", + "userAccountMenu.awayMenuItem.label": "離席中", + "userAccountMenu.dndMenuItem.primaryLabel": "取り込み中", + "userAccountMenu.dndMenuItem.secondaryLabel": "すべての通知を無効化する", + "userAccountMenu.dndMenuItem.secondaryLabel.doNotClear": "無期限", + "userAccountMenu.dndMenuItem.secondaryLabel.untilLaterSomeTime": "{time} まで", + "userAccountMenu.dndMenuItem.secondaryLabel.untilTodaySomeTime": "{time} まで", + "userAccountMenu.dndMenuItem.secondaryLabel.untilTomorrowSomeTime": "明日の{time} まで", + "userAccountMenu.dndSubMenu.title": "解除予定:", + "userAccountMenu.dndSubMenuItem.1Hour": "1時間", + "userAccountMenu.dndSubMenuItem.2Hours": "2時間", + "userAccountMenu.dndSubMenuItem.30Minutes": "30分", + "userAccountMenu.dndSubMenuItem.custom": "日時を選択", + "userAccountMenu.dndSubMenuItem.doNotClear": "無期限", + "userAccountMenu.dndSubMenuItem.tomorrow": "明日", + "userAccountMenu.dndSubMenuItem.tomorrowsDateTime": "{shortDay}, {shortTime}", + "userAccountMenu.logoutMenuItem.label": "ログアウト", + "userAccountMenu.menuButton.ariaDescription.away": "ステータスが\"離席中\"です。", + "userAccountMenu.menuButton.ariaDescription.dnd": "ステータスが\"取り込み中\"です。", + "userAccountMenu.menuButton.ariaDescription.offline": "ステータスが\"オフライン\"です。", + "userAccountMenu.menuButton.ariaDescription.online": "ステータスが\"オンライン\"です。", + "userAccountMenu.menuButton.ariaDescription.ooo": "ステータスが\"外出中\"です。", + "userAccountMenu.menuButton.ariaLabel": "ユーザーのアカウントメニュー", + "userAccountMenu.offlineMenuItem.label": "オフライン", + "userAccountMenu.onlineMenuItem.label": "オンライン", + "userAccountMenu.oooMenuItem.primaryLabel": "外出中", + "userAccountMenu.oooMenuItem.secondaryLabel": "自動返信が有効化されています", + "userAccountMenu.profileMenuItem.label": "プロフィール", + "userAccountMenu.setCustomStatusMenuItem.clearTooltip": "カスタムステータスをクリアする", + "userAccountMenu.setCustomStatusMenuItem.hasStatusWithExpiryAndNoText.ariaDescription": "ステータスの有効期限は {time} です。カスタムステータスを設定してください。", + "userAccountMenu.setCustomStatusMenuItem.hasStatusWithTextAndExpiry.ariaDescription": "ステータス \"{text}\" の有効期限は {time} です。カスタムステータスを設定してください。", + "userAccountMenu.setCustomStatusMenuItem.hasStatusWithTextAndNoExpiry.ariaDescription": "ステータスは \"{text}\" です。カスタムステータスを設定してください。", + "userAccountMenu.setCustomStatusMenuItem.noStatusSet": "カスタムステータスを設定する", + "userAccountMenu.setCustomStatusMenuItem.noStatusTextSet": "カスタムステータステキストを設定する", "userGuideHelp.askTheCommunity": "コミュニティに質問する", "userGuideHelp.helpResources": "ヘルプ", "userGuideHelp.keyboardShortcuts": "キーボードショートカット", diff --git a/webapp/channels/src/i18n/ko.json b/webapp/channels/src/i18n/ko.json index 77dc946e6236..8d32ec983ae9 100644 --- a/webapp/channels/src/i18n/ko.json +++ b/webapp/channels/src/i18n/ko.json @@ -1826,13 +1826,10 @@ "analytics.system.privateGroups": "비공개 채널", "analytics.system.publicChannels": "공개 채널", "analytics.system.skippedIntensiveQueries": "성능 극대화를 위해 일부 통계들이 비활성화됐습니다. config.json에서 다시 활성화할 수 있습니다.", - "analytics.system.textPosts": "텍스트 글 수", "analytics.system.title": "시스템 통계", "analytics.system.totalBotPosts": "봇들의 총 메시지 수", "analytics.system.totalChannels": "전체 채널", "analytics.system.totalCommands": "전체 명령어", - "analytics.system.totalFilePosts": "파일이 포함된 글 수", - "analytics.system.totalHashtagPosts": "해시태그가 포함된 글 수", "analytics.system.totalIncomingWebhooks": "전체 Incoming Webhook", "analytics.system.totalMasterDbConnections": "마스터 DB 연결", "analytics.system.totalOutgoingWebhooks": "전체 Outgoing Webhook", diff --git a/webapp/channels/src/i18n/lt.json b/webapp/channels/src/i18n/lt.json index 3853ea5f2a2e..c5a1fdc07ebc 100644 --- a/webapp/channels/src/i18n/lt.json +++ b/webapp/channels/src/i18n/lt.json @@ -2381,13 +2381,10 @@ "analytics.system.publicChannels": "Viešieji kanalai", "analytics.system.seatsPurchased": "Iš viso mokamų naudotojų", "analytics.system.skippedIntensiveQueries": "Siekiant maksimaliai padidinti našumą, kai kurie statistiniai duomenys išjungiami. Tu gali iš naujo įjunkite juos config.json.", - "analytics.system.textPosts": "Įrašai tik su tekstu", "analytics.system.title": "Sistemos statistika", "analytics.system.totalBotPosts": "Iš viso žinučių iš robotų", "analytics.system.totalChannels": "Iš viso kanalų", "analytics.system.totalCommands": "Total Commands", - "analytics.system.totalFilePosts": "Įrašai su failais", - "analytics.system.totalHashtagPosts": "Įrašai su grotažymėmis", "analytics.system.totalIncomingWebhooks": "Įeinantys žiniatinklio kabliukai", "analytics.system.totalMasterDbConnections": "Master DB Conns", "analytics.system.totalOutgoingWebhooks": "Siunčiami žiniatinklio kabliai", diff --git a/webapp/channels/src/i18n/nb-NO.json b/webapp/channels/src/i18n/nb-NO.json index e0cbcbb98edd..ac51775226ce 100644 --- a/webapp/channels/src/i18n/nb-NO.json +++ b/webapp/channels/src/i18n/nb-NO.json @@ -3265,12 +3265,16 @@ "user.settings.custom_theme.linkColor": "Lenke farge", "user.settings.custom_theme.newMessageSeparator": "Ny meldingsseparator", "user.settings.custom_theme.onlineIndicator": "Online indikator", + "user.settings.custom_theme.sidebarTextActiveColor": "Sidefeltets aktive tekstfarge", + "user.settings.custom_theme.sidebarTitle": "Sidefelt-stiler", + "user.settings.custom_theme.sidebarUnreadText": "Ulest tekst i sidefeltet", "user.settings.display.availabilityStatusOnPostsDescription": "Når aktivert, vises online tilgjengelighet på profilbilder i meldingslisten.", "user.settings.display.availabilityStatusOnPostsTitle": "Vis online tilgjengelighet på profilbilder", "user.settings.display.channeldisplaymode": "Velg bredden på senterkanalen.", "user.settings.display.clickToReply": "Klikk for å åpne tråder", "user.settings.display.clickToReplyDescription": "Når aktivert, klikk hvor som helst på en melding for å åpne svartråden", "user.settings.display.clockDisplay": "Klokkevisning", + "user.settings.display.collapseDisplay": "Standardutseende for forhåndsvisning av bilder", "user.settings.display.collapseOn": "Utvidet", "user.settings.display.collapsedReplyThreadsOff": "Av", "user.settings.display.collapsedReplyThreadsOn": "På", @@ -3279,6 +3283,7 @@ "user.settings.display.colorizeDes": "Bruk farger for å skille brukere i kompakt modus", "user.settings.display.fixedWidthCentered": "Fast bredde, sentrert", "user.settings.display.fullScreen": "Full bredde", + "user.settings.display.icon": "Ikon for visningsinnstillinger", "user.settings.display.language": "Språk", "user.settings.display.lastActiveDesc": "Når den er aktivert, vil andre brukere se når du sist var aktiv.", "user.settings.display.lastActiveDisplay": "Del siste tidspunkt du var aktiv", @@ -3295,12 +3300,18 @@ "user.settings.display.messageDisplayTitle": "Meldingsvisning", "user.settings.display.militaryClock": "24-timers klokke (eksempel: 16:00)", "user.settings.display.normalClock": "12-timers klokke (eksempel: 4:00PM)", + "user.settings.display.oneClickReactionsOnPostsTitle": "Raske reaksjoner på meldinger", "user.settings.display.preferTime": "Velg hvordan du foretrekker tiden vist.", "user.settings.display.teammateNameDisplayDescription": "Angi hvordan andre brukers navn skal vises i innlegg og direktemeldingslisten.", "user.settings.display.teammateNameDisplayFullname": "Vis for- og etternavn", "user.settings.display.teammateNameDisplayNicknameFullname": "Vis kallenavn hvis det finnes, ellers vis for- og etternavn", "user.settings.display.teammateNameDisplayUsername": "Vis brukernavn", + "user.settings.display.theme.applyToAllTeams": "Bruk nytt tema på alle teamene mine", + "user.settings.display.theme.customTheme": "Egendefinert tema", + "user.settings.display.theme.describe": "Åpne for å administrere temaet ditt", "user.settings.display.theme.otherThemes": "Se andre temaer", + "user.settings.display.theme.themeColors": "Temaets farger", + "user.settings.display.theme.title": "Tema", "user.settings.display.timezone": "Tidssone", "user.settings.display.title": "Visningsinnstillinger", "user.settings.general.close": "Lukk", @@ -3317,6 +3328,7 @@ "user.settings.general.emailOffice365CantUpdate": "Innlogging skjer gjennom Entra ID. E-postadressen kan ikke oppdateres. E-postadressen som brukes for varsler er {email}.", "user.settings.general.emailOpenIdCantUpdate": "Innlogging skjer gjennom OpenID Connect. E-postadressen kan ikke oppdateres. E-postadressen som brukes for varsler er {email}.", "user.settings.general.emailSamlCantUpdate": "Innlogging skjer gjennom SAML. E-postadressen kan ikke oppdateres. E-postadressen som brukes for varsler er {email}.", + "user.settings.general.emptyAttribute": "Klikk på \"Rediger\" for å legge til det egendefinerte attributtet", "user.settings.general.emptyName": "Klikk \"Rediger\" for å legge til ditt fulle navn", "user.settings.general.emptyNickname": "Klikk \"Rediger\" for å legge til et kallenavn", "user.settings.general.emptyPassword": "Skriv inn ditt nåværende passord.", diff --git a/webapp/channels/src/i18n/nl.json b/webapp/channels/src/i18n/nl.json index 7ccecd715de2..1b26df412c7c 100644 --- a/webapp/channels/src/i18n/nl.json +++ b/webapp/channels/src/i18n/nl.json @@ -1495,6 +1495,7 @@ "admin.logs.showErrors": "Toon laatste {n} fouten", "admin.logs.title": "Server Logs", "admin.manage_roles.additionalRoles": "Selecteer aanvullende machtigingen voor het account. Lees meer over rollen en machtigingen .", + "admin.manage_roles.additionalRoles_warning": "Opmerking:De hierboven verleende machtigingen gelden voor het account als geheel, ongeacht of deze geverifieerd is met behulp van een sessiecookie of een persoonlijk toegangstoken. Door bijvoorbeeld Post:all te selecteren kan de account berichten plaatsen in kanalen waar hij geen lid van is, zelfs zonder een persoonlijk toegangstoken te gebruiken.", "admin.manage_roles.allowUserAccessTokens": "Sta dit account toe om persoonlijke toegangstokens te generen.", "admin.manage_roles.allowUserAccessTokensDesc": "Als u deze machtiging verwijdert, worden de bestaande tokens niet verwijderd. Om deze te verwijderen, gaat u naar het menu Beheer Tokens van de gebruiker.", "admin.manage_roles.botAdditionalRoles": "Selecteer aanvullende machtigingen voor het account. Lees meer over rollen en machtigingen .", @@ -1595,8 +1596,10 @@ "admin.password.preview": "Foutboodschap voorbeeld", "admin.password.symbol": "Ten minstens een symbool: (bijv. \"~!@#$%^&*()\")", "admin.password.uppercase": "Minimaal 1 hoofdletter", + "admin.permissions.group.convert_private_channel_to_public.description": "Private kanalen omzetten naar openbare", + "admin.permissions.group.convert_private_channel_to_public.name": "Omzetten naar openbaar", "admin.permissions.group.convert_public_channel_to_private.description": "Zet publieke kanalen om naar privé", - "admin.permissions.group.convert_public_channel_to_private.name": "Kanalen omzetten", + "admin.permissions.group.convert_public_channel_to_private.name": "Omzetten naar privé", "admin.permissions.group.custom_groups.description": "Creëer, bewerk, verwijder en beheer de leden van aangepaste groepen.", "admin.permissions.group.custom_groups.name": "Aangepaste groepen", "admin.permissions.group.delete_posts.description": "Verwijder de eigen berichten en berichten van anderen.", @@ -1656,9 +1659,9 @@ "admin.permissions.permission.assign_system_admin_role.description": "Toekennen van een systeembeheerdersrol", "admin.permissions.permission.assign_system_admin_role.name": "Toekennen van een systeembeheerdersrol", "admin.permissions.permission.convert_private_channel_to_public.description": "Private kanalen omzetten naar publieke", - "admin.permissions.permission.convert_private_channel_to_public.name": "Kanalen omzetten", + "admin.permissions.permission.convert_private_channel_to_public.name": "Omzetten naar openbaar", "admin.permissions.permission.convert_public_channel_to_private.description": "Zet publieke kanalen om naar privé", - "admin.permissions.permission.convert_public_channel_to_private.name": "Kanalen omzetten", + "admin.permissions.permission.convert_public_channel_to_private.name": "Omzetten naar privé", "admin.permissions.permission.create_custom_group.description": "Aangepaste groepen aanmaken.", "admin.permissions.permission.create_custom_group.name": "Aanmaken", "admin.permissions.permission.create_direct_channel.description": "Maak een direct kanaal aan", @@ -1884,7 +1887,8 @@ "admin.permissions.sysconsole_section_user_management_channels.name": "Kanalen", "admin.permissions.sysconsole_section_user_management_groups.name": "Groepen", "admin.permissions.sysconsole_section_user_management_permissions.name": "Machtigingen", - "admin.permissions.sysconsole_section_user_management_system_roles.name": "Systeemrollen", + "admin.permissions.sysconsole_section_user_management_system_roles.description": "Door \"Geen toegang\" in te stellen wordt alleen de interface van de Systeem Console beperkt. De onderliggende API endpoints zijn toegankelijk voor alle gebruikers in een alleen-lezen status voor basis productfunctionaliteit.", + "admin.permissions.sysconsole_section_user_management_system_roles.name": "Gedelegeerd Verfijnd Beheer", "admin.permissions.sysconsole_section_user_management_teams.name": "Teams", "admin.permissions.sysconsole_section_user_management_users.description": "Kan beheerderswachtwoorden niet resetten", "admin.permissions.sysconsole_section_user_management_users.name": "Gebruikers", @@ -2574,9 +2578,11 @@ "admin.system_properties.user_properties.table.actions": "Acties", "admin.system_properties.user_properties.table.actions.delete": "Verwijderen", "admin.system_properties.user_properties.table.property": "Eigenschap", + "admin.system_properties.user_properties.table.property_name.input.name": "Naam eigenschap", "admin.system_properties.user_properties.table.type": "Type", "admin.system_properties.user_properties.table.type.text": "Tekst", "admin.system_properties.user_properties.table.validation.name_required": "Voer een eigenschapnaam in.", + "admin.system_properties.user_properties.table.validation.name_taken": "Naam eigenschap is al in gebruik.", "admin.system_properties.user_properties.table.validation.name_unique": "Namen van eigenschappen moeten uniek zijn.", "admin.system_properties.user_properties.title": "Eigenschappen gebruiker", "admin.system_roles_feature_discovery.copy": "Gebruik aangepaste beheersrollen om aangewezen gebruikers lees- en/of schrijftoegang te geven tot bepaalde delen van de systeemconsole.", @@ -2902,13 +2908,10 @@ "analytics.system.publicChannels": "Publieke kanalen", "analytics.system.seatsPurchased": "Aantal gebruikers binnen je licentie", "analytics.system.skippedIntensiveQueries": "Om de prestaties te maximaliseren, zijn sommige statistieken uitgeschakeld. Je kan deze opnieuw inschakelen in config.json .", - "analytics.system.textPosts": "Berichten met enkel tekst", "analytics.system.title": "Systeem-statistieken", "analytics.system.totalBotPosts": "Totaal aantal berichten van Bots", "analytics.system.totalChannels": "Totaal aantal kanalen", "analytics.system.totalCommands": "Totaal aantal opdrachten", - "analytics.system.totalFilePosts": "Berichten met bestanden", - "analytics.system.totalHashtagPosts": "Berichten met hashtags", "analytics.system.totalIncomingWebhooks": "Inkomende webhooks", "analytics.system.totalMasterDbConnections": "Meester DB Conns", "analytics.system.totalOutgoingWebhooks": "Uitgaande webhooks", @@ -3228,6 +3231,7 @@ "channel_header.closeChannelInfo": "Informatie sluiten", "channel_header.convert": "Omzetten naar privé-kanaal", "channel_header.delete": "Kanaal Archiveren", + "channel_header.directchannel": "{displayName} (jij) Kanaalmenu", "channel_header.directchannel.you": "{displayname} (jij) ", "channel_header.flagged": "Bewaarde berichten", "channel_header.groupMessageHasGuests": "Dit groepsbericht heeft gasten", @@ -3240,6 +3244,7 @@ "channel_header.mute": "Kanaal Dempen", "channel_header.muteConversation": "Conversatie dempen", "channel_header.openChannelInfo": "Informatie bekijken", + "channel_header.otherchannel": "{displayName} Kanaalmenu", "channel_header.pinnedPosts": "Vastgezette berichten", "channel_header.recentMentions": "Recente vermeldingen", "channel_header.rename": "Kanaalnaam Bewerken", @@ -3277,6 +3282,7 @@ "channel_info_rhs.menu.members": "Leden", "channel_info_rhs.menu.notification_preferences": "Meldingsvoorkeuren", "channel_info_rhs.menu.pinned": "Vastgezette berichten", + "channel_info_rhs.menu.title": "Kanaalinfo-acties", "channel_info_rhs.top_buttons.add_people": "Mensen toevoegen", "channel_info_rhs.top_buttons.add_people.tooltip": "Teamleden aan dit kanaal toevoegen", "channel_info_rhs.top_buttons.copied": "Gekopieerd", @@ -3701,6 +3707,8 @@ "email_verify.return": "Terug naar aanmelden", "email_verify.sending": "E-mail wordt verzonden.…", "email_verify.sent": "Verificatie e-mail is verzonden", + "emoji_gif_picker.dialog.emojis": "Emoticonkiezer", + "emoji_gif_picker.dialog.gifs": "GIF kiezer", "emoji_gif_picker.tabs.emojis": "Emojis", "emoji_gif_picker.tabs.gifs": "GIF's", "emoji_list.actions": "Acties", @@ -4808,6 +4816,8 @@ "pricing_modal.title": "Kies een plan", "pricing_modal.wantToTry": "Wil je proberen? ", "pricing_modal.wantToUpgrade": "Wil je upgraden? ", + "profile_popover.aria_label.with_username": "{userName}profiel popover", + "profile_popover.aria_label.without_username": "profiel popover", "promote_to_user_modal.desc": "Met deze actie wordt de gast {username} bevordert naar een lid. Het zal de gebruiker in staat stellen deel te nemen aan openbare kanalen en te communiceren met gebruikers buiten de kanalen waar ze momenteel lid van zijn. Weet je zeker dat je de gast {username} wilt bevorderen tot gebruiker?", "promote_to_user_modal.promote": "Bevorderen", "promote_to_user_modal.title": "Bevorder gast {username} tot gebruiker", @@ -4961,6 +4971,7 @@ "search_hint.enter_to_search": "Druk op Enter om te zoeken", "search_hint.enter_to_select": "Druk op Enter om te selecteren", "search_hint.filter": "Filter je zoekopdracht met:", + "search_hint.reset_filters": "Je filters zijn gereset omdat je een ander team koos", "search_item.channelArchived": "Gearchiveerd", "search_item.direct": "Direct bericht (met {username})", "search_item.file_tag.direct_message": "Privé-bericht", @@ -4976,6 +4987,9 @@ "search_list_option.on": "Berichten op een datum", "search_list_option.phrases": "Berichten met woordcombinaties", "search_results.channel-files-header": "Recente bestanden", + "search_teams_selector.all_teams": "Alle teams", + "search_teams_selector.search_teams": "Teams zoeken", + "search_teams_selector.your_teams": "Jouw teams", "sectionNotice.dismiss": "Melding verwijderen", "select_team.icon": "Selecteer Team Pictogram", "select_team.join.icon": "Word Lid van Team Pictogram", diff --git a/webapp/channels/src/i18n/pl.json b/webapp/channels/src/i18n/pl.json index 430921cad7f0..a25b75329e68 100644 --- a/webapp/channels/src/i18n/pl.json +++ b/webapp/channels/src/i18n/pl.json @@ -2905,13 +2905,10 @@ "analytics.system.publicChannels": "Kanały publiczne", "analytics.system.seatsPurchased": "Licencjonowane miejsca", "analytics.system.skippedIntensiveQueries": "Aby zmaksymalizować wydajność, niektóre statystyki są wyłączone. Możesz ponownie je włączyć w config.json.", - "analytics.system.textPosts": "Wiadomości z samym tekstem", "analytics.system.title": "Statystyki systemu", "analytics.system.totalBotPosts": "Ilość postów dodanych przez boty", "analytics.system.totalChannels": "Wszystkie Kanały", "analytics.system.totalCommands": "Wszystkie Polecenia", - "analytics.system.totalFilePosts": "Wiadomości z Plikami", - "analytics.system.totalHashtagPosts": "Wiadomości z Hashtagami", "analytics.system.totalIncomingWebhooks": "Przychodzące Webhooki", "analytics.system.totalMasterDbConnections": "Połączenia bazą danych Master", "analytics.system.totalOutgoingWebhooks": "Wychodzące Webhooki", diff --git a/webapp/channels/src/i18n/pt-BR.json b/webapp/channels/src/i18n/pt-BR.json index f720dd3215c6..b2073fb25be4 100644 --- a/webapp/channels/src/i18n/pt-BR.json +++ b/webapp/channels/src/i18n/pt-BR.json @@ -2292,13 +2292,10 @@ "analytics.system.privateGroups": "Canais Privados", "analytics.system.publicChannels": "Canais Públicos", "analytics.system.skippedIntensiveQueries": "Para maximizar o desempenho, algumas estatísticas estão desativadas. Você pode reativá-los em config.json.", - "analytics.system.textPosts": "Post com Texto somente", "analytics.system.title": "Estatísticas do Sistema", "analytics.system.totalBotPosts": "Total de Publicações dos Bots", "analytics.system.totalChannels": "Total de Canais", "analytics.system.totalCommands": "Total de Comandos", - "analytics.system.totalFilePosts": "Posts com Arquivos", - "analytics.system.totalHashtagPosts": "Posts com Hashtags", "analytics.system.totalIncomingWebhooks": "Webhooks Entrada", "analytics.system.totalMasterDbConnections": "Master DB Conns", "analytics.system.totalOutgoingWebhooks": "Webhooks Saída", diff --git a/webapp/channels/src/i18n/ro.json b/webapp/channels/src/i18n/ro.json index fcf753c43285..6b3937810fb8 100644 --- a/webapp/channels/src/i18n/ro.json +++ b/webapp/channels/src/i18n/ro.json @@ -2060,13 +2060,10 @@ "analytics.system.privateGroups": "Canale private", "analytics.system.publicChannels": "Canale publice", "analytics.system.skippedIntensiveQueries": "Pentru a maximiza performanța, unele statistici sunt dezactivate. Puteți re-le permită în config.json.", - "analytics.system.textPosts": "Mesaje cu text numai", "analytics.system.title": "Statistica sistemului", "analytics.system.totalBotPosts": "Numărul total de mesaje de la Boți", "analytics.system.totalChannels": "Canalele totale", "analytics.system.totalCommands": "Comenzi totale", - "analytics.system.totalFilePosts": "Postări cu fișiere", - "analytics.system.totalHashtagPosts": "Postări cu Hashtags", "analytics.system.totalIncomingWebhooks": "Webhooks de intrare", "analytics.system.totalMasterDbConnections": "Conectiuni Master DB", "analytics.system.totalOutgoingWebhooks": "Webhook de ieșire", diff --git a/webapp/channels/src/i18n/ru.json b/webapp/channels/src/i18n/ru.json index 02bcbd8d9316..065fe94c6ca8 100644 --- a/webapp/channels/src/i18n/ru.json +++ b/webapp/channels/src/i18n/ru.json @@ -1875,7 +1875,7 @@ "admin.permissions.sysconsole_section_user_management_channels.name": "Каналы", "admin.permissions.sysconsole_section_user_management_groups.name": "Группы", "admin.permissions.sysconsole_section_user_management_permissions.name": "Разрешения", - "admin.permissions.sysconsole_section_user_management_system_roles.name": "Системные роли", + "admin.permissions.sysconsole_section_user_management_system_roles.name": "Делегированное гранулированное администрирование", "admin.permissions.sysconsole_section_user_management_teams.name": "Команды", "admin.permissions.sysconsole_section_user_management_users.description": "Невозможно сбросить пароли администратора", "admin.permissions.sysconsole_section_user_management_users.name": "Пользователи", @@ -2225,6 +2225,51 @@ "admin.saml_feature_discovery.copy": "Когда вы подключаете Mattermost к провайдеру единого входа в своей организации, пользователи могут получить доступ к Mattermost без повторного ввода своих учетных данных.", "admin.saml_feature_discovery.title": "Интегрируйте SAML 2.0 с Mattermost Профессиональный", "admin.saving": "Сохранение конфигурации...", + "admin.secure_connection_detail.shared_channels.placeholder.title_home": "Вы не поделились ни одним каналом", + "admin.secure_connection_detail.shared_channels.placeholder.title_remote": "{remote} не поделился ни одним каналом", + "admin.secure_connection_detail.shared_channels.table.name": "Имя", + "admin.secure_connection_detail.shared_channels.table.remote_actions.remove": "Удалить", + "admin.secure_connection_detail.shared_channels.table.team_home": "Текущая команда", + "admin.secure_connection_detail.shared_channels.table.team_remote": "Команда назначения", + "admin.secure_connection_row.menu-button.aria_label": "Варианты подключения для {connection}", + "admin.secure_connection_row.menu.aria_label": "меню строки безопасного соединения", + "admin.secure_connection_row.menu.delete": "Удалить", + "admin.secure_connection_row.menu.edit": "Изменить", + "admin.secure_connection_row.menu.share": "Сгенерировать код приглашения", + "admin.secure_connections.accept_invite.confirm.done.button": "Принять", + "admin.secure_connections.accept_invite.invite_code": "Зашифровать код приглашения", + "admin.secure_connections.accept_invite.organization_name": "Название организации", + "admin.secure_connections.accept_invite.password": "Пароль", + "admin.secure_connections.accept_invite.prompt": "Принять безопасное соединение с другого сервера", + "admin.secure_connections.accept_invite.prompt_invite_password": "Введите зашифрованный код приглашения, которым поделился с вами администратор сервера, к которому вы подключаетесь.", + "admin.secure_connections.accept_invite.saving_changes_error": "При принятии приглашения произошла ошибка.", + "admin.secure_connections.accept_invite.select_team": "Выберите команду назначения, в которой будут размещены каналы.", + "admin.secure_connections.accept_invite.select_team.legend": "Выберите команду", + "admin.secure_connections.accept_invite.share_title": "Принять приглашение на подключение", + "admin.secure_connections.confirm.delete.button": "Да, удалить", + "admin.secure_connections.confirm.delete.text": "Вы уверены, что хотите удалить безопасное соединение {displayName} ?", + "admin.secure_connections.confirm.delete.title": "Удалить безопасное соединение", + "admin.secure_connections.create_invite.confirm.done.button": "Готово", + "admin.secure_connections.create_invite.confirm.save.button": "Сохранить", + "admin.secure_connections.create_invite.create_invite.notice.title": "Разделите эти две части отдельно, чтобы избежать компрометации системы безопасности", + "admin.secure_connections.create_invite.create_title": "Создать подключение", + "admin.secure_connections.create_invite.create_title_done": "Подключение создано", + "admin.secure_connections.create_invite.share.invite_code": "Зашифровать код приглашения", + "admin.secure_connections.create_invite.share.label": "Поделитесь этим кодом и паролем", + "admin.secure_connections.create_invite.share.message": "Поделитесь кодом приглашения и паролем с администратором сервера, к которому вы хотите подключиться.", + "admin.secure_connections.create_invite.share.password": "Пароль", + "admin.secure_connections.create_invite.share_title": "Код приглашения", + "admin.secure_connections.details.org_name.help": "Дав соединению узнаваемое название, вы сможете запомнить его назначение.", + "admin.secure_connections.details.org_name.label": "Название организации", + "admin.secure_connections.details.saving_changes": "Сохранение безопасного соединения…", + "admin.secure_connections.details.saving_changes_error": "Произошла ошибка при сохранении защищенного соединения", + "admin.secure_connections.details.shared_channels.add_channels.button": "Добавить канал", + "admin.secure_connections.details.shared_channels.subtitle": "Список всех каналов, которыми делится ваша организация, и каналов, которыми вы делитесь извне.", + "admin.secure_connections.details.shared_channels.tabs.home": "Ваши каналы", + "admin.secure_connections.details.shared_channels.title": "Общие каналы", + "admin.secure_connections.details.subtitle": "Имя подключения и другие разрешения", + "admin.secure_connections.details.team.help": "Выберите команду по умолчанию, в которую будут помещаться все общие каналы. Позже это можно будет обновить для конкретных общих каналов.", + "admin.secure_connections.details.team.label": "Команда назначения", "admin.security.password": "Пароль", "admin.server_logs.CopyLog": "Копировать журнал", "admin.server_logs.DataCopied": "Копирование данных", @@ -2790,13 +2835,10 @@ "analytics.system.publicChannels": "Публичные каналы", "analytics.system.seatsPurchased": "Лицензионные места", "analytics.system.skippedIntensiveQueries": "Для обеспечения максимальной производительности некоторые статистические данные отключены. Вы можете повторно включить их в config.json.", - "analytics.system.textPosts": "Только текстовые сообщения", "analytics.system.title": "Статистика системы", "analytics.system.totalBotPosts": "Всего сообщений от ботов", "analytics.system.totalChannels": "Всего каналов", "analytics.system.totalCommands": "Всего комманд", - "analytics.system.totalFilePosts": "Сообщения с файлами", - "analytics.system.totalHashtagPosts": "Сообщения с хештегами", "analytics.system.totalIncomingWebhooks": "Входящие вебхуки", "analytics.system.totalMasterDbConnections": "Подключений к главной БД", "analytics.system.totalOutgoingWebhooks": "Исходящие вебхуки", diff --git a/webapp/channels/src/i18n/sv.json b/webapp/channels/src/i18n/sv.json index 87e087c4a686..60a300e5de38 100644 --- a/webapp/channels/src/i18n/sv.json +++ b/webapp/channels/src/i18n/sv.json @@ -2905,13 +2905,10 @@ "analytics.system.publicChannels": "Publika kanaler", "analytics.system.seatsPurchased": "Licensierade användarkonton", "analytics.system.skippedIntensiveQueries": "För att maximera prestanda så är viss statistik inaktiverad. Du kan aktivera dem i config.json.", - "analytics.system.textPosts": "Meddelanden med endast text", "analytics.system.title": "Systemstatistik", "analytics.system.totalBotPosts": "Totalt antal meddelanden från bottar", "analytics.system.totalChannels": "Totalt antal kanaler", "analytics.system.totalCommands": "Totalt antal kommandon", - "analytics.system.totalFilePosts": "Meddelanden med filer", - "analytics.system.totalHashtagPosts": "Meddelanden med hashtags", "analytics.system.totalIncomingWebhooks": "Inkommande webhooks", "analytics.system.totalMasterDbConnections": "Antal anslutningar till huvuddatabasen", "analytics.system.totalOutgoingWebhooks": "Utgående webhooks", diff --git a/webapp/channels/src/i18n/tr.json b/webapp/channels/src/i18n/tr.json index 4871e7a07d4c..530d806662e6 100644 --- a/webapp/channels/src/i18n/tr.json +++ b/webapp/channels/src/i18n/tr.json @@ -1887,7 +1887,8 @@ "admin.permissions.sysconsole_section_user_management_channels.name": "Kanallar", "admin.permissions.sysconsole_section_user_management_groups.name": "Gruplar", "admin.permissions.sysconsole_section_user_management_permissions.name": "İzinler", - "admin.permissions.sysconsole_section_user_management_system_roles.name": "Sistem rolleri", + "admin.permissions.sysconsole_section_user_management_system_roles.description": "\"Erişilemesin\" seçeneği yalnızca sistem panosu arayüzünü kısıtlar. Temel ürün işlevselliği için tüm kullanıcılar arka plandaki API uç noktalarına salt okunur olarak erişebilir.", + "admin.permissions.sysconsole_section_user_management_system_roles.name": "Atanmış Parçalı Yönetim", "admin.permissions.sysconsole_section_user_management_teams.name": "Takımlar", "admin.permissions.sysconsole_section_user_management_users.description": "Yönetici parolaları sıfırlanamaz", "admin.permissions.sysconsole_section_user_management_users.name": "Kullanıcılar", @@ -2577,9 +2578,11 @@ "admin.system_properties.user_properties.table.actions": "İşlemler", "admin.system_properties.user_properties.table.actions.delete": "Sil", "admin.system_properties.user_properties.table.property": "Özellik", + "admin.system_properties.user_properties.table.property_name.input.name": "Özellik adı", "admin.system_properties.user_properties.table.type": "Tür", "admin.system_properties.user_properties.table.type.text": "Metin", "admin.system_properties.user_properties.table.validation.name_required": "Lütfen bir özellik adı yazın.", + "admin.system_properties.user_properties.table.validation.name_taken": "Özellik adı zaten kullanılmış.", "admin.system_properties.user_properties.table.validation.name_unique": "Özellik adları eşsiz olmalıdır.", "admin.system_properties.user_properties.title": "Kullanıcı özellikleri", "admin.system_roles_feature_discovery.copy": "Özelleştirilebilir yönetici rollerini atayarak, belirli kullanıcılara sistem panosunun belirli bölümlerine okuma ve/veya yazma erişimi verin.", @@ -2905,13 +2908,10 @@ "analytics.system.publicChannels": "Herkese açık kanallar", "analytics.system.seatsPurchased": "Ücreti ödenmiş koltuk sayısı", "analytics.system.skippedIntensiveQueries": "En iyi başarımı elde etmek için bazı istatistikler kapatılmıştır. Bu istatistikleri config.json içinden açabilirsiniz.", - "analytics.system.textPosts": "Yalnızca metin içeren iletiler", "analytics.system.title": "Sistem istatistikleri", "analytics.system.totalBotPosts": "Botların toplam ileti sayısı", "analytics.system.totalChannels": "Toplam kanal", "analytics.system.totalCommands": "Toplam komut", - "analytics.system.totalFilePosts": "Dosya içeren iletiler", - "analytics.system.totalHashtagPosts": "Hashtag içeren iletiler", "analytics.system.totalIncomingWebhooks": "Gelen İnternet kancaları", "analytics.system.totalMasterDbConnections": "Ana veri tabanı bağlantıları", "analytics.system.totalOutgoingWebhooks": "Giden İnternet kancaları", @@ -3282,6 +3282,7 @@ "channel_info_rhs.menu.members": "Üyeler", "channel_info_rhs.menu.notification_preferences": "Bildirim ayarları", "channel_info_rhs.menu.pinned": "Sabitlenmiş iletiler", + "channel_info_rhs.menu.title": "Kanal bilgileri işlemleri", "channel_info_rhs.top_buttons.add_people": "Kişi ekle", "channel_info_rhs.top_buttons.add_people.tooltip": "Bu kanala takım üyeleri ekle", "channel_info_rhs.top_buttons.copied": "Kopyalandı", @@ -4970,6 +4971,7 @@ "search_hint.enter_to_search": "Aramak için Enter tuşuna basın", "search_hint.enter_to_select": "Seçmek için Enter tuşuna basın", "search_hint.filter": "Aramanızı şununla süzün:", + "search_hint.reset_filters": "Farklı bir takım seçtiğiniz için süzgeçleriniz sıfırlandı", "search_item.channelArchived": "Arşivlenmiş", "search_item.direct": "Doğrudan ileti ({username} ile)", "search_item.file_tag.direct_message": "Doğrudan ileti", @@ -4985,6 +4987,9 @@ "search_list_option.on": "Bir tarihteki iletiler", "search_list_option.phrases": "Belirli ifadeler geçen iletiler", "search_results.channel-files-header": "Son kullanılan dosyalar", + "search_teams_selector.all_teams": "Tüm takımlar", + "search_teams_selector.search_teams": "Takım ara", + "search_teams_selector.your_teams": "Takımlarınız", "sectionNotice.dismiss": "Bildirimi yok say", "select_team.icon": "Takım seçme simgesi", "select_team.join.icon": "Takıma katılma simgesi", diff --git a/webapp/channels/src/i18n/uk.json b/webapp/channels/src/i18n/uk.json index 9104f6118b61..e843cf00091f 100644 --- a/webapp/channels/src/i18n/uk.json +++ b/webapp/channels/src/i18n/uk.json @@ -1595,8 +1595,10 @@ "admin.password.preview": "Помилка попереднього перегляду", "admin.password.symbol": "Хоча б один символ (Наприклад: \"~!@#$%^&*()\")", "admin.password.uppercase": "Хоча б одна велика літера", + "admin.permissions.group.convert_private_channel_to_public.description": "Перетворити приватні канали на публічні", + "admin.permissions.group.convert_private_channel_to_public.name": "Перетворити на публічний", "admin.permissions.group.convert_public_channel_to_private.description": "Перетворити публічні канали на приватні", - "admin.permissions.group.convert_public_channel_to_private.name": "Перетворити канали", + "admin.permissions.group.convert_public_channel_to_private.name": "Перетворити на приватний", "admin.permissions.group.custom_groups.description": "Створюйте, редагуйте, видаляйте та керуйте учасниками користувацьких груп.", "admin.permissions.group.custom_groups.name": "Користувацькі групи", "admin.permissions.group.delete_posts.description": "Видалити свої та чужі дописи.", @@ -1656,9 +1658,9 @@ "admin.permissions.permission.assign_system_admin_role.description": "Призначити роль системного адміністратора", "admin.permissions.permission.assign_system_admin_role.name": "Призначити роль системного адміністратора", "admin.permissions.permission.convert_private_channel_to_public.description": "Перетворити приватні канали на публічні", - "admin.permissions.permission.convert_private_channel_to_public.name": "Перетворити канали", + "admin.permissions.permission.convert_private_channel_to_public.name": "Перетворити на публічний", "admin.permissions.permission.convert_public_channel_to_private.description": "Перетворити публічні канали на приватні", - "admin.permissions.permission.convert_public_channel_to_private.name": "Перетворити канали", + "admin.permissions.permission.convert_public_channel_to_private.name": "Перетворити на приватний", "admin.permissions.permission.create_custom_group.description": "Створити власні групи.", "admin.permissions.permission.create_custom_group.name": "Створити", "admin.permissions.permission.create_direct_channel.description": "Створіть приватний канал", @@ -1884,11 +1886,11 @@ "admin.permissions.sysconsole_section_user_management_channels.name": "Канали", "admin.permissions.sysconsole_section_user_management_groups.name": "Групи", "admin.permissions.sysconsole_section_user_management_permissions.name": "Дозволи", - "admin.permissions.sysconsole_section_user_management_system_roles.name": "Системні ролі", + "admin.permissions.sysconsole_section_user_management_system_roles.name": "Делеговане керування ролями системи", "admin.permissions.sysconsole_section_user_management_teams.name": "Команди", "admin.permissions.sysconsole_section_user_management_users.description": "Не вдалось скинути паролі адміністратора", "admin.permissions.sysconsole_section_user_management_users.name": "Користувачі", - "admin.permissions.systemRoles": "Делеговане детальне адміністрування", + "admin.permissions.systemRoles": "Делеговане керування ролями системи", "admin.permissions.systemRolesBannerText": "Керування різними рівнями доступу до системної консолі.", "admin.permissions.systemRolesBannerTitle": "Адміністраторські ролі", "admin.permissions.systemScheme": "Системна схема", @@ -2486,7 +2488,7 @@ "admin.sidebar.siteStatistics": "Статистика сайту", "admin.sidebar.smtp": "SMTP", "admin.sidebar.subscription": "Підписка", - "admin.sidebar.systemRoles": "Делеговане детальне адміністрування", + "admin.sidebar.systemRoles": "Делеговане керування ролями системи", "admin.sidebar.system_properties": "Властивості системи", "admin.sidebar.teamStatistics": "Статистика команди", "admin.sidebar.teams": "Команди", @@ -2609,7 +2611,7 @@ "admin.system_users.filters.status.any": "Будь-який", "admin.system_users.filters.status.deactive": "Деактивовані користувачі", "admin.system_users.filters.status.title": "Статус", - "admin.system_users.filters.team.allTeams": "Всі команди", + "admin.system_users.filters.team.allTeams": "Всі Команди", "admin.system_users.filters.team.errorLoading": "Помилка під час завантаження команд", "admin.system_users.filters.team.errorSearching": "Помилка під час пошуку команд", "admin.system_users.filters.team.noTeams": "Немає команд", @@ -2902,13 +2904,10 @@ "analytics.system.publicChannels": "Публічні Канали", "analytics.system.seatsPurchased": "Ліцензовані місця", "analytics.system.skippedIntensiveQueries": "Для забезпечення максимальної продуктивності, деякі статистичні дані вимкнено. Ви можете увімкнути їх знову у файлі config.json.", - "analytics.system.textPosts": "Дописи лише з текстом", "analytics.system.title": "Статистика системи", "analytics.system.totalBotPosts": "Всього повідомлень від ботів", "analytics.system.totalChannels": "Всього каналів", "analytics.system.totalCommands": "Всього команд", - "analytics.system.totalFilePosts": "Повідомлення з файлами", - "analytics.system.totalHashtagPosts": "Повідомлення з хештегом", "analytics.system.totalIncomingWebhooks": "Вхідні вебхуки", "analytics.system.totalMasterDbConnections": "Підключений до головної БД", "analytics.system.totalOutgoingWebhooks": "Вихідні вебхуки", @@ -4374,8 +4373,8 @@ "more_channels.count": "{count} Результатів", "more_channels.count_one": "1 Результат", "more_channels.count_zero": "0 результатів", - "more_channels.create": "Створити новий Канал", - "more_channels.hide_joined": "Приховати Приєднані", + "more_channels.create": "Створити Новий Канал", + "more_channels.hide_joined": "Приховати приєднані", "more_channels.hide_joined_checked": "Прапорець \"Сховати приєднані канали\", відмічено", "more_channels.hide_joined_not_checked": "Прапорець \"Сховати приєднані канали\", не відмічено", "more_channels.joined": "Приєднався", @@ -4952,6 +4951,9 @@ "search_list_option.on": "Messages on a date", "search_list_option.phrases": "Повідомлення із фразами", "search_results.channel-files-header": "Нещодавні файли", + "search_teams_selector.all_teams": "Всі Команди", + "search_teams_selector.search_teams": "Пошук команд", + "search_teams_selector.your_teams": "Ваші команди", "sectionNotice.dismiss": "Відхилити повідомлення", "select_team.icon": "Виберіть піктограму команди", "select_team.join.icon": "Приєднатися до команди", @@ -5055,6 +5057,8 @@ "sidebar.sortedByRecencyLabel": "Нещодавня активність", "sidebar.sortedManually": "Вручну", "sidebar.team_menu.button.plusIcon": "Піктограма Плюс", + "sidebar.team_menu.button.teamImage": "Зображення команди {teamName}", + "sidebar.team_menu.button.teamInitials": "Ініціали команди {teamName}", "sidebar.team_menu.menuAriaLabel": "меню команди", "sidebar.team_select": "{siteName} - Приєднатися до команди", "sidebar.types.channels": "КАНАЛИ", @@ -5062,6 +5066,14 @@ "sidebar.types.favorites": "ОБРАНІ", "sidebar.types.unreads": "НЕПРОЧИТАНІ", "sidebar.unreads": "Більше непрочитаних", + "sidebarLeft.browserOrCreateChannelMenu.browseChannelsMenuItem.primaryLabel": "Перегляд каналів", + "sidebarLeft.browserOrCreateChannelMenu.createCategoryMenuItem.primaryLabel": "Створити нову Категорію", + "sidebarLeft.browserOrCreateChannelMenu.createNewChannelMenuItem.primaryLabel": "Створити новий канал", + "sidebarLeft.browserOrCreateChannelMenu.createUserGroupMenuItem.primaryLabel": "Створити нову групу користувачів", + "sidebarLeft.browserOrCreateChannelMenu.invitePeopleMenuItem.primaryLabel": "Запросити людей", + "sidebarLeft.browserOrCreateChannelMenu.invitePeopleMenuItem.secondaryLabel": "Додати людей до команди", + "sidebarLeft.browserOrCreateChannelMenu.openDirectMessageMenuItem.primaryLabel": "Відкрити приватне повідомлення", + "sidebarLeft.browserOrCreateChannelMenuButton.label": "Перегляд або створення каналів", "sidebar_left.addChannelsCta": "Додати канали", "sidebar_left.add_channel_dropdown.browseChannels": "Перегляд каналів", "sidebar_left.add_channel_dropdown.createNewChannel": "Створити новий канал", @@ -5103,7 +5115,10 @@ "sidebar_left.sidebar_channel_modal.confirmation_text": "Перетворити на приватний канал", "sidebar_left.sidebar_channel_modal.header": "Перетворити на Приватний канал", "sidebar_left.sidebar_channel_modal.no_common_teams_error.body": "Групове повідомлення не може бути перетворене на канал, оскільки учасники не є членами однієї команди. Додайте всіх учасників до однієї команди, щоб перетворити це групове повідомлення на канал у цій команді.", + "sidebar_left.sidebar_channel_modal.select_team_placeholder": "Виберіть Команду", + "sidebar_left.sidebar_channel_modal.warning_body": "Ви збираєтеся перетворити групове повідомлення з {memberNames} на канал. Цю дію не можна скасувати.", "sidebar_left.sidebar_channel_modal.warning_body_yourself": "себе", + "sidebar_left.sidebar_channel_modal.warning_header": "Історію розмов будуть бачити всі учасники каналу", "sidebar_left.sidebar_channel_navigator.addChannelsCta": "Додати канали", "sidebar_left.sidebar_channel_navigator.inviteUsers": "Запросити користувачів", "sidebar_right_menu.console": "Системна консоль", @@ -5116,17 +5131,25 @@ "signup_team.no_open_teams": "Немає команд доступних для входу. Будь-ласка, попросіть свого адміністратора запросити Вас.", "signup_team.no_open_teams_canCreate": "Немає доступних команд для входу. Створіть нову команду або попросіть свого адміністратора запросити Вас.", "signup_team_system_console": "Перейти в системну консоль", + "signup_user_completed.cardtitle": "Створіть свій обліковий запис", + "signup_user_completed.cardtitle.external": "Створіть свій обліковий запис одним із наведених нижче способів:", "signup_user_completed.chooseUser": "Виберіть ім'я користувача", "signup_user_completed.create": "Створити обліковий запис", "signup_user_completed.emailIs": "Ви будете використовувати цю адресу для входу на {siteName}.", "signup_user_completed.emailLabel": "Адреса електронної пошти", "signup_user_completed.failed_update_user_state": "Будь-ласка, очистіть кеш і спробуйте увійти.", "signup_user_completed.haveAccount": "Вже є обліковий запис?", + "signup_user_completed.invalid_invite.message": "Щоб отримати запрошення, зверніться до вашого адміністратора.", + "signup_user_completed.invalid_invite.title": "Це посилання є недійсним", + "signup_user_completed.no_open_server.title": "Цей сервер не дозволяє відкриті реєстрації", + "signup_user_completed.or": "або створити обліковий запис у", "signup_user_completed.required": "Це поле обов'язкове для заповнення", "signup_user_completed.reserved": "Це ім'я користувача зарезервовано, будь ласка, оберіть інше.", "signup_user_completed.return": "Повернутися до входу в систему", "signup_user_completed.saving": "Створення облікового запису…", "signup_user_completed.signIn": "Авторизуватися", + "signup_user_completed.subtitle": "Створіть свій обліковий запис Mattermost, щоб почати співпрацювати зі своєю командою", + "signup_user_completed.title": "Давайте почнемо", "signup_user_completed.userHelp": "Ви можете використовувати малі літери, цифри, крапки, тире та підкреслення.", "signup_user_completed.usernameLength": "Імена користувачів повинні починатися з малої літери і мати довжину {min}-{max} символів. Ви можете використовувати малі літери, цифри, крапки, тире та підкреслення.", "signup_user_completed.validEmail": "Будь ласка, введіть дійсну адресу електронної пошти", @@ -5135,6 +5158,7 @@ "single_image_view.download_tooltip": "Завантажити", "slash_commands.header": "Швидкі команди", "someting.string": "defaultString", + "start_cloud_trial.modal.enter_trial_email.input.label": "Введіть робочу електронну пошту", "start_cloud_trial.modal.enter_trial_email.input.placeholder": "name@companyname.com", "start_trial.modal.failed": "Не вдалося", "start_trial.modal.gettingTrial": "Отримання пробної версії...", diff --git a/webapp/channels/src/i18n/vi.json b/webapp/channels/src/i18n/vi.json index 850e11e8ff9e..14dfe54bbbfc 100644 --- a/webapp/channels/src/i18n/vi.json +++ b/webapp/channels/src/i18n/vi.json @@ -2369,13 +2369,10 @@ "analytics.system.publicChannels": "Kênh Công khai", "analytics.system.seatsPurchased": "Tổng số người dùng trả phí", "analytics.system.skippedIntensiveQueries": "Để tối đa hóa hiệu suất, một số thống kê đã bị vô hiệu hóa. Bạn có thể [bật lại chúng trong config.json] (! https://docs.mattermost.com/administration/stosystem.html).", - "analytics.system.textPosts": "Bài đăng chỉ có văn bản", "analytics.system.title": "Thống kê hệ thống", "analytics.system.totalBotPosts": "Tổng số bài đăng từ Bots", "analytics.system.totalChannels": "Tổng số kênh", "analytics.system.totalCommands": "Tổng số lệnh", - "analytics.system.totalFilePosts": "Bài đăng có tệp", - "analytics.system.totalHashtagPosts": "Các bài đăng có Hashtags", "analytics.system.totalIncomingWebhooks": "Webhooks đến", "analytics.system.totalMasterDbConnections": "Master DB Conns", "analytics.system.totalOutgoingWebhooks": "Webhook đi", diff --git a/webapp/channels/src/i18n/zh-CN.json b/webapp/channels/src/i18n/zh-CN.json index 9b18ab056ff5..f43e82f2bc53 100644 --- a/webapp/channels/src/i18n/zh-CN.json +++ b/webapp/channels/src/i18n/zh-CN.json @@ -2902,13 +2902,10 @@ "analytics.system.publicChannels": "公共频道", "analytics.system.seatsPurchased": "许可席位", "analytics.system.skippedIntensiveQueries": "为了最大性能,部分统计已停用。您可以在 config.json 重新开启。", - "analytics.system.textPosts": "纯文字消息", "analytics.system.title": "系统统计", "analytics.system.totalBotPosts": "机器人总消息数", "analytics.system.totalChannels": "频道总数", "analytics.system.totalCommands": "命令总数", - "analytics.system.totalFilePosts": "有文件的发文", - "analytics.system.totalHashtagPosts": "有标签的发文", "analytics.system.totalIncomingWebhooks": "传入 Webhook", "analytics.system.totalMasterDbConnections": "父数据库连接数", "analytics.system.totalOutgoingWebhooks": "传出 Webhook", diff --git a/webapp/channels/src/i18n/zh-TW.json b/webapp/channels/src/i18n/zh-TW.json index e338b7646792..771dba8490bf 100644 --- a/webapp/channels/src/i18n/zh-TW.json +++ b/webapp/channels/src/i18n/zh-TW.json @@ -2862,13 +2862,10 @@ "analytics.system.publicChannels": "公開頻道數", "analytics.system.seatsPurchased": "已擴充的員額", "analytics.system.skippedIntensiveQueries": "為了效能,部份統計已被停用。可在 config.json 中重新啟用。", - "analytics.system.textPosts": "以純文字傳訊", "analytics.system.title": "系統統計", "analytics.system.totalBotPosts": "來自機器人的訊息總數", "analytics.system.totalChannels": "頻道總數", "analytics.system.totalCommands": "全部命令", - "analytics.system.totalFilePosts": "包含檔案的訊息", - "analytics.system.totalHashtagPosts": "包含 # 標籤的訊息", "analytics.system.totalIncomingWebhooks": "送入的 Webhook", "analytics.system.totalMasterDbConnections": "主資料庫連線", "analytics.system.totalOutgoingWebhooks": "寄出的 Webhook", From 169274b3aa4f02701c0cbb78b0894f2317007ccc Mon Sep 17 00:00:00 2001 From: enzowritescode <1328683+enzowritescode@users.noreply.github.com> Date: Mon, 10 Feb 2025 22:48:01 -0700 Subject: [PATCH 027/164] [MM-62864] Bump katex to 0.16.21 (#30095) --- webapp/channels/package.json | 2 +- webapp/package-lock.json | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/webapp/channels/package.json b/webapp/channels/package.json index 9e8a762af52a..291aeb2a5f03 100644 --- a/webapp/channels/package.json +++ b/webapp/channels/package.json @@ -45,7 +45,7 @@ "html-to-react": "1.6.0", "inobounce": "0.2.1", "ipaddr.js": "2.1.0", - "katex": "0.16.10", + "katex": "0.16.21", "localforage": "1.10.0", "localforage-observable": "2.1.1", "lodash": "4.17.21", diff --git a/webapp/package-lock.json b/webapp/package-lock.json index 5bb720e34066..59ac666cebfd 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -98,7 +98,7 @@ "html-to-react": "1.6.0", "inobounce": "0.2.1", "ipaddr.js": "2.1.0", - "katex": "0.16.10", + "katex": "0.16.21", "localforage": "1.10.0", "localforage-observable": "2.1.1", "lodash": "4.17.21", @@ -20268,9 +20268,9 @@ } }, "node_modules/katex": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.10.tgz", - "integrity": "sha512-ZiqaC04tp2O5utMsl2TEZTXxa6WSC4yo0fv5ML++D3QZv/vx2Mct0mTlRx3O+uUkjfuAgOkzsCmq5MiUEsDDdA==", + "version": "0.16.21", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.21.tgz", + "integrity": "sha512-XvqR7FgOHtWupfMiigNzmh+MgUVmDGU2kXZm899ZkPfcuoPuFxyHmXsgATDpFZDAXCI8tvinaVcDo8PIIJSo4A==", "funding": [ "https://opencollective.com/katex", "https://github.com/sponsors/katex" @@ -28204,6 +28204,7 @@ "typescript": "^5.0.0" }, "peerDependencies": { + "@mattermost/types": "^9.3.0 || ^10.0.0", "typescript": "^4.3.0 || ^5.0.0" }, "peerDependenciesMeta": { From b2b956c043d8122f631c562a7f33f8e9ee02c9ff Mon Sep 17 00:00:00 2001 From: ayush-chauhan233 Date: Tue, 11 Feb 2025 11:34:42 +0530 Subject: [PATCH 028/164] [MM-55278]: Fixed autofocus on submenu's first element (#29547) --- .../channels/intro_channel.spec.ts | 8 +-- .../dot_menu/post_reminder_submenu.tsx | 15 +++-- webapp/channels/src/components/menu/menu.scss | 5 +- .../src/components/menu/menu.test.tsx | 2 +- .../src/components/menu/menu_item.tsx | 1 + .../channels/src/components/menu/sub_menu.tsx | 59 ++++++++++--------- .../user_account_name_menuitem.test.tsx.snap | 2 +- .../user_account_dnd_menuitem.tsx | 23 ++++---- 8 files changed, 61 insertions(+), 54 deletions(-) diff --git a/e2e-tests/playwright/tests/accessibility/channels/intro_channel.spec.ts b/e2e-tests/playwright/tests/accessibility/channels/intro_channel.spec.ts index 57eda5243123..6b0cdff2bbc4 100644 --- a/e2e-tests/playwright/tests/accessibility/channels/intro_channel.spec.ts +++ b/e2e-tests/playwright/tests/accessibility/channels/intro_channel.spec.ts @@ -129,12 +129,10 @@ test('Post actions tab support', async ({pw, axe}) => { // # Press arrow right await channelsPage.postDotMenu.remindMenuItem.press('ArrowRight'); - // * Reminder menu should be visible and have focused - channelsPage.postReminderMenu.toBeVisible(); - await expect(channelsPage.postReminderMenu.container).toBeFocused(); + // * Reminder menu should be visible + expect(channelsPage.postReminderMenu.container).toBeVisible(); - // * Should move focus to 30 mins after arrow down - await channelsPage.postReminderMenu.container.press('ArrowDown'); + // * Should have focus on 30 mins after submenu opens expect(await channelsPage.postReminderMenu.thirtyMinsMenuItem).toBeFocused(); // * Should move focus to 1 hour after arrow down diff --git a/webapp/channels/src/components/dot_menu/post_reminder_submenu.tsx b/webapp/channels/src/components/dot_menu/post_reminder_submenu.tsx index 6eb08fc94e28..634dfcc2a7bc 100644 --- a/webapp/channels/src/components/dot_menu/post_reminder_submenu.tsx +++ b/webapp/channels/src/components/dot_menu/post_reminder_submenu.tsx @@ -159,13 +159,16 @@ function PostReminderSubmenu(props: Props) { leadingElement={} trailingElements={} menuId={`remind_post_${props.post.id}-menu`} + subMenuHeader={ +
+ {formatMessage( + { + id: 'post_info.post_reminder.sub_menu.header', + defaultMessage: 'Set a reminder for:', + }, + )} +
} > -
- {formatMessage( - {id: 'post_info.post_reminder.sub_menu.header', - defaultMessage: 'Set a reminder for:'}, - )} -
{postReminderSubMenuItems} ); diff --git a/webapp/channels/src/components/menu/menu.scss b/webapp/channels/src/components/menu/menu.scss index f8934f023600..53de4230192c 100644 --- a/webapp/channels/src/components/menu/menu.scss +++ b/webapp/channels/src/components/menu/menu.scss @@ -7,12 +7,13 @@ min-width: 114px; max-width: 496px; max-height: 80vh; + padding: 4px 0; background-color: var(--center-channel-bg); - box-shadow: var(--elevation-4); + box-shadow: var(--elevation-4), 0 0 0 1px rgba(var(--center-channel-color-rgb), 0.12) inset; } &.AsSubMenu { & .MuiPaper-root { - box-shadow: var(--elevation-5); + box-shadow: var(--elevation-5), 0 0 0 1px rgba(var(--center-channel-color-rgb), 0.12) inset; } } } diff --git a/webapp/channels/src/components/menu/menu.test.tsx b/webapp/channels/src/components/menu/menu.test.tsx index b0b06108ebfb..4b27c7e91634 100644 --- a/webapp/channels/src/components/menu/menu.test.tsx +++ b/webapp/channels/src/components/menu/menu.test.tsx @@ -140,7 +140,7 @@ describe('menu click handlers', () => { expect(screen.getByText('Open modal from submenu')).toBeInTheDocument(); // Press the down arrow once to focus first submenu item and then twice more to select the one we want - userEvent.keyboard('{arrowdown}{arrowdown}{arrowdown}'); + userEvent.keyboard('{arrowdown}{arrowdown}'); expect(screen.getByText('Open modal from submenu').closest('li')).toHaveFocus(); diff --git a/webapp/channels/src/components/menu/menu_item.tsx b/webapp/channels/src/components/menu/menu_item.tsx index f0a9e909d4d3..03004e3c9597 100644 --- a/webapp/channels/src/components/menu/menu_item.tsx +++ b/webapp/channels/src/components/menu/menu_item.tsx @@ -309,6 +309,7 @@ export const MenuItemStyled = styled(MuiMenuItem, { flexWrap: 'nowrap', justifyContent: 'flex-end', color: isRegular ? 'rgba(var(--center-channel-color-rgb), 0.75)' : 'var(--error-text)', + marginInlineStart: '24px', gap: '4px', fontSize: '12px', lineHeight: '16px', diff --git a/webapp/channels/src/components/menu/sub_menu.tsx b/webapp/channels/src/components/menu/sub_menu.tsx index a54111f5ead6..1fb001f43f39 100644 --- a/webapp/channels/src/components/menu/sub_menu.tsx +++ b/webapp/channels/src/components/menu/sub_menu.tsx @@ -1,8 +1,8 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import MuiMenu from '@mui/material/Menu'; import MuiMenuList from '@mui/material/MenuList'; +import MuiPopover from '@mui/material/Popover'; import type {PopoverOrigin} from '@mui/material/Popover'; import React, { useState, @@ -33,6 +33,8 @@ import {SubMenuContext, useMenuContextValue} from './menu_context'; import {MenuItem} from './menu_item'; import type {Props as MenuItemProps} from './menu_item'; +import './menu.scss'; + interface Props { id: MenuItemProps['id']; leadingElement?: MenuItemProps['leadingElement']; @@ -47,6 +49,7 @@ interface Props { menuAriaDescribedBy?: string; forceOpenOnLeft?: boolean; // Most of the times this is not needed, since submenu position is calculated and placed + subMenuHeader?: ReactNode; children: ReactNode; } @@ -63,6 +66,7 @@ export function SubMenu(props: Props) { menuAriaDescribedBy, forceOpenOnLeft, children, + subMenuHeader, ...rest } = props; @@ -136,6 +140,7 @@ export function SubMenu(props: Props) { menuId, menuAriaLabel, children, + subMenuHeader, }, })); } @@ -166,35 +171,30 @@ export function SubMenu(props: Props) { onMouseLeave={handleMouseLeave} onKeyDown={handleKeyDown} > - - {/* This component is needed here to re enable pointer events for the submenu items which we had to disable above as */} - {/* pointer turns to default as soon as it leaves the parent menu */} - {/* Notice we dont use the below component in menu.tsx */} - + - + {subMenuHeader} + {children} - - - + + + ); } @@ -203,6 +203,7 @@ interface SubMenuModalProps { menuId: Props['menuId']; menuAriaLabel?: Props['menuAriaLabel']; children: Props['children']; + subMenuHeader?: ReactNode; } function SubMenuModal(props: SubMenuModalProps) { @@ -224,9 +225,11 @@ function SubMenuModal(props: SubMenuModalProps) { className='menuModal' > + {props.subMenuHeader} {props.children} diff --git a/webapp/channels/src/components/user_account_menu/__snapshots__/user_account_name_menuitem.test.tsx.snap b/webapp/channels/src/components/user_account_menu/__snapshots__/user_account_name_menuitem.test.tsx.snap index 503dd88dda67..a2cb92e893cf 100644 --- a/webapp/channels/src/components/user_account_menu/__snapshots__/user_account_name_menuitem.test.tsx.snap +++ b/webapp/channels/src/components/user_account_menu/__snapshots__/user_account_name_menuitem.test.tsx.snap @@ -4,7 +4,7 @@ exports[`UserAccountNameMenuItem should not break if no props are passed 1`] = `
); @@ -278,7 +283,6 @@ const PostOptions = (props: Props): JSX.Element => { } else if (!props.isPostBeingEdited) { options = (
    diff --git a/webapp/channels/src/components/post_view/post_reaction/__snapshots__/post_reaction.test.tsx.snap b/webapp/channels/src/components/post_view/post_reaction/__snapshots__/post_reaction.test.tsx.snap deleted file mode 100644 index ff24080843ab..000000000000 --- a/webapp/channels/src/components/post_view/post_reaction/__snapshots__/post_reaction.test.tsx.snap +++ /dev/null @@ -1,41 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`components/post_view/PostReaction should match snapshot 1`] = ` - - - - - - -`; diff --git a/webapp/channels/src/components/post_view/post_reaction/post_reaction.test.tsx b/webapp/channels/src/components/post_view/post_reaction/post_reaction.test.tsx index 742aa7034cef..7331bd6c89b5 100644 --- a/webapp/channels/src/components/post_view/post_reaction/post_reaction.test.tsx +++ b/webapp/channels/src/components/post_view/post_reaction/post_reaction.test.tsx @@ -3,10 +3,12 @@ import React from 'react'; -import {shallowWithIntl} from 'tests/helpers/intl-test-helper'; +import {Permissions} from 'mattermost-redux/constants'; + +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import {TestHelper} from 'utils/test_helper'; -import PostReaction, {type PostReaction as PostReactionComponent} from './post_reaction'; +import PostReaction from './post_reaction'; describe('components/post_view/PostReaction', () => { const baseProps = { @@ -16,24 +18,62 @@ describe('components/post_view/PostReaction', () => { getDotMenuRef: jest.fn(), showIcon: false, showEmojiPicker: false, - toggleEmojiPicker: jest.fn(), + setShowEmojiPicker: jest.fn(), actions: { toggleReaction: jest.fn(), }, }; - test('should match snapshot', () => { - const wrapper = shallowWithIntl(); - expect(wrapper).toMatchSnapshot(); + const userId = 'userId'; + const initialState = { + entities: { + roles: { + roles: { + system_user: TestHelper.getRoleMock({permissions: [Permissions.ADD_REACTION]}), + }, + }, + users: { + currentUserId: userId, + profiles: { + userId: TestHelper.getUserMock({id: userId, roles: 'system_user'}), + }, + }, + }, + }; + + test('should not render the emoji picker initially', async () => { + const {rerender} = renderWithContext( + , + initialState, + ); + + expect(screen.queryByPlaceholderText('Search emojis')).not.toBeInTheDocument(); + + await Promise.resolve(); + + rerender( + , + ); + + expect(screen.queryByPlaceholderText('Search emojis')).toBeInTheDocument(); }); - test('should call toggleReaction and toggleEmojiPicker on handleToggleEmoji', () => { - const wrapper = shallowWithIntl(); - const instance = wrapper.instance() as PostReactionComponent; + test('should toggle the reaction and close the emoji picker when an emoji is selected', async () => { + renderWithContext( + , + initialState, + ); + + userEvent.type(screen.getByPlaceholderText('Search emojis'), '{enter}'); - instance.handleToggleEmoji(TestHelper.getCustomEmojiMock({name: 'smile'})); expect(baseProps.actions.toggleReaction).toHaveBeenCalledTimes(1); - expect(baseProps.actions.toggleReaction).toHaveBeenCalledWith('post_id_1', 'smile'); - expect(baseProps.toggleEmojiPicker).toHaveBeenCalledTimes(1); + expect(baseProps.actions.toggleReaction).toHaveBeenCalledWith('post_id_1', 'grinning'); + expect(baseProps.setShowEmojiPicker).toHaveBeenCalledWith(false); }); }); diff --git a/webapp/channels/src/components/post_view/post_reaction/post_reaction.tsx b/webapp/channels/src/components/post_view/post_reaction/post_reaction.tsx index 354d7492ccc6..7adb372ce4d8 100644 --- a/webapp/channels/src/components/post_view/post_reaction/post_reaction.tsx +++ b/webapp/channels/src/components/post_view/post_reaction/post_reaction.tsx @@ -2,114 +2,87 @@ // See LICENSE.txt for license information. import classNames from 'classnames'; -import React from 'react'; -import type {WrappedComponentProps} from 'react-intl'; -import {defineMessages, injectIntl} from 'react-intl'; +import React, {useCallback} from 'react'; +import {useIntl} from 'react-intl'; import type {Emoji} from '@mattermost/types/emojis'; import Permissions from 'mattermost-redux/constants/permissions'; import {getEmojiName} from 'mattermost-redux/utils/emoji_utils'; -import EmojiPickerOverlay from 'components/emoji_picker/emoji_picker_overlay'; +import useEmojiPicker from 'components/emoji_picker/use_emoji_picker'; import ChannelPermissionGate from 'components/permissions_gates/channel_permission_gate'; import EmojiIcon from 'components/widgets/icons/emoji_icon'; import WithTooltip from 'components/with_tooltip'; import {Locations} from 'utils/constants'; -const TOP_OFFSET = -7; - -const messages = defineMessages({ - addReaction: { - id: 'post_info.tooltip.add_reactions', - defaultMessage: 'Add Reaction', - }, -}); - -export type Props = WrappedComponentProps & { +export type Props = { channelId?: string; postId: string; teamId: string; - getDotMenuRef: () => HTMLUListElement | null; location?: keyof typeof Locations; + setShowEmojiPicker: (showEmojiPicker: boolean) => void; showEmojiPicker: boolean; - toggleEmojiPicker: (e?: React.MouseEvent) => void; actions: { toggleReaction: (postId: string, emojiName: string) => void; }; } -type State = { - location: keyof typeof Locations; - showEmojiPicker: boolean; -} - -export class PostReaction extends React.PureComponent { - public static defaultProps: Partial = { - location: Locations.CENTER as 'CENTER', - showEmojiPicker: false, - }; +export default function PostReaction({ + channelId, + location = Locations.CENTER, + postId, + teamId, + showEmojiPicker, + setShowEmojiPicker, + actions: { + toggleReaction, + }, +}: Props) { + const intl = useIntl(); - handleToggleEmoji = (emoji: Emoji): void => { - this.setState({showEmojiPicker: false}); + const handleEmojiClick = useCallback((emoji: Emoji) => { const emojiName = getEmojiName(emoji); - this.props.actions.toggleReaction(this.props.postId, emojiName); - this.props.toggleEmojiPicker(); - }; + toggleReaction(postId, emojiName); - render() { - const { - channelId, - location, - postId, - showEmojiPicker, - teamId, - intl, - } = this.props; + setShowEmojiPicker(false); + }, [postId, setShowEmojiPicker, toggleReaction]); - let spaceRequiredAbove; - let spaceRequiredBelow; - if (location === Locations.RHS_ROOT || location === Locations.RHS_COMMENT) { - spaceRequiredAbove = EmojiPickerOverlay.RHS_SPACE_REQUIRED_ABOVE; - spaceRequiredBelow = EmojiPickerOverlay.RHS_SPACE_REQUIRED_BELOW; - } + const { + emojiPicker, + getReferenceProps, + setReference, + } = useEmojiPicker({ + showEmojiPicker, + setShowEmojiPicker, - return ( - - <> - - - - - - - ); - } -} + onEmojiClick: handleEmojiClick, + }); + + const ariaLabel = intl.formatMessage({id: 'post_info.tooltip.add_reactions', defaultMessage: 'Add Reaction'}); -export default injectIntl(PostReaction); + return ( + + + + + {emojiPicker} + + ); +} diff --git a/webapp/channels/src/components/post_view/reaction/reaction.scss b/webapp/channels/src/components/post_view/reaction/reaction.scss index 052718b81f9e..ab15cbb471c3 100644 --- a/webapp/channels/src/components/post_view/reaction/reaction.scss +++ b/webapp/channels/src/components/post_view/reaction/reaction.scss @@ -56,11 +56,19 @@ cursor: default; } - &__add { + &.Reaction__add { position: relative; font-size: 20px; line-height: 0; vertical-align: middle; + + &.Reaction__add--open { + background-color: rgba(var(--button-bg-rgb), 0.08); + color: functions.v(button-bg); + fill: functions.v(button-bg); + opacity: 1; + visibility: visible; + } } &__emoji { diff --git a/webapp/channels/src/components/post_view/reaction_list/__snapshots__/reactions_list.test.tsx.snap b/webapp/channels/src/components/post_view/reaction_list/__snapshots__/reactions_list.test.tsx.snap index ebf9f8e5438d..c8a60129541c 100644 --- a/webapp/channels/src/components/post_view/reaction_list/__snapshots__/reactions_list.test.tsx.snap +++ b/webapp/channels/src/components/post_view/reaction_list/__snapshots__/reactions_list.test.tsx.snap @@ -48,52 +48,36 @@ exports[`components/ReactionList should render when there are reactions 1`] = ` ] } /> -
    - - - - - - - - -
    +
`; diff --git a/webapp/channels/src/components/post_view/reaction_list/add_reaction_button.test.tsx b/webapp/channels/src/components/post_view/reaction_list/add_reaction_button.test.tsx new file mode 100644 index 000000000000..4518e8cb41b0 --- /dev/null +++ b/webapp/channels/src/components/post_view/reaction_list/add_reaction_button.test.tsx @@ -0,0 +1,56 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; + +import {Permissions} from 'mattermost-redux/constants'; + +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; +import {TestHelper} from 'utils/test_helper'; + +import AddReactionButton from './add_reaction_button'; + +describe('AddReactionButton', () => { + const userId = 'userId'; + + const initialState = { + entities: { + roles: { + roles: { + system_user: TestHelper.getRoleMock({permissions: [Permissions.ADD_REACTION]}), + }, + }, + users: { + currentUserId: userId, + profiles: { + userId: TestHelper.getUserMock({id: userId, roles: 'system_user'}), + }, + }, + }, + }; + + test('should show emoji picker when clicked and then close it when an emoji is selected', async () => { + const props = { + post: TestHelper.getPostMock({user_id: userId, channel_id: 'channelId'}), + teamId: 'teamId', + onEmojiClick: jest.fn(), + }; + + renderWithContext( + , + initialState, + ); + + expect(screen.queryByText('Emoji Picker')).not.toBeInTheDocument(); + + userEvent.click(screen.getByLabelText('Add a reaction')); + + expect(screen.queryByText('Emoji Picker')).toBeVisible(); + + // Search for an emoji instead of clicking on one because the emoji picker doesn't render items when testing + userEvent.type(screen.getByPlaceholderText('Search emojis'), 'banana{enter}'); + + expect(props.onEmojiClick).toHaveBeenCalledWith(expect.objectContaining({short_name: 'banana'})); + expect(screen.queryByText('Emoji Picker')).not.toBeInTheDocument(); + }); +}); diff --git a/webapp/channels/src/components/post_view/reaction_list/add_reaction_button.tsx b/webapp/channels/src/components/post_view/reaction_list/add_reaction_button.tsx new file mode 100644 index 000000000000..3d289a883b3c --- /dev/null +++ b/webapp/channels/src/components/post_view/reaction_list/add_reaction_button.tsx @@ -0,0 +1,77 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import classNames from 'classnames'; +import React, {useCallback, useState} from 'react'; +import {useIntl} from 'react-intl'; + +import type {Emoji} from '@mattermost/types/emojis'; +import type {Post} from '@mattermost/types/posts'; + +import {Permissions} from 'mattermost-redux/constants'; + +import useEmojiPicker from 'components/emoji_picker/use_emoji_picker'; +import ChannelPermissionGate from 'components/permissions_gates/channel_permission_gate'; +import AddReactionIcon from 'components/widgets/icons/add_reaction_icon'; +import WithTooltip from 'components/with_tooltip'; + +type Props = { + post: Post; + teamId: string; + + onEmojiClick: (emoji: Emoji) => void; +} + +export default function AddReactionButton({ + post, + teamId, + + onEmojiClick, +}: Props) { + const intl = useIntl(); + + const [showEmojiPicker, setShowEmojiPicker] = useState(false); + + const handleEmojiClick = useCallback((emoji: Emoji) => { + onEmojiClick(emoji); + setShowEmojiPicker(false); + }, [onEmojiClick]); + + const { + emojiPicker, + getReferenceProps, + setReference, + } = useEmojiPicker({ + showEmojiPicker, + setShowEmojiPicker, + + onEmojiClick: handleEmojiClick, + }); + + const ariaLabel = intl.formatMessage({id: 'reaction.add.ariaLabel', defaultMessage: 'Add a reaction'}); + + return ( + + + + + + + {emojiPicker} + + ); +} diff --git a/webapp/channels/src/components/post_view/reaction_list/reaction_list.tsx b/webapp/channels/src/components/post_view/reaction_list/reaction_list.tsx index 8e4d3a8cf5cc..2c9b58727393 100644 --- a/webapp/channels/src/components/post_view/reaction_list/reaction_list.tsx +++ b/webapp/channels/src/components/post_view/reaction_list/reaction_list.tsx @@ -2,32 +2,18 @@ // See LICENSE.txt for license information. import React from 'react'; -import {defineMessages} from 'react-intl'; import type {Emoji} from '@mattermost/types/emojis'; import type {Post} from '@mattermost/types/posts'; import type {Reaction as ReactionType} from '@mattermost/types/reactions'; -import Permissions from 'mattermost-redux/constants/permissions'; import {getEmojiName} from 'mattermost-redux/utils/emoji_utils'; -import EmojiPickerOverlay from 'components/emoji_picker/emoji_picker_overlay'; -import ChannelPermissionGate from 'components/permissions_gates/channel_permission_gate'; import Reaction from 'components/post_view/reaction'; -import AddReactionIcon from 'components/widgets/icons/add_reaction_icon'; -import WithTooltip from 'components/with_tooltip'; import {localizeMessage} from 'utils/utils'; -const DEFAULT_EMOJI_PICKER_RIGHT_OFFSET = 15; -const EMOJI_PICKER_WIDTH_OFFSET = 260; - -const messages = defineMessages({ - addAReaction: { - id: 'reaction_list.addReactionTooltip', - defaultMessage: 'Add a reaction', - }, -}); +import AddReactionButton from './add_reaction_button'; type Props = { @@ -62,18 +48,14 @@ type Props = { type State = { emojiNames: string[]; - showEmojiPicker: boolean; }; export default class ReactionList extends React.PureComponent { - private addReactionButtonRef = React.createRef(); - constructor(props: Props) { super(props); this.state = { emojiNames: [], - showEmojiPicker: false, }; } @@ -89,25 +71,11 @@ export default class ReactionList extends React.PureComponent { return (emojiNames === state.emojiNames) ? null : {emojiNames}; } - getTarget = (): HTMLButtonElement | null => { - return this.addReactionButtonRef.current; - }; - handleEmojiClick = (emoji: Emoji): void => { - this.setState({showEmojiPicker: false}); const emojiName = getEmojiName(emoji); this.props.actions.toggleReaction(this.props.post.id, emojiName); }; - hideEmojiPicker = (): void => { - this.setState({showEmojiPicker: false}); - }; - - toggleEmojiPicker = (e?: React.MouseEvent): void => { - e?.stopPropagation(); - this.setState({showEmojiPicker: !this.state.showEmojiPicker}); - }; - render(): React.ReactNode { const reactionsByName = new Map(); @@ -141,69 +109,24 @@ export default class ReactionList extends React.PureComponent { return null; }); - const addReactionButton = this.getTarget(); - let rightOffset = DEFAULT_EMOJI_PICKER_RIGHT_OFFSET; - if (addReactionButton) { - rightOffset = window.innerWidth - addReactionButton.getBoundingClientRect().right - EMOJI_PICKER_WIDTH_OFFSET; - - if (rightOffset < 0) { - rightOffset = DEFAULT_EMOJI_PICKER_RIGHT_OFFSET; - } - } - - let emojiPicker = null; + let addReaction = null; if (this.props.canAddReactions) { - emojiPicker = ( - - - - - - - - + addReaction = ( + ); } - let addReactionClassName = 'post-add-reaction'; - if (this.state.showEmojiPicker) { - addReactionClassName += ' post-add-reaction-emoji-picker-open'; - } - return (
{reactions} -
- {emojiPicker} -
+ {addReaction}
); } diff --git a/webapp/channels/src/i18n/en.json b/webapp/channels/src/i18n/en.json index bab9930e8ba0..be24590edac9 100644 --- a/webapp/channels/src/i18n/en.json +++ b/webapp/channels/src/i18n/en.json @@ -4835,7 +4835,6 @@ "reaction_limit_reached_modal.body": "Oops! It looks like we've hit a ceiling on emoji reactions for this message. Please contact your system administrator for any adjustments to this limit.", "reaction_limit_reached_modal.body.admin": "Oops! It looks like we've hit a ceiling on emoji reactions for this message. We've set a limit to keep things running smoothly on your server. As a system administrator, you can adjust this limit from the system console.", "reaction_limit_reached_modal.title": "You've reached the reaction limit", - "reaction_list.addReactionTooltip": "Add a reaction", "reaction.add.ariaLabel": "Add a reaction", "reaction.clickToAdd": "(click to add)", "reaction.clickToRemove": "(click to remove)", diff --git a/webapp/channels/src/sass/components/_emoji-picker-tabs.scss b/webapp/channels/src/sass/components/_emoji-picker-tabs.scss index 13d0f592e5ca..3cf8416b9172 100644 --- a/webapp/channels/src/sass/components/_emoji-picker-tabs.scss +++ b/webapp/channels/src/sass/components/_emoji-picker-tabs.scss @@ -1,3 +1,5 @@ +@use "utils/variables"; + .emoji-picker { pointer-events: auto; @@ -49,6 +51,11 @@ } } +.emoji-picker-overlay { + z-index: variables.$z-index-popover; + pointer-events: none; +} + .custom-emoji-tab__icon__text { display: flex; flex-direction: row; diff --git a/webapp/channels/src/sass/components/_emoticons.scss b/webapp/channels/src/sass/components/_emoticons.scss index 0a1fc3737f3b..149a760f53e4 100644 --- a/webapp/channels/src/sass/components/_emoticons.scss +++ b/webapp/channels/src/sass/components/_emoticons.scss @@ -86,11 +86,11 @@ } .emoji-picker { - position: absolute; - z-index: 1100; - display: flex; + // position: relative; + // z-index: 1100; + // display: flex; width: 350px; - flex-direction: column; + // flex-direction: column; border: 1px solid; border-radius: var(--radius-s); margin-right: 3px; @@ -118,10 +118,6 @@ margin-right: 0; } - &.bottom { - margin-top: 10px; - } - .nav-tabs { display: flex; flex: 0 0 34px; diff --git a/webapp/channels/src/sass/components/_post-right.scss b/webapp/channels/src/sass/components/_post-right.scss index 190bcec66203..d9e0dd6239e6 100644 --- a/webapp/channels/src/sass/components/_post-right.scss +++ b/webapp/channels/src/sass/components/_post-right.scss @@ -202,12 +202,6 @@ opacity: 0.7; } - .emoji-picker { - position: absolute; - top: -361px; - right: 0; - } - form { padding: 0; } diff --git a/webapp/channels/src/sass/components/_post.scss b/webapp/channels/src/sass/components/_post.scss index caf7416452b4..fc0d6520bf4a 100644 --- a/webapp/channels/src/sass/components/_post.scss +++ b/webapp/channels/src/sass/components/_post.scss @@ -343,12 +343,6 @@ } } - .emoji-picker { - position: absolute; - top: -361px; - right: 0; - } - .scroll { .custom-textarea { overflow: auto; @@ -658,11 +652,9 @@ visibility: visible; } - .post-add-reaction { - .Reaction { - opacity: 1; - visibility: visible; - } + .Reaction__add { + opacity: 1; + visibility: visible; } } @@ -1373,13 +1365,10 @@ } } - .post-add-reaction { + .Reaction__add { display: inline-block; - - .Reaction { - opacity: 0; - visibility: hidden; - } + opacity: 0; + visibility: hidden; } .post__body { @@ -1488,21 +1477,8 @@ align-items: center; padding: 4px 0 0; - .post-add-reaction-emoji-picker-open { + .Reaction__add { display: inline-block; - - .Reaction { - opacity: 1; - visibility: visible; - } - } - } - - .post-add-reaction-emoji-picker-open { - .Reaction { - background-color: rgba(var(--button-bg-rgb), 0.08); - color: functions.v(button-bg); - fill: functions.v(button-bg); } } diff --git a/webapp/channels/src/sass/responsive/_mobile.scss b/webapp/channels/src/sass/responsive/_mobile.scss index 7e5af04a3362..1b302a265fa7 100644 --- a/webapp/channels/src/sass/responsive/_mobile.scss +++ b/webapp/channels/src/sass/responsive/_mobile.scss @@ -467,7 +467,6 @@ } .emoji-picker { - z-index: 1070; // !important is used to overide inline styles // used on larger screens top: 0 !important; @@ -482,10 +481,6 @@ margin-top: 66px; } - &.bottom { - margin-top: 0; - } - .search-grid-container { height: calc(100vh - 170px); } @@ -861,11 +856,9 @@ } } - .post-add-reaction { - .Reaction { - opacity: 1; - visibility: visible; - } + .Reaction__add { + opacity: 1; + visibility: visible; } .browser--ie & { @@ -2082,19 +2075,6 @@ transform: translate3d(0, 0, 0); } } - - // overides edit modal body element's css position - // to allow emoji picker to fill the screen on mobile screens < 480 - .edit-modal-body--add-reaction { - position: static; - - .emoji-picker { - top: -1px !important; - left: -1px !important; - width: calc(100% + 2px); - height: calc(100% + 2px); - } - } } @media screen and (max-height: 640px) { diff --git a/webapp/channels/src/utils/constants.tsx b/webapp/channels/src/utils/constants.tsx index 8397c1ef8668..8f04cfdc0024 100644 --- a/webapp/channels/src/utils/constants.tsx +++ b/webapp/channels/src/utils/constants.tsx @@ -1657,10 +1657,6 @@ export const Constants = { OPEN_TEAM: 'O', THREADS: 'threads', MAX_POST_LEN: 4000, - EMOJI_SIZE: 16, - DEFAULT_EMOJI_PICKER_LEFT_OFFSET: 87, - DEFAULT_EMOJI_PICKER_RIGHT_OFFSET: 15, - EMOJI_PICKER_WIDTH_OFFSET: 295, SIDEBAR_MINIMUM_WIDTH: 640, THEME_ELEMENTS: [ { diff --git a/webapp/channels/src/utils/floating.ts b/webapp/channels/src/utils/floating.ts new file mode 100644 index 000000000000..49a3efb1e47d --- /dev/null +++ b/webapp/channels/src/utils/floating.ts @@ -0,0 +1,57 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {detectOverflow} from '@floating-ui/react'; +import type {Boundary, MiddlewareState} from '@floating-ui/react-dom'; + +export type HorizontallyWithinOptions = { + + /** + * An element or Rect that the floating element should be aligned with. Often, this will be the result of calling + * document.getElementById with the ID of a parent element (like the post textbox for the emoji picker). + * + * See Floating UI's documentation on detectOverflow for more details. + */ + boundary?: Boundary | null; +} + +/** + * horizontallyWithin is a middleware for useFloating which shifts the floating element left or right to try to keep + * it within the horizontal boundaries of the given boundary element. + * + * If the floating element is wider than the boundary, it'll be positioned right aligned with the boundary. + */ +export function horizontallyWithin(options: HorizontallyWithinOptions = {}) { + return ({ + name: 'horizontallyWithin', + options, + async fn(state: MiddlewareState) { + const {boundary} = options; + + if (!boundary) { + return {}; + } + + const overflow = await detectOverflow(state, { + boundary, + }); + + if (overflow.right > 0) { + // The floating element is overflowing on the right, so shift left + return { + x: state.x - overflow.right, + y: state.y, + }; + } else if (overflow.left > 0) { + // The floating element is overflowing on the left, so shift right + return { + x: state.x + overflow.left, + y: state.y, + }; + } + + // The floating element is horizontally within the boundary, so do nothing + return {}; + }, + }); +} diff --git a/webapp/channels/src/utils/position_utils.test.tsx b/webapp/channels/src/utils/position_utils.test.tsx deleted file mode 100644 index 70d4c4474107..000000000000 --- a/webapp/channels/src/utils/position_utils.test.tsx +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -import {popOverOverlayPosition} from 'utils/position_utils'; - -test('Should return placement position for overlay based on bounds, space required and innerHeight', () => { - const targetBounds = { - top: 400, - bottom: 500, - }; - - expect(popOverOverlayPosition(targetBounds as DOMRect, 1000, 300)).toEqual('top'); - expect(popOverOverlayPosition(targetBounds as DOMRect, 1000, 500, 300)).toEqual('bottom'); - expect(popOverOverlayPosition(targetBounds as DOMRect, 1000, 450)).toEqual('bottom'); - expect(popOverOverlayPosition(targetBounds as DOMRect, 1000, 600)).toEqual('left'); -}); diff --git a/webapp/channels/src/utils/position_utils.tsx b/webapp/channels/src/utils/position_utils.tsx deleted file mode 100644 index da16b7f592c2..000000000000 --- a/webapp/channels/src/utils/position_utils.tsx +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -import type {ComponentProps} from 'react'; -import type {Overlay} from 'react-bootstrap'; - -export function popOverOverlayPosition( - targetBounds: DOMRect, - innerHeight: number, - spaceRequiredAbove: number, - spaceRequiredBelow?: number, - horizontalPosition?: 'left' | 'right', -) { - let placement: ComponentProps['placement']; - - if (targetBounds.top > spaceRequiredAbove) { - placement = 'top'; - } else if (innerHeight - targetBounds.bottom > (spaceRequiredBelow || spaceRequiredAbove)) { - placement = 'bottom'; - } else { - placement = horizontalPosition || 'left'; - } - return placement; -} From 8b164711a4dde8b8d42cb7b1f800640b04c8b229 Mon Sep 17 00:00:00 2001 From: Harrison Healey Date: Fri, 14 Feb 2025 16:21:52 -0500 Subject: [PATCH 046/164] MM-63138 Fix some console warnings (#30209) * Fix forwardRef propTypes console error The console error that was fixed by this is: "Warning: forwardRef render functions do not support propTypes or defaultProps. Did you accidentally pass a React component?" * Fix function component ref console error This fixes the following error: Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React. forwardRef ()? --- .../new_search/search_box_input.tsx | 6 ++--- .../__snapshots__/thread_footer.test.tsx.snap | 20 +++++++------- .../threading/common/button/button.tsx | 27 +++++++++++-------- .../__snapshots__/follow_button.test.tsx.snap | 8 +++--- 4 files changed, 33 insertions(+), 28 deletions(-) diff --git a/webapp/channels/src/components/new_search/search_box_input.tsx b/webapp/channels/src/components/new_search/search_box_input.tsx index 1b0faa690035..92347e5b93e0 100644 --- a/webapp/channels/src/components/new_search/search_box_input.tsx +++ b/webapp/channels/src/components/new_search/search_box_input.tsx @@ -75,7 +75,7 @@ type Props = { focus: (newPosition: number) => void; } -const SearchInput = ({searchTerms, searchType, setSearchTerms, onKeyDown, focus}: Props, inputRef: React.Ref) => { +const SearchInput = forwardRef(({searchTerms, searchType, setSearchTerms, onKeyDown, focus}, inputRef) => { const intl = useIntl(); let searchPlaceholder = intl.formatMessage({id: 'search_bar.search', defaultMessage: 'Search'}); @@ -131,6 +131,6 @@ const SearchInput = ({searchTerms, searchType, setSearchTerms, onKeyDown, focus} )} ); -}; +}); -export default forwardRef(SearchInput); +export default SearchInput; diff --git a/webapp/channels/src/components/threading/channel_threads/thread_footer/__snapshots__/thread_footer.test.tsx.snap b/webapp/channels/src/components/threading/channel_threads/thread_footer/__snapshots__/thread_footer.test.tsx.snap index f5a967e52f70..a92d4fec15e5 100644 --- a/webapp/channels/src/components/threading/channel_threads/thread_footer/__snapshots__/thread_footer.test.tsx.snap +++ b/webapp/channels/src/components/threading/channel_threads/thread_footer/__snapshots__/thread_footer.test.tsx.snap @@ -15,7 +15,7 @@ exports[`components/threading/channel_threads/thread_footer should match snapsho isFollowing={true} onClick={[Function]} > - - +
@@ -317,7 +317,7 @@ exports[`components/threading/channel_threads/thread_footer should report total
- - + - - +
- - + - - + , Props> -function Button({ - prepend, - append, - children, - isActive, - hasDot, - marginTop, - allowTextOverflow = false, - ...attrs -}: Props & Attrs) { +const Button = React.forwardRef(( + { + prepend, + append, + children, + isActive, + hasDot, + marginTop, + allowTextOverflow = false, + ...attrs + }, + ref, +) => { return ( ); -} +}); +Button.displayName = 'Button'; export default memo(Button); diff --git a/webapp/channels/src/components/threading/common/follow_button/__snapshots__/follow_button.test.tsx.snap b/webapp/channels/src/components/threading/common/follow_button/__snapshots__/follow_button.test.tsx.snap index 94aeeeb4a88c..0b069e27e9e9 100644 --- a/webapp/channels/src/components/threading/common/follow_button/__snapshots__/follow_button.test.tsx.snap +++ b/webapp/channels/src/components/threading/common/follow_button/__snapshots__/follow_button.test.tsx.snap @@ -5,7 +5,7 @@ exports[`components/threading/common/follow_button should say follow 1`] = ` isFollowing={false} onClick={[MockFunction]} > - - + `; @@ -30,7 +30,7 @@ exports[`components/threading/common/follow_button should say following 1`] = ` - - + `; From 4750df98c25ace899ea0430f06c3853968dc5521 Mon Sep 17 00:00:00 2001 From: ayush-chauhan233 Date: Mon, 17 Feb 2025 06:06:38 +0530 Subject: [PATCH 047/164] [MM-62961] Fixed pointer event of submenus (#30219) --- webapp/channels/src/components/menu/menu.scss | 1 + webapp/channels/src/components/menu/sub_menu.tsx | 1 + 2 files changed, 2 insertions(+) diff --git a/webapp/channels/src/components/menu/menu.scss b/webapp/channels/src/components/menu/menu.scss index 53de4230192c..6ce7ca7813c5 100644 --- a/webapp/channels/src/components/menu/menu.scss +++ b/webapp/channels/src/components/menu/menu.scss @@ -12,6 +12,7 @@ box-shadow: var(--elevation-4), 0 0 0 1px rgba(var(--center-channel-color-rgb), 0.12) inset; } &.AsSubMenu { + pointer-events: none; & .MuiPaper-root { box-shadow: var(--elevation-5), 0 0 0 1px rgba(var(--center-channel-color-rgb), 0.12) inset; } diff --git a/webapp/channels/src/components/menu/sub_menu.tsx b/webapp/channels/src/components/menu/sub_menu.tsx index 1fb001f43f39..3bf0738a58b5 100644 --- a/webapp/channels/src/components/menu/sub_menu.tsx +++ b/webapp/channels/src/components/menu/sub_menu.tsx @@ -189,6 +189,7 @@ export function SubMenu(props: Props) { autoFocusItem={isSubMenuOpen} sx={{ py: 0, + pointerEvents: 'auto', }} > {children} From e7a246c065c5b9e8084463ceb237c78e046ee7af Mon Sep 17 00:00:00 2001 From: Agniva De Sarker Date: Mon, 17 Feb 2025 12:20:21 +0530 Subject: [PATCH 048/164] [AI assisted]: MM-62914: Added MFA authentication for plugin requests as well (#30160) We wipe the token if MFA authentication is enabled. Also added a test case to lock in the functionality. https://mattermost.atlassian.net/browse/MM-62914 ```release-note NONE ``` --- server/channels/app/authentication.go | 51 ++++++++++++++++ server/channels/app/plugin_api_test.go | 85 ++++++++++++++++++++++++++ server/channels/app/plugin_requests.go | 26 ++++++-- server/channels/web/context.go | 43 +------------ server/i18n/en.json | 4 ++ 5 files changed, 164 insertions(+), 45 deletions(-) diff --git a/server/channels/app/authentication.go b/server/channels/app/authentication.go index 08115b03469c..532beb8fd40e 100644 --- a/server/channels/app/authentication.go +++ b/server/channels/app/authentication.go @@ -6,12 +6,14 @@ package app import ( "errors" "net/http" + "path" "strings" "github.com/mattermost/mattermost/server/public/model" "github.com/mattermost/mattermost/server/public/shared/mlog" "github.com/mattermost/mattermost/server/public/shared/request" "github.com/mattermost/mattermost/server/v8/channels/app/users" + "github.com/mattermost/mattermost/server/v8/channels/utils" "github.com/mattermost/mattermost/server/v8/platform/shared/mfa" ) @@ -233,6 +235,55 @@ func (a *App) CheckUserMfa(rctx request.CTX, user *model.User, token string) *mo return nil } +func (a *App) MFARequired(rctx request.CTX) *model.AppError { + if license := a.Channels().License(); license == nil || !*license.Features.MFA || !*a.Config().ServiceSettings.EnableMultifactorAuthentication || !*a.Config().ServiceSettings.EnforceMultifactorAuthentication { + return nil + } + + session := rctx.Session() + // Session cannot be nil or empty if MFA is to be enforced. + if session == nil || session.Id == "" { + return model.NewAppError("MfaRequired", "api.context.get_session.app_error", nil, "", http.StatusUnauthorized) + } + + // OAuth integrations are excepted + if session.IsOAuth { + return nil + } + + user, err := a.GetUser(session.UserId) + if err != nil { + return model.NewAppError("MfaRequired", "api.context.get_user.app_error", nil, "", http.StatusUnauthorized).Wrap(err) + } + + if user.IsGuest() && !*a.Config().GuestAccountsSettings.EnforceMultifactorAuthentication { + return nil + } + // Only required for email and ldap accounts + if user.AuthService != "" && + user.AuthService != model.UserAuthServiceEmail && + user.AuthService != model.UserAuthServiceLdap { + return nil + } + + // Special case to let user get themself + subpath, _ := utils.GetSubpathFromConfig(a.Config()) + if rctx.Path() == path.Join(subpath, "/api/v4/users/me") { + return nil + } + + // Bots are exempt + if user.IsBot { + return nil + } + + if !user.MfaActive { + return model.NewAppError("MfaRequired", "api.context.mfa_required.app_error", nil, "", http.StatusForbidden) + } + + return nil +} + func checkUserLoginAttempts(user *model.User, max int) *model.AppError { if user.FailedAttempts >= max { return model.NewAppError("checkUserLoginAttempts", "api.user.check_user_login_attempts.too_many.app_error", nil, "user_id="+user.Id, http.StatusUnauthorized) diff --git a/server/channels/app/plugin_api_test.go b/server/channels/app/plugin_api_test.go index 9a45e70e8dce..8ed8034bf287 100644 --- a/server/channels/app/plugin_api_test.go +++ b/server/channels/app/plugin_api_test.go @@ -1896,6 +1896,91 @@ func TestPluginHTTPConnHijack(t *testing.T) { require.Equal(t, "OK", string(body)) } +func TestPluginMFAEnforcement(t *testing.T) { + th := Setup(t).InitBasic() + defer th.TearDown() + + th.App.Srv().SetLicense(model.NewTestLicense("mfa")) + + pluginCode := ` + package main + + import ( + "net/http" + "github.com/mattermost/mattermost/server/public/plugin" + ) + + type MyPlugin struct { + plugin.MattermostPlugin + } + + func (p *MyPlugin) ServeHTTP(c *plugin.Context, w http.ResponseWriter, r *http.Request) { + // Simply return the value of Mattermost-User-Id header + userID := r.Header.Get("Mattermost-User-Id") + w.Write([]byte(userID)) + } + + func main() { + plugin.ClientMain(&MyPlugin{}) + } + ` + + // Create and setup plugin + tearDown, ids, errs := SetAppEnvironmentWithPlugins(t, []string{pluginCode}, th.App, th.NewPluginAPI) + defer tearDown() + require.NoError(t, errs[0]) + require.Len(t, ids, 1) + + pluginID := ids[0] + + // Create user that requires MFA + user := th.CreateUser() + + // Create session + session, appErr := th.App.CreateSession(th.Context, &model.Session{ + UserId: user.Id, + }) + require.Nil(t, appErr) + + client := &http.Client{} + makeRequest := func() string { + reqURL := fmt.Sprintf("http://localhost:%d/plugins/%s", th.Server.ListenAddr.Port, pluginID) + req, err := http.NewRequest("GET", reqURL, nil) + require.NoError(t, err) + req.Header.Set(model.HeaderAuth, model.HeaderToken+" "+session.Token) + + resp, err := client.Do(req) + require.NoError(t, err) + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + require.NoError(t, err) + return string(body) + } + + t.Run("MFA not enforced", func(t *testing.T) { + th.App.UpdateConfig(func(cfg *model.Config) { + *cfg.ServiceSettings.EnableMultifactorAuthentication = true + *cfg.ServiceSettings.EnforceMultifactorAuthentication = false + }) + + // Should return user ID since MFA is not enforced + userID := makeRequest() + assert.Equal(t, user.Id, userID) + }) + + t.Run("MFA enforced", func(t *testing.T) { + th.App.UpdateConfig(func(cfg *model.Config) { + *cfg.ServiceSettings.EnableMultifactorAuthentication = true + *cfg.ServiceSettings.EnforceMultifactorAuthentication = true + }) + + // Should return empty string since MFA is enforced but not active + userID := makeRequest() + assert.Empty(t, userID) + }) +} + func TestPluginHTTPUpgradeWebSocket(t *testing.T) { th := Setup(t) defer th.TearDown() diff --git a/server/channels/app/plugin_requests.go b/server/channels/app/plugin_requests.go index 40726d07e0e8..c2354daab23d 100644 --- a/server/channels/app/plugin_requests.go +++ b/server/channels/app/plugin_requests.go @@ -16,6 +16,7 @@ import ( "github.com/mattermost/mattermost/server/public/model" "github.com/mattermost/mattermost/server/public/plugin" "github.com/mattermost/mattermost/server/public/shared/mlog" + "github.com/mattermost/mattermost/server/public/shared/request" "github.com/mattermost/mattermost/server/v8/channels/utils" ) @@ -146,16 +147,33 @@ func (ch *Channels) servePluginRequest(w http.ResponseWriter, r *http.Request, h token = r.URL.Query().Get("access_token") } + // If MFA is required and user has not activated it, we wipe the token. + app := New(ServerConnector(ch)) + rctx := request.EmptyContext(ch.srv.Log()).WithPath(r.URL.Path) + + // The appErr is later used at L176 and L226. + session, appErr := app.GetSession(token) + if session != nil { + rctx = rctx.WithSession(session) + } + + if mfaAppErr := app.MFARequired(rctx); mfaAppErr != nil { + pluginID := mux.Vars(r)["plugin_id"] + ch.srv.Log().Warn("Treating session as unauthenticated since MFA required", + mlog.String("plugin_id", pluginID), + mlog.String("url", r.URL.Path), + mlog.Err(mfaAppErr), + ) + token = "" + } + // Mattermost-Plugin-ID can only be set by inter-plugin requests r.Header.Del("Mattermost-Plugin-ID") r.Header.Del("Mattermost-User-Id") if token != "" { - session, appErr := New(ServerConnector(ch)).GetSession(token) - csrfCheckPassed := false - - if session != nil && appErr == nil && cookieAuth && r.Method != "GET" { + if (session != nil && session.Id != "") && appErr == nil && cookieAuth && r.Method != "GET" { sentToken := "" if r.Header.Get(model.HeaderCsrfToken) == "" { diff --git a/server/channels/web/context.go b/server/channels/web/context.go index 56e88b3da556..1437b76ab0f4 100644 --- a/server/channels/web/context.go +++ b/server/channels/web/context.go @@ -5,7 +5,6 @@ package web import ( "net/http" - "path" "regexp" "strings" @@ -158,46 +157,8 @@ func (c *Context) RemoteClusterTokenRequired() { } func (c *Context) MfaRequired() { - // Must be licensed for MFA and have it configured for enforcement - if license := c.App.Channels().License(); license == nil || !*license.Features.MFA || !*c.App.Config().ServiceSettings.EnableMultifactorAuthentication || !*c.App.Config().ServiceSettings.EnforceMultifactorAuthentication { - return - } - - // OAuth integrations are excepted - if c.AppContext.Session().IsOAuth { - return - } - - user, err := c.App.GetUser(c.AppContext.Session().UserId) - if err != nil { - c.Err = model.NewAppError("MfaRequired", "api.context.get_user.app_error", nil, "", http.StatusUnauthorized).Wrap(err) - return - } - - if user.IsGuest() && !*c.App.Config().GuestAccountsSettings.EnforceMultifactorAuthentication { - return - } - // Only required for email and ldap accounts - if user.AuthService != "" && - user.AuthService != model.UserAuthServiceEmail && - user.AuthService != model.UserAuthServiceLdap { - return - } - - // Special case to let user get themself - subpath, _ := utils.GetSubpathFromConfig(c.App.Config()) - if c.AppContext.Path() == path.Join(subpath, "/api/v4/users/me") { - return - } - - // Bots are exempt - if user.IsBot { - return - } - - if !user.MfaActive { - c.Err = model.NewAppError("MfaRequired", "api.context.mfa_required.app_error", nil, "", http.StatusForbidden) - return + if appErr := c.App.MFARequired(c.AppContext); appErr != nil { + c.Err = appErr } } diff --git a/server/i18n/en.json b/server/i18n/en.json index bf574dcf44fe..bbed00cc9853 100644 --- a/server/i18n/en.json +++ b/server/i18n/en.json @@ -1705,6 +1705,10 @@ "id": "api.context.404.app_error", "translation": "Sorry, we could not find the page." }, + { + "id": "api.context.get_session.app_error", + "translation": "Session not found." + }, { "id": "api.context.get_user.app_error", "translation": "Unable to get user from session UserID." From cbb1081550e05aecff80b4d3ebe2d1f620ea7b2a Mon Sep 17 00:00:00 2001 From: M-ZubairAhmed Date: Mon, 17 Feb 2025 13:41:26 +0530 Subject: [PATCH 049/164] [MM-54861] Command + K shortcut to hyperlink text doesn't work when editing a post (#30195) --- .../advanced_text_editor.tsx | 26 +++---- webapp/channels/src/utils/constants.tsx | 8 ++ webapp/channels/src/utils/utils.test.tsx | 76 ++++++++++++++++++- webapp/channels/src/utils/utils.tsx | 13 +++- 4 files changed, 103 insertions(+), 20 deletions(-) diff --git a/webapp/channels/src/components/advanced_text_editor/advanced_text_editor.tsx b/webapp/channels/src/components/advanced_text_editor/advanced_text_editor.tsx index 72a0a547f562..79134183fdc5 100644 --- a/webapp/channels/src/components/advanced_text_editor/advanced_text_editor.tsx +++ b/webapp/channels/src/components/advanced_text_editor/advanced_text_editor.tsx @@ -55,6 +55,7 @@ import Constants, { AdvancedTextEditor as AdvancedTextEditorConst, UserStatuses, ModalIdentifiers, + AdvancedTextEditorTextboxIds, } from 'utils/constants'; import {canUploadFiles as canUploadFilesAccordingToConfig} from 'utils/file_utils'; import type {ApplyMarkdownOptions} from 'utils/markdown/apply_markdown'; @@ -132,22 +133,17 @@ const AdvancedTextEditor = ({ const getDraftSelector = useMemo(makeGetDraft, []); const getDisplayName = useMemo(makeGetDisplayName, []); - let textboxId = 'textbox'; - - switch (location) { - case Locations.CENTER: - textboxId = 'post_textbox'; - break; - case Locations.RHS_COMMENT: - textboxId = 'reply_textbox'; - break; - case Locations.MODAL: - textboxId = 'modal_textbox'; - break; - } - + let textboxId: string; if (isInEditMode) { - textboxId = 'edit_textbox'; + textboxId = AdvancedTextEditorTextboxIds.InEditMode; + } else if (location === Locations.CENTER) { + textboxId = AdvancedTextEditorTextboxIds.InCenter; + } else if (location === Locations.RHS_COMMENT) { + textboxId = AdvancedTextEditorTextboxIds.InRHSComment; + } else if (location === Locations.MODAL) { + textboxId = AdvancedTextEditorTextboxIds.InModal; + } else { + textboxId = AdvancedTextEditorTextboxIds.Default; } const isRHS = Boolean(postId && !isThreadView); diff --git a/webapp/channels/src/utils/constants.tsx b/webapp/channels/src/utils/constants.tsx index 8f04cfdc0024..a33bb5c2d4ee 100644 --- a/webapp/channels/src/utils/constants.tsx +++ b/webapp/channels/src/utils/constants.tsx @@ -751,6 +751,14 @@ export const AdvancedTextEditor = { EDIT: 'edit', }; +export const AdvancedTextEditorTextboxIds = { + InCenter: 'post_textbox', + InRHSComment: 'reply_textbox', + InModal: 'modal_textbox', + InEditMode: 'edit_textbox', + Default: 'textbox', +}; + export const TELEMETRY_CATEGORIES = { CLOUD_PURCHASING: 'cloud_purchasing', CLOUD_PRICING: 'cloud_pricing', diff --git a/webapp/channels/src/utils/utils.test.tsx b/webapp/channels/src/utils/utils.test.tsx index 0d126c664c39..011638bf8b3d 100644 --- a/webapp/channels/src/utils/utils.test.tsx +++ b/webapp/channels/src/utils/utils.test.tsx @@ -9,7 +9,7 @@ import store from 'stores/redux_store'; import * as lineBreakHelpers from 'tests/helpers/line_break_helpers'; import * as ua from 'tests/helpers/user_agent_mocks'; -import Constants, {ValidationErrors} from 'utils/constants'; +import Constants, {ValidationErrors, AdvancedTextEditorTextboxIds} from 'utils/constants'; import * as Utils from 'utils/utils'; describe('Utils.getDisplayNameByUser', () => { @@ -498,3 +498,77 @@ describe('Utils.numberToFixedDynamic', () => { }); }); }); + +describe('isTextSelectedInPostOrReply', () => { + function createKeyboardEvent(target: Partial) { + return { + target: { + selectionStart: 0, + selectionEnd: 0, + id: AdvancedTextEditorTextboxIds.Default, + ...target, + }, + } as unknown as KeyboardEvent; + } + + test('returns false when not typing in a textbox', () => { + const event = createKeyboardEvent({ + id: 'not_a_textbox', + }); + expect(Utils.isTextSelectedInPostOrReply(event)).toBe(false); + }); + + test('returns false when no text is selected in center textbox', () => { + const event = createKeyboardEvent({ + id: AdvancedTextEditorTextboxIds.InCenter, + selectionStart: 5, + selectionEnd: 5, + }); + expect(Utils.isTextSelectedInPostOrReply(event)).toBe(false); + }); + + test('returns true when text is selected in center textbox', () => { + const event = createKeyboardEvent({ + id: AdvancedTextEditorTextboxIds.InCenter, + selectionStart: 0, + selectionEnd: 5, + }); + expect(Utils.isTextSelectedInPostOrReply(event)).toBe(true); + }); + + test('returns false when no text is selected in RHS comment textbox', () => { + const event = createKeyboardEvent({ + id: AdvancedTextEditorTextboxIds.InRHSComment, + selectionStart: 3, + selectionEnd: 3, + }); + expect(Utils.isTextSelectedInPostOrReply(event)).toBe(false); + }); + + test('returns true when text is selected in RHS comment textbox', () => { + const event = createKeyboardEvent({ + id: AdvancedTextEditorTextboxIds.InRHSComment, + selectionStart: 0, + selectionEnd: 3, + }); + expect(Utils.isTextSelectedInPostOrReply(event)).toBe(true); + }); + + test('returns false when no text is selected in edit mode textbox', () => { + const event = createKeyboardEvent({ + id: AdvancedTextEditorTextboxIds.InEditMode, + selectionStart: 7, + selectionEnd: 7, + }); + expect(Utils.isTextSelectedInPostOrReply(event)).toBe(false); + }); + + test('returns true when text is selected in edit mode textbox', () => { + const event = createKeyboardEvent({ + id: AdvancedTextEditorTextboxIds.InEditMode, + selectionStart: 0, + selectionEnd: 7, + }); + expect(Utils.isTextSelectedInPostOrReply(event)).toBe(true); + }); +}); diff --git a/webapp/channels/src/utils/utils.tsx b/webapp/channels/src/utils/utils.tsx index 62beab83bdd2..1238c6afad05 100644 --- a/webapp/channels/src/utils/utils.tsx +++ b/webapp/channels/src/utils/utils.tsx @@ -55,7 +55,7 @@ import {focusPost} from 'components/permalink_view/actions'; import type {TextboxElement} from 'components/textbox'; import {getHistory} from 'utils/browser_history'; -import Constants, {FileTypes, ValidationErrors, A11yCustomEventTypes} from 'utils/constants'; +import Constants, {FileTypes, ValidationErrors, A11yCustomEventTypes, AdvancedTextEditorTextboxIds} from 'utils/constants'; import type {A11yFocusEventDetail} from 'utils/constants'; import * as Keyboard from 'utils/keyboard'; import * as UserAgent from 'utils/user_agent'; @@ -1439,13 +1439,18 @@ function isSelection() { return selection!.type === 'Range'; } +/** + * Checks if text is selected in the a textbox in center or in RHS or in edit mode of post + */ export function isTextSelectedInPostOrReply(e: React.KeyboardEvent | KeyboardEvent) { const {id} = e.target as HTMLElement; - const isTypingInPost = id === 'post_textbox'; - const isTypingInReply = id === 'reply_textbox'; + const isTypingInValidTextbox = + id === AdvancedTextEditorTextboxIds.InCenter || + id === AdvancedTextEditorTextboxIds.InRHSComment || + id === AdvancedTextEditorTextboxIds.InEditMode; - if (!isTypingInPost && !isTypingInReply) { + if (isTypingInValidTextbox === false) { return false; } From ab9fd5e4f693fb40a061068e9d304f6591462ba9 Mon Sep 17 00:00:00 2001 From: Tom De Moor Date: Mon, 17 Feb 2025 14:03:52 +0000 Subject: [PATCH 050/164] Adding a label wip languages (#30100) * adding extra label to WIP-languages * adding an extra space * fix linting * modifying test * updating test --------- Co-authored-by: Tom De Moor --- webapp/channels/src/i18n/i18n.jsx | 2 +- webapp/channels/src/i18n/i18n.test.jsx | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/webapp/channels/src/i18n/i18n.jsx b/webapp/channels/src/i18n/i18n.jsx index aa62e6f4128a..e146f3e97978 100644 --- a/webapp/channels/src/i18n/i18n.jsx +++ b/webapp/channels/src/i18n/i18n.jsx @@ -152,7 +152,7 @@ export function getAllLanguages(includeExperimental) { ...langIDs.reduce((out, id) => { out[id] = { value: id, - name: langLabels[id], + name: langLabels[id] + ' (Experimental)', url: langFiles[id], order: order++, }; diff --git a/webapp/channels/src/i18n/i18n.test.jsx b/webapp/channels/src/i18n/i18n.test.jsx index 8187af59f0dd..d7ba8ed83c49 100644 --- a/webapp/channels/src/i18n/i18n.test.jsx +++ b/webapp/channels/src/i18n/i18n.test.jsx @@ -18,7 +18,7 @@ describe('i18n', () => { // with experimental languages expect(getAllLanguages(true)).toStrictEqual({ cc: { - name: 'CC Language', + name: 'CC Language (Experimental)', value: 'cc', order: 22, url: 'cc.json', @@ -44,7 +44,7 @@ describe('i18n', () => { state.entities.general.config.EnableExperimentalLocales = 'true'; expect(getLanguages(state)).toStrictEqual({ cc: { - name: 'CC Language', + name: 'CC Language (Experimental)', value: 'cc', order: 22, url: 'cc.json', @@ -64,7 +64,7 @@ describe('i18n', () => { // experimental language (e.g. in progress) expect(getLanguageInfo('cc')).toStrictEqual({ - name: 'CC Language', + name: 'CC Language (Experimental)', value: 'cc', order: 22, url: 'cc.json', From 32037706b545b009b555eacefc2e15fa65aa484d Mon Sep 17 00:00:00 2001 From: Agniva De Sarker Date: Mon, 17 Feb 2025 19:39:39 +0530 Subject: [PATCH 051/164] MM-62960: Support both webhub iteration scopes properly (#30224) When Channel iteration mode is enabled, we need to ensure that channel scoped events do not fall through to the connIndex.All() condition. This is possible because there are multiple hubs in a given system. So a single event will flow through all of them and in some hubs, a channel scoped event might not have any connections. In that case, we need to stop processing further. https://mattermost.atlassian.net/browse/MM-62960 ```release-note NONE ``` --- server/channels/api4/post_test.go | 21 ++++++++++++++++++-- server/channels/app/platform/web_hub.go | 8 ++++++++ server/channels/app/platform/web_hub_test.go | 6 +++++- 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/server/channels/api4/post_test.go b/server/channels/api4/post_test.go index 715d43259242..2ba7c0a5ae9b 100644 --- a/server/channels/api4/post_test.go +++ b/server/channels/api4/post_test.go @@ -3354,8 +3354,25 @@ func TestPermanentDeletePost(t *testing.T) { } func TestWebHubMembership(t *testing.T) { - th := Setup(t).InitBasic() - defer th.TearDown() + t.Run("WithChannelIteration", func(t *testing.T) { + th := SetupConfig(t, func(cfg *model.Config) { + *cfg.ServiceSettings.EnableWebHubChannelIteration = true + }).InitBasic() + defer th.TearDown() + + _testWebHubMembership(th, t) + }) + + t.Run("WithoutChannelIteration", func(t *testing.T) { + th := Setup(t).InitBasic() + defer th.TearDown() + + _testWebHubMembership(th, t) + }) +} + +func _testWebHubMembership(th *TestHelper, t *testing.T) { + t.Helper() u1 := th.CreateUser() th.LinkUserToTeam(u1, th.BasicTeam) diff --git a/server/channels/app/platform/web_hub.go b/server/channels/app/platform/web_hub.go index 6afafd87c24f..948272f42f4c 100644 --- a/server/channels/app/platform/web_hub.go +++ b/server/channels/app/platform/web_hub.go @@ -682,6 +682,14 @@ func (h *Hub) Start() { continue } + // There are multiple hubs in a system. So while supporting both channel based iteration and the old + // method, there would be events scoped to a channel being sent to multiple hubs. And only one hub would + // have the targetConns. Therefore, we need to stop here if channel based iteration is enabled, and it's a + // channel-scoped event. + if channelID := msg.GetBroadcast().ChannelId; channelID != "" && *h.platform.Config().ServiceSettings.EnableWebHubChannelIteration { + continue + } + for webConn := range connIndex.All() { broadcast(webConn) } diff --git a/server/channels/app/platform/web_hub_test.go b/server/channels/app/platform/web_hub_test.go index 74fea05dbf59..bd931646dbc9 100644 --- a/server/channels/app/platform/web_hub_test.go +++ b/server/channels/app/platform/web_hub_test.go @@ -386,7 +386,11 @@ func TestHubConnIndex(t *testing.T) { t.Run("ForChannel", func(t *testing.T) { require.Len(t, connIndex.byChannelID, 1) - require.Equal(t, []*WebConn{wc1, wc2, wc3}, connIndex.ForChannel(th.BasicChannel.Id)) + ids := make([]string, 0) + for _, c := range connIndex.ForChannel(th.BasicChannel.Id) { + ids = append(ids, c.GetConnectionID()) + } + require.ElementsMatch(t, []string{wc1ID, wc2ID, wc3ID}, ids) require.Len(t, connIndex.ForChannel("notexist"), 0) }) From 3da6e7a75dd2151380945aa9193d7329088fcb97 Mon Sep 17 00:00:00 2001 From: Ibrahim Serdar Acikgoz Date: Mon, 17 Feb 2025 18:10:44 +0100 Subject: [PATCH 052/164] server/Makefile: pin dbcmp (#30245) --- server/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/Makefile b/server/Makefile index 4a137978f53f..79929f677ec4 100644 --- a/server/Makefile +++ b/server/Makefile @@ -840,7 +840,7 @@ test-migration: # run pgloader and save the logs pgloader --debug tests/temp.load > migration.log - $(GO) install github.com/mattermost/dbcmp/cmd/dbcmp@latest + $(GO) install github.com/mattermost/dbcmp/cmd/dbcmp@9324978963 # compare two database contents # db_migrations differ due to a typo in the 92. migration name # for now we exclude plugins such as playbooks and focalboard From 62a24aa91e6be88ee47e72b1d97a22b270b16b4d Mon Sep 17 00:00:00 2001 From: "Weblate (bot)" Date: Mon, 17 Feb 2025 20:09:54 +0100 Subject: [PATCH 053/164] Translations update from Mattermost Weblate (#30241) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Translated using Weblate (Russian) Currently translated at 96.8% (2519 of 2602 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/ru/ * Translated using Weblate (German) Currently translated at 100.0% (2602 of 2602 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/de/ * Translated using Weblate (Turkish) Currently translated at 100.0% (2602 of 2602 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/tr/ * Translated using Weblate (German) Currently translated at 100.0% (6071 of 6071 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/de/ * Translated using Weblate (Turkish) Currently translated at 100.0% (6071 of 6071 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/tr/ * Translated using Weblate (Czech) Currently translated at 100.0% (2602 of 2602 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/cs/ * Translated using Weblate (Czech) Currently translated at 100.0% (6071 of 6071 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/cs/ * Translated using Weblate (Polish) Currently translated at 100.0% (2602 of 2602 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/pl/ * Translated using Weblate (Chinese (Simplified Han script)) Currently translated at 100.0% (2602 of 2602 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/zh_Hans/ * Translated using Weblate (Chinese (Simplified Han script)) Currently translated at 100.0% (6071 of 6071 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/zh_Hans/ * Translated using Weblate (Norwegian Bokmål) Currently translated at 60.5% (3673 of 6071 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/nb_NO/ * Translated using Weblate (Swedish) Currently translated at 99.8% (6060 of 6071 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/sv/ * Translated using Weblate (Dutch) Currently translated at 99.8% (2599 of 2602 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/nl/ * Translated using Weblate (Dutch) Currently translated at 99.9% (6070 of 6071 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/nl/ * Translated using Weblate (Ukrainian) Currently translated at 99.2% (2583 of 2602 strings) Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/uk/ * Translated using Weblate (Polish) Currently translated at 99.1% (6021 of 6071 strings) Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/pl/ * Update translation files Updated by "Cleanup translation files" hook in Weblate. Translation: Mattermost/server Translate-URL: https://translate.mattermost.com/projects/mattermost/server/ * Update translation files Updated by "Cleanup translation files" hook in Weblate. Translation: Mattermost/webapp Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/ --------- Co-authored-by: Konstantin Co-authored-by: jprusch Co-authored-by: Kaya Zeren Co-authored-by: Martin Mičuda Co-authored-by: master7 Co-authored-by: ThrRip Co-authored-by: Frank Paul Silye Co-authored-by: MArtin Johnson Co-authored-by: Tom De Moor Co-authored-by: Serhii Khomiuk --- server/i18n/cs.json | 44 ++++++++++++++++++-------- server/i18n/de.json | 44 ++++++++++++++++++-------- server/i18n/en-AU.json | 12 -------- server/i18n/ja.json | 12 -------- server/i18n/nl.json | 44 ++++++++++++++++++-------- server/i18n/pl.json | 48 +++++++++++++++++++++-------- server/i18n/ru.json | 28 +++++++++++++++++ server/i18n/sv.json | 12 -------- server/i18n/tr.json | 44 ++++++++++++++++++-------- server/i18n/uk.json | 8 +++++ server/i18n/zh-CN.json | 44 ++++++++++++++++++-------- webapp/channels/src/i18n/be.json | 1 - webapp/channels/src/i18n/bg.json | 1 - webapp/channels/src/i18n/cs.json | 36 ++++++++++++---------- webapp/channels/src/i18n/de.json | 4 ++- webapp/channels/src/i18n/en-AU.json | 1 - webapp/channels/src/i18n/es.json | 1 - webapp/channels/src/i18n/fa.json | 1 - webapp/channels/src/i18n/fr.json | 1 - webapp/channels/src/i18n/fy.json | 1 - webapp/channels/src/i18n/hr.json | 1 - webapp/channels/src/i18n/hu.json | 1 - webapp/channels/src/i18n/it.json | 1 - webapp/channels/src/i18n/ja.json | 1 - webapp/channels/src/i18n/ko.json | 1 - webapp/channels/src/i18n/lt.json | 1 - webapp/channels/src/i18n/nb-NO.json | 28 ++++++++++++++++- webapp/channels/src/i18n/nl.json | 4 ++- webapp/channels/src/i18n/pl.json | 14 +++++++-- webapp/channels/src/i18n/pt-BR.json | 1 - webapp/channels/src/i18n/ro.json | 1 - webapp/channels/src/i18n/ru.json | 1 - webapp/channels/src/i18n/sv.json | 3 +- webapp/channels/src/i18n/tr.json | 4 ++- webapp/channels/src/i18n/uk.json | 1 - webapp/channels/src/i18n/vi.json | 1 - webapp/channels/src/i18n/zh-CN.json | 35 ++++++++++++++++----- webapp/channels/src/i18n/zh-TW.json | 1 - 38 files changed, 327 insertions(+), 160 deletions(-) diff --git a/server/i18n/cs.json b/server/i18n/cs.json index dfc802f5b8a1..e6b6b3fe1bf3 100644 --- a/server/i18n/cs.json +++ b/server/i18n/cs.json @@ -10315,18 +10315,6 @@ "id": "app.custom_profile_attributes.property_field_update.app_error", "translation": "Nepodařilo se aktualizovat pole vlastního atributu profilu" }, - { - "id": "app.custom_profile_attributes.property_value_creation.app_error", - "translation": "Nelze vytvořit hodnotu vlastnosti" - }, - { - "id": "app.custom_profile_attributes.property_value_list.app_error", - "translation": "Nepodařilo se načíst hodnoty vlastností" - }, - { - "id": "app.custom_profile_attributes.property_value_update.app_error", - "translation": "Nelze aktualizovat hodnotu vlastnosti" - }, { "id": "app.custom_profile_attributes.search_property_fields.app_error", "translation": "Nepodařilo se vyhledat pole vlastních atributů profilu" @@ -10402,5 +10390,37 @@ { "id": "api.command.execute_command.deleted.error", "translation": "Nelze spustit příkaz v odstraněném kanálu." + }, + { + "id": "api.channel.bookmark.create_channel_bookmark.deleted_channel.forbidden.app_error", + "translation": "Nepodařilo se vytvořit záložku kanálu." + }, + { + "id": "api.channel.bookmark.delete_channel_bookmark.deleted_channel.forbidden.app_error", + "translation": "Nepodařilo se smazat záložku kanálu." + }, + { + "id": "api.channel.bookmark.update_channel_bookmark.deleted_channel.forbidden.app_error", + "translation": "Nepodařilo se aktualizovat záložku kanálu." + }, + { + "id": "api.channel.bookmark.update_channel_bookmark_sort_order.deleted_channel.forbidden.app_error", + "translation": "Nepodařilo se aktualizovat pořadí záložek kanálu." + }, + { + "id": "api.custom_profile_attributes.field_not_found", + "translation": "pokoušíte se upravit pole, které neexistuje" + }, + { + "id": "api.user.view_archived_channels.list_channel_bookmarks_for_channel.app_error", + "translation": "Nelze načíst záložky pro archivovaný kanál" + }, + { + "id": "app.file_info.get_count.app_error", + "translation": "Nepodařilo se získat počet všech souborů." + }, + { + "id": "app.file_info.get_storage_usage.app_error", + "translation": "Nepodařilo se získat využití úložiště pro všechny soubory." } ] diff --git a/server/i18n/de.json b/server/i18n/de.json index 7f3f44dcb9a7..5ee07fe756c8 100644 --- a/server/i18n/de.json +++ b/server/i18n/de.json @@ -10311,18 +10311,6 @@ "id": "app.custom_profile_attributes.property_field_update.app_error", "translation": "Benutzerdefiniertes Profilattributfeld kann nicht aktualisiert werden" }, - { - "id": "app.custom_profile_attributes.property_value_list.app_error", - "translation": "Eigenschaftswerte können nicht abgerufen werden" - }, - { - "id": "app.custom_profile_attributes.property_value_creation.app_error", - "translation": "Kann keinen Eigenschaftswert erstellen" - }, - { - "id": "app.custom_profile_attributes.property_value_update.app_error", - "translation": "Eigenschaftswert kann nicht aktualisiert werden" - }, { "id": "app.custom_profile_attributes.search_property_fields.app_error", "translation": "Benutzerdefinierte Profilattributfelder können nicht durchsucht werden" @@ -10398,5 +10386,37 @@ { "id": "api.command.execute_command.deleted.error", "translation": "Kommando kann nicht in einem gelöschten Kanal ausgeführt werden." + }, + { + "id": "api.channel.bookmark.create_channel_bookmark.deleted_channel.forbidden.app_error", + "translation": "Die Erstellung des Kanal-Lesezeichens ist fehlgeschlagen." + }, + { + "id": "api.channel.bookmark.delete_channel_bookmark.deleted_channel.forbidden.app_error", + "translation": "Das Löschen des Kanal-Lesezeichens ist fehlgeschlagen." + }, + { + "id": "api.channel.bookmark.update_channel_bookmark.deleted_channel.forbidden.app_error", + "translation": "Die Aktualisierung des Kanal-Lesezeichens ist fehlgeschlagen." + }, + { + "id": "api.channel.bookmark.update_channel_bookmark_sort_order.deleted_channel.forbidden.app_error", + "translation": "Die Sortierreihenfolge des Kanal-Lesezeichens konnte nicht aktualisiert werden." + }, + { + "id": "api.custom_profile_attributes.field_not_found", + "translation": "Versuch, ein Feld zu patchen, das nicht existiert" + }, + { + "id": "api.user.view_archived_channels.list_channel_bookmarks_for_channel.app_error", + "translation": "Lesezeichen für einen archivierten Kanal können nicht abgerufen werden" + }, + { + "id": "app.file_info.get_count.app_error", + "translation": "Die Anzahl aller Dateien konnte nicht ermittelt werden." + }, + { + "id": "app.file_info.get_storage_usage.app_error", + "translation": "Die Speichernutzung aller Dateien konnte nicht ermittelt werden." } ] diff --git a/server/i18n/en-AU.json b/server/i18n/en-AU.json index 2b77e755a52a..030854b5ffa3 100644 --- a/server/i18n/en-AU.json +++ b/server/i18n/en-AU.json @@ -10296,14 +10296,6 @@ "id": "app.custom_profile_attributes.list_property_values.app_error", "translation": "Unable to get custom profile attribute values" }, - { - "id": "app.custom_profile_attributes.property_value_creation.app_error", - "translation": "Cannot create property value" - }, - { - "id": "app.custom_profile_attributes.property_value_list.app_error", - "translation": "Unable to retrieve property values" - }, { "id": "app.custom_profile_attributes.property_field_delete.app_error", "translation": "Unable to delete Custom Profile Attribute field" @@ -10324,10 +10316,6 @@ "id": "app.file_info.delete_for_post_ids.app_error", "translation": "Failed to remove the requested files from database" }, - { - "id": "app.custom_profile_attributes.property_value_update.app_error", - "translation": "Cannot update property value" - }, { "id": "app.file_info.undelete_for_post_ids.app_error", "translation": "Failed to restore post file attachments." diff --git a/server/i18n/ja.json b/server/i18n/ja.json index 86511f19b581..af07aff620c4 100644 --- a/server/i18n/ja.json +++ b/server/i18n/ja.json @@ -10301,18 +10301,6 @@ "id": "app.custom_profile_attributes.property_field_update.app_error", "translation": "カスタムプロフィール属性のフィールドを更新できませんでした" }, - { - "id": "app.custom_profile_attributes.property_value_creation.app_error", - "translation": "プロパティ値を作成できません" - }, - { - "id": "app.custom_profile_attributes.property_value_list.app_error", - "translation": "プロパティ値を取得できませんでした" - }, - { - "id": "app.custom_profile_attributes.property_value_update.app_error", - "translation": "プロパティ値を更新できません" - }, { "id": "app.custom_profile_attributes.search_property_fields.app_error", "translation": "カスタムプロフィール属性のフィールドを検索できませんでした" diff --git a/server/i18n/nl.json b/server/i18n/nl.json index 4435ac30f30a..91603afbf2a1 100644 --- a/server/i18n/nl.json +++ b/server/i18n/nl.json @@ -10323,10 +10323,6 @@ "id": "app.custom_profile_attributes.property_field_update.app_error", "translation": "Kan aangepast profielattribuutveld niet bijwerken" }, - { - "id": "app.custom_profile_attributes.property_value_creation.app_error", - "translation": "Kan eigenschap waarde niet creëren" - }, { "id": "app.custom_profile_attributes.search_property_fields.app_error", "translation": "Kan niet zoeken in aangepaste profielattribuutvelden" @@ -10391,14 +10387,6 @@ "id": "model.property_value.is_valid.app_error", "translation": "Ongeldige waarde voor de eigenschap: {{.FieldName}} ({{.Reason}})." }, - { - "id": "app.custom_profile_attributes.property_value_list.app_error", - "translation": "Kan de waarden van eigenschappen niet ophalen" - }, - { - "id": "app.custom_profile_attributes.property_value_update.app_error", - "translation": "Kan de waarde van de eigenschap niet bijwerken" - }, { "id": "api.command.execute_command.deleted.error", "translation": "Kan geen opdracht uitvoeren in verwijderde chatroom." @@ -10410,5 +10398,37 @@ { "id": "api.custom_profile_attributes.license_error", "translation": "Jouw licentie ondersteunt geen Aangepaste Profielattributen." + }, + { + "id": "api.channel.bookmark.create_channel_bookmark.deleted_channel.forbidden.app_error", + "translation": "Het aanmaken van de kanaalbladwijzer is mislukt." + }, + { + "id": "api.channel.bookmark.delete_channel_bookmark.deleted_channel.forbidden.app_error", + "translation": "Het verwijderen van de kanaalbladwijzer is mislukt." + }, + { + "id": "api.channel.bookmark.update_channel_bookmark.deleted_channel.forbidden.app_error", + "translation": "Het bijwerken van de kanaalbladwijzer is mislukt." + }, + { + "id": "api.channel.bookmark.update_channel_bookmark_sort_order.deleted_channel.forbidden.app_error", + "translation": "Het bijwerken van de sorteervolgorde van de kanaalbladwijzers is mislukt." + }, + { + "id": "api.user.view_archived_channels.list_channel_bookmarks_for_channel.app_error", + "translation": "Kan geen bladwijzers ophalen voor een gearchiveerd kanaal" + }, + { + "id": "app.file_info.get_count.app_error", + "translation": "Het is niet gelukt om alle bestanden te tellen." + }, + { + "id": "app.file_info.get_storage_usage.app_error", + "translation": "Het is niet gelukt om het opslaggebruik van alle bestanden op te halen." + }, + { + "id": "api.custom_profile_attributes.field_not_found", + "translation": "proberen een veld te patchen dat niet bestaat" } ] diff --git a/server/i18n/pl.json b/server/i18n/pl.json index 4d43f891da8d..8e01796fe39d 100644 --- a/server/i18n/pl.json +++ b/server/i18n/pl.json @@ -10311,22 +10311,10 @@ "id": "app.custom_profile_attributes.property_field_update.app_error", "translation": "Nie można zaktualizować pola atrybutu profilu niestandardowego" }, - { - "id": "app.custom_profile_attributes.property_value_creation.app_error", - "translation": "Nie można utworzyć wartości właściwości" - }, { "id": "app.custom_profile_attributes.property_field_not_found.app_error", "translation": "Nie znaleziono pola atrybutu profilu niestandardowego" }, - { - "id": "app.custom_profile_attributes.property_value_list.app_error", - "translation": "Nie można pobrać wartości właściwości" - }, - { - "id": "app.custom_profile_attributes.property_value_update.app_error", - "translation": "Nie można zaktualizować wartości właściwości" - }, { "id": "app.custom_profile_attributes.search_property_fields.app_error", "translation": "Nie można przeszukiwać pól atrybutów profilu niestandardowego" @@ -10398,5 +10386,41 @@ { "id": "api.file.zip_file_reader.app_error", "translation": "Nie można pobrać czytnika plików zip." + }, + { + "id": "api.channel.bookmark.create_channel_bookmark.deleted_channel.forbidden.app_error", + "translation": "Nie udało się utworzyć zakładki kanału." + }, + { + "id": "api.channel.bookmark.update_channel_bookmark_sort_order.deleted_channel.forbidden.app_error", + "translation": "Nie udało się zaktualizować kolejności sortowania zakładki kanału." + }, + { + "id": "api.custom_profile_attributes.field_not_found", + "translation": "próba załatania pola, które nie istnieje" + }, + { + "id": "api.user.view_archived_channels.list_channel_bookmarks_for_channel.app_error", + "translation": "Nie można pobrać zakładek dla zarchiwizowanego kanału" + }, + { + "id": "app.file_info.get_count.app_error", + "translation": "Nie udało się uzyskać liczby wszystkich plików." + }, + { + "id": "app.file_info.get_storage_usage.app_error", + "translation": "Nie udało się uzyskać użycia pamięci masowej dla wszystkich plików." + }, + { + "id": "api.channel.bookmark.delete_channel_bookmark.deleted_channel.forbidden.app_error", + "translation": "Nie udało się usunąć zakładki kanału." + }, + { + "id": "api.channel.bookmark.update_channel_bookmark.deleted_channel.forbidden.app_error", + "translation": "Nie udało się zaktualizować zakładki kanału." + }, + { + "id": "api.command.execute_command.deleted.error", + "translation": "Nie można uruchomić polecenia w usuniętym kanale." } ] diff --git a/server/i18n/ru.json b/server/i18n/ru.json index 2ff97e3b1b44..564bb14005d7 100644 --- a/server/i18n/ru.json +++ b/server/i18n/ru.json @@ -10054,5 +10054,33 @@ { "id": "api.file.zip_file_reader.app_error", "translation": "Невозможно получить программу для чтения zip-файлов." + }, + { + "id": "api.channel.bookmark.create_channel_bookmark.deleted_channel.forbidden.app_error", + "translation": "Не удалось создать закладку канала." + }, + { + "id": "api.channel.bookmark.delete_channel_bookmark.deleted_channel.forbidden.app_error", + "translation": "Не удалось удалить закладку канала." + }, + { + "id": "api.channel.bookmark.update_channel_bookmark.deleted_channel.forbidden.app_error", + "translation": "Не удалось обновить закладку канала." + }, + { + "id": "api.channel.bookmark.update_channel_bookmark_sort_order.deleted_channel.forbidden.app_error", + "translation": "Не удалось обновить порядок сортировки закладок канала." + }, + { + "id": "api.user.view_archived_channels.list_channel_bookmarks_for_channel.app_error", + "translation": "Невозможно получить закладки для заархивированного канала" + }, + { + "id": "app.delete_scheduled_post.delete_permission.error", + "translation": "У вас нет разрешения на удаление этого ресурса." + }, + { + "id": "api.command.execute_command.deleted.error", + "translation": "Невозможно выполнить команду в удаленном канале." } ] diff --git a/server/i18n/sv.json b/server/i18n/sv.json index 42aaeb291e86..9f883887ad19 100644 --- a/server/i18n/sv.json +++ b/server/i18n/sv.json @@ -10295,10 +10295,6 @@ "id": "app.custom_profile_attributes.property_field_delete.app_error", "translation": "Det går inte att ta bort det anpassade profilattributfältet" }, - { - "id": "app.custom_profile_attributes.property_value_update.app_error", - "translation": "Det går inte att uppdatera egenskapens värde" - }, { "id": "app.custom_profile_attributes.search_property_fields.app_error", "translation": "Det går inte att söka efter fälten för anpassade profilattribut" @@ -10359,10 +10355,6 @@ "id": "app.custom_profile_attributes.property_field_update.app_error", "translation": "Det går inte att uppdatera anpassade profilattribut-fältet" }, - { - "id": "app.custom_profile_attributes.property_value_creation.app_error", - "translation": "Det går inte att skapa ett egenskaps-värde" - }, { "id": "app.file_info.undelete_for_post_ids.app_error", "translation": "Misslyckades med att återställa bilagor till postfiler." @@ -10383,10 +10375,6 @@ "id": "app.file_info.get_by_ids.app_error", "translation": "Det går inte att få filinformationen genom id för postredigeringshistorik." }, - { - "id": "app.custom_profile_attributes.property_value_list.app_error", - "translation": "Det går inte att hämta värden för egenskaper" - }, { "id": "api.custom_profile_attributes.license_error", "translation": "Din licens tillåter inte anpassade profil-attribut." diff --git a/server/i18n/tr.json b/server/i18n/tr.json index 609ad67218aa..54e0444514d2 100644 --- a/server/i18n/tr.json +++ b/server/i18n/tr.json @@ -10303,10 +10303,6 @@ "id": "app.custom_profile_attributes.property_field_update.app_error", "translation": "Özel profil özniteliği alanı güncellenemedi" }, - { - "id": "app.custom_profile_attributes.property_value_creation.app_error", - "translation": "Özellik değeri oluşturulamadı" - }, { "id": "app.custom_profile_attributes.property_field_delete.app_error", "translation": "Özel profil özniteliği alanı silinemedi" @@ -10323,14 +10319,6 @@ "id": "api.file.zip_file_reader.app_error", "translation": "Bir zip dosyası okuyucusu alınamadı." }, - { - "id": "app.custom_profile_attributes.property_value_list.app_error", - "translation": "Özellik değerleri alınamadı" - }, - { - "id": "app.custom_profile_attributes.property_value_update.app_error", - "translation": "Özellik değeri güncellenemedi" - }, { "id": "app.file_info.delete_for_post_ids.app_error", "translation": "İstenilen dosyalar veri tabanından kaldırılamadı" @@ -10398,5 +10386,37 @@ { "id": "api.command.execute_command.deleted.error", "translation": "Silinmiş bir kanalda komut yürütülemez." + }, + { + "id": "api.channel.bookmark.update_channel_bookmark.deleted_channel.forbidden.app_error", + "translation": "Kanal yer imi güncellenemedi." + }, + { + "id": "api.custom_profile_attributes.field_not_found", + "translation": "var olmayan bir alana eklenmeye çalışılıyor" + }, + { + "id": "api.user.view_archived_channels.list_channel_bookmarks_for_channel.app_error", + "translation": "Arşivlenmiş bir kanalın yer imleri alınamaz" + }, + { + "id": "app.file_info.get_count.app_error", + "translation": "Tüm dosyaların sayısı alınamadı." + }, + { + "id": "app.file_info.get_storage_usage.app_error", + "translation": "Tüm dosyaların depolama alanı kullanımı alınamadı." + }, + { + "id": "api.channel.bookmark.create_channel_bookmark.deleted_channel.forbidden.app_error", + "translation": "Kanal yer imi eklenemedi." + }, + { + "id": "api.channel.bookmark.delete_channel_bookmark.deleted_channel.forbidden.app_error", + "translation": "Kanal yer imi silinemedi." + }, + { + "id": "api.channel.bookmark.update_channel_bookmark_sort_order.deleted_channel.forbidden.app_error", + "translation": "Kanal yer imi sıralaması güncellenemedi." } ] diff --git a/server/i18n/uk.json b/server/i18n/uk.json index 84a55b303060..159fa05815d2 100644 --- a/server/i18n/uk.json +++ b/server/i18n/uk.json @@ -10350,5 +10350,13 @@ { "id": "app.custom_profile_attributes.property_field_update.app_error", "translation": "Не вдається оновити поле атрибуту користувацького профілю" + }, + { + "id": "app.file_info.get_count.app_error", + "translation": "Не вдалося порахувати всі файли." + }, + { + "id": "api.channel.bookmark.delete_channel_bookmark.deleted_channel.forbidden.app_error", + "translation": "Не вдалося видалити закладку каналу." } ] diff --git a/server/i18n/zh-CN.json b/server/i18n/zh-CN.json index 934d8b946298..55a1b608fe00 100644 --- a/server/i18n/zh-CN.json +++ b/server/i18n/zh-CN.json @@ -10297,18 +10297,6 @@ "id": "app.custom_profile_attributes.property_field_update.app_error", "translation": "无法更新自定义个人资料属性字段" }, - { - "id": "app.custom_profile_attributes.property_value_creation.app_error", - "translation": "无法创建属性值" - }, - { - "id": "app.custom_profile_attributes.property_value_list.app_error", - "translation": "无法获取属性值" - }, - { - "id": "app.custom_profile_attributes.property_value_update.app_error", - "translation": "无法更新属性值" - }, { "id": "app.custom_profile_attributes.search_property_fields.app_error", "translation": "无法搜索自定义个人资料属性字段" @@ -10388,5 +10376,37 @@ { "id": "api.file.zip_file_reader.app_error", "translation": "无法获取 zip 文件读取器。" + }, + { + "id": "api.channel.bookmark.update_channel_bookmark.deleted_channel.forbidden.app_error", + "translation": "更新频道书签失败。" + }, + { + "id": "api.channel.bookmark.update_channel_bookmark_sort_order.deleted_channel.forbidden.app_error", + "translation": "更新频道书签的排列顺序失败。" + }, + { + "id": "api.user.view_archived_channels.list_channel_bookmarks_for_channel.app_error", + "translation": "无法获取已归档频道的书签" + }, + { + "id": "api.channel.bookmark.create_channel_bookmark.deleted_channel.forbidden.app_error", + "translation": "创建频道书签失败。" + }, + { + "id": "api.channel.bookmark.delete_channel_bookmark.deleted_channel.forbidden.app_error", + "translation": "删除频道书签失败。" + }, + { + "id": "api.custom_profile_attributes.field_not_found", + "translation": "尝试更新一个不存在的字段" + }, + { + "id": "app.file_info.get_storage_usage.app_error", + "translation": "获取文件总存储占用失败。" + }, + { + "id": "app.file_info.get_count.app_error", + "translation": "获取文件总数量失败。" } ] diff --git a/webapp/channels/src/i18n/be.json b/webapp/channels/src/i18n/be.json index 354c53b5fc9a..6e4103a5fd9a 100644 --- a/webapp/channels/src/i18n/be.json +++ b/webapp/channels/src/i18n/be.json @@ -3525,7 +3525,6 @@ "reaction.usersAndOthersReacted": "{users} і {otherUsers, number} {otherUsers, plural, one {іншы карыстальнік} few {іншых карыстальнікаў} other {іншых карыстальнікаў}}", "reaction.usersReacted": "{users} і {lastUser}", "reaction.you": "Вы", - "reaction_list.addReactionTooltip": "Дадаць рэакцыю", "remove_group_confirm_button": "Так, выдаліць групу і {memberCount, plural, one {удзельніка} other {удзельнікаў}}", "remove_group_confirm_message": "{memberCount, number} {memberCount, plural, one {удзельнік} few {удзельніка} other {удзельнікаў}} звязаных з гэтай групай будуць выдалены з каманды. Вы ўпэўненыя, што хочаце выдаліць гэтую групу і {memberCount} {memberCount, plural, one {удзельніка} other {удзельнікаў}}?", "remove_group_confirm_title": "Выдаліць групу і {memberCount, number} {memberCount, plural, one {удзельніка} other {удзельнікаў}}", diff --git a/webapp/channels/src/i18n/bg.json b/webapp/channels/src/i18n/bg.json index 0d3e054ee50f..bc1637e7c6e0 100644 --- a/webapp/channels/src/i18n/bg.json +++ b/webapp/channels/src/i18n/bg.json @@ -3946,7 +3946,6 @@ "reaction.usersAndOthersReacted": "{users} и {otherUsers, number} {otherUsers, plural, one {друг потребител} other {други потребителя}}", "reaction.usersReacted": "{users} и {lastUser}", "reaction.you": "Вие", - "reaction_list.addReactionTooltip": "Добави реакция", "remove_group_confirm_button": "Да, премахнете групата и {memberCount, plural, one {члена} other {членовете}}", "remove_group_confirm_message": "{memberCount, number} {memberCount, plural, one {член} other {члена}} свързани с тази група, ще бъдат премахнати от екипа. Сигурни ли сте, че искате да премахнете тази група и {memberCount} {memberCount, plural, one {член} other {члена}}?", "remove_group_confirm_title": "Премахнете групата и {memberCount, number} {memberCount, plural, one {член} other {члена}}", diff --git a/webapp/channels/src/i18n/cs.json b/webapp/channels/src/i18n/cs.json index 935bde80f2a5..b3e3456660d1 100644 --- a/webapp/channels/src/i18n/cs.json +++ b/webapp/channels/src/i18n/cs.json @@ -738,9 +738,9 @@ "admin.data_retention.globalPoliciesTable.description": "Popis", "admin.data_retention.globalPoliciesTable.edit": "Upravit", "admin.data_retention.globalPoliciesTable.files": "Soubory", - "admin.data_retention.globalPolicy.subTitle": "Zachovat zprávy po určitou dobu.", + "admin.data_retention.globalPolicy.subTitle": "Zachovat zprávy a soubory po určitou dobu.", "admin.data_retention.globalPolicy.title": "Globální zásady uchovávání", - "admin.data_retention.globalPolicyTitle": "Globální zásady uchovávání", + "admin.data_retention.globalPolicyTitle": "Globální Zásady Uchovávání", "admin.data_retention.global_policy.form.numberError": "Musíte přidat číslo větší nebo rovno 1.", "admin.data_retention.jobCreation.subTitle": "Denní protokol zpráv a souborů odstraněných na základě výše definovaných zásad.", "admin.data_retention.jobCreation.title": "Záznam o zásadách", @@ -749,15 +749,15 @@ "admin.data_retention.retention_days": "{count} {count, plural, one {den} other {dny}}", "admin.data_retention.retention_hours": "{count} {count, plural, one {hodina} few {hodiny} other {hodin}}", "admin.data_retention.retention_years": "{count} {count, plural, one {rok} other {roky}}", - "admin.data_retention.settings.title": "Zásady uchovávání údajů", - "admin.data_retention.title": "Zásady uchovávání údajů", + "admin.data_retention.settings.title": "Zásady Uchovávání Dat", + "admin.data_retention.title": "Zásada uchovávání údajů", "admin.data_retention_feature_discovery.copy": "Uchovávejte svá data pouze tak dlouho, jak je potřebujete. Vytvořte úkoly pro uchovávání dat pro vybrané kanály a týmy, které automaticky odstraní nepotřebná data.", "admin.data_retention_feature_discovery.title": "Vytváření plánů uchovávání dat pomocí Mattermost Enterprise", - "admin.database.migrations_table.help_text": "Seznam migrací schémat použitých na váš datový sklad.", + "admin.database.migrations_table.help_text": "Seznam schémat migrací aplikovaných pro vaše datové úložiště.", "admin.database.migrations_table.name": "Jméno", "admin.database.migrations_table.title": "Aplikované migrace schémat", "admin.database.migrations_table.version": "Verze", - "admin.database.search_backend.help_text": "Zobrazuje aktuálně aktivní backend používaný pro vyhledávání. Hodnoty mohou být none, database, elasticsearch, bleve atd.", + "admin.database.search_backend.help_text": "Zobrazuje aktuálně aktivní backend používaný pro vyhledávání. Hodnoty mohou být žádná, database, elasticsearch, bleve atd.", "admin.database.search_backend.title": "Backend aktivního vyhledávání", "admin.database.title": "Databáze", "admin.developer.title": "Nastavení vývojáře", @@ -765,24 +765,24 @@ "admin.elasticsearch.backendExample": "Např.: „elasticsearch“", "admin.elasticsearch.backendTitle": "Typ backendu:", "admin.elasticsearch.bulkIndexingTitle": "Hromadná indexace:", - "admin.elasticsearch.caDescription": "(Volitelné) Vlastní certifikáty certifikační autority pro server Elasticsearch. Pokud chcete použít výchozí certifikační autority z operačního systému, ponechte tuto položku prázdnou.", + "admin.elasticsearch.caDescription": "(Volitelné) Vlastní certifikáty autority (CA) pro server Elasticsearch. Nechte prázdné pro použití výchozích certifikátů CA z operačního systému.", "admin.elasticsearch.caExample": "Např.: \"./elasticsearch/ca.pem\"", "admin.elasticsearch.caTitle": "Cesta CA:", "admin.elasticsearch.channelIndexRebuildJobTitle": "Úloha obnovy indexu kanálů.", - "admin.elasticsearch.clientCertDescription": "(Nepovinné) Klientský certifikát pro připojení k serveru Elasticsearch ve formátu PEM.", - "admin.elasticsearch.clientCertExample": "Např.: \"./elasticsearch/client-cert.pem\"", + "admin.elasticsearch.clientCertDescription": "(Volitelné) Klientský certifikát pro připojení k serveru Elasticsearch ve formátu PEM.", + "admin.elasticsearch.clientCertExample": "Např.: „./elasticsearch/client-cert.pem“", "admin.elasticsearch.clientCertTitle": "Cesta k certifikátu klienta:", - "admin.elasticsearch.clientKeyDescription": "(Nepovinné) Klíč pro klientský certifikát ve formátu PEM.", - "admin.elasticsearch.clientKeyExample": "Např.: \"./elasticsearch/client-cert.pem\"", - "admin.elasticsearch.clientKeyTitle": "Cesta k certifikátu klienta:", + "admin.elasticsearch.clientKeyDescription": "(Volitelné) Klíč pro klientský certifikát ve formátu PEM.", + "admin.elasticsearch.clientKeyExample": "Např.: \"./elasticsearch/client-key.pem\"", + "admin.elasticsearch.clientKeyTitle": "Cesta ke klíči certifikátu klienta:", "admin.elasticsearch.connectionUrlDescription": "Adresa serveru Elasticsearch. {documentationLink}", "admin.elasticsearch.connectionUrlExample": "Např.: \"https://elasticsearch.example.org:9200\"", "admin.elasticsearch.connectionUrlTitle": "Adresa připojení k serveru:", - "admin.elasticsearch.createJob.help": "Všichni uživatelé, kanály a příspěvky v databázi budou indexováni od nejstaršího po nejnovější. Vyhledávání Bleve je dostupné v průběhu indexace, ale výsledky mohou být nekompletní dokud indexace nedoběhne.", + "admin.elasticsearch.createJob.help": "Všichni uživatelé, kanály a příspěvky v databázi budou indexováni od nejstarších po nejnovější. Elasticsearch je dostupný během indexování, ale výsledky vyhledávání mohou být neúplné, dokud nebude úloha indexování dokončena.", "admin.elasticsearch.createJob.title": "Zaindexovat nyní", - "admin.elasticsearch.elasticsearch_test_button": "Testovací připojení", - "admin.elasticsearch.enableAutocompleteDescription": "Vyžaduje úspěšné připojení k Elasticsearch serveru. Pokud zapnuto Elasticsearch bude použit pro všechny vyhledávací dotazy s použitím nejnovějšího indexu. Výsledky hledání mohou být neúplné, dokud se nedokončí hromadné indexování stávající databáze. Pokud je vypnuto použije se standartní databázové vyhledávání.", - "admin.elasticsearch.enableAutocompleteTitle": "Povolit Elasticsearch pro vyhledávací dotazy:", + "admin.elasticsearch.elasticsearch_test_button": "Otestovat připojení", + "admin.elasticsearch.enableAutocompleteDescription": "Vyžaduje úspěšné připojení k serveru Elasticsearch. Pokud je nastaveno na \"true\", Elasticsearch bude použit pro všechny dotazy pro automatické doplňování uživatelů a kanálů pomocí nejnovějšího indexu. Výsledky automatického doplňování mohou být neúplné, dokud nebude dokončen hromadný index stávající databáze uživatelů a kanálů. Pokud je nastaveno na \"false\", používá se pro automatické doplňování databáze.", + "admin.elasticsearch.enableAutocompleteTitle": "Povolit Elasticsearch pro dotazy na automatické doplňování:", "admin.elasticsearch.enableIndexingDescription": "Pokud je pravda, indexování nových příspěvků nastane automaticky. Vyhledávací dotazy budou používat vyhledávání v databázi, dokud nebude zapnuto \"Enable Elasticsearch for search queries\". {documentationLink}", "admin.elasticsearch.enableIndexingTitle": "Povolit indexování Elasticsearch:", "admin.elasticsearch.enableSearchingDescription": "Vyžaduje úspěšné připojení k Elasticsearch serveru. Pokud zapnuto Elasticsearch bude použit pro všechny vyhledávací dotazy s použitím nejnovějšího indexu. Výsledky hledání mohou být neúplné, dokud se nedokončí hromadné indexování stávající databáze. Pokud je vypnuto použije se standartní databázové vyhledávání.", @@ -2903,6 +2903,7 @@ "analytics.system.infoAndSkippedIntensiveQueries1": "Použijte data pouze pro vybraný tým. Vyloučit příspěvky v kanálech přímých zpráv, které nejsou vázány na tým.", "analytics.system.infoAndSkippedIntensiveQueries2": "Pro maximalizaci výkonu jsou některé statistiky vypnuty. Můžete je znovu povolit v souboru config.json.", "analytics.system.monthlyActiveUsers": "Měsíční počet aktivních uživatelů", + "analytics.system.perDayStatistics": "Načíst pokročilé statistiky", "analytics.system.postTypes": "Zprávy, soubory a hashtagy", "analytics.system.privateGroups": "Privátní kanál", "analytics.system.publicChannels": "Veřejné kanály", @@ -2912,6 +2913,8 @@ "analytics.system.totalBotPosts": "Množstvý zpráv od botů", "analytics.system.totalChannels": "Celkový počet kanálů", "analytics.system.totalCommands": "Celkový počet příkazů", + "analytics.system.totalFiles": "Celkový počet souborů", + "analytics.system.totalFilesSize": "Celková velikost souborů", "analytics.system.totalIncomingWebhooks": "Příchozí webhooky", "analytics.system.totalMasterDbConnections": "Celkový počet připojení k hlavní databázi", "analytics.system.totalOutgoingWebhooks": "Odchozí webové háčky", @@ -4847,7 +4850,6 @@ "reaction_limit_reached_modal.body": "Jejda! Zdá se, že jsme u této zprávy narazili na strop reakcí na emotikony. Obraťte se na správce systému ohledně jakýchkoli úprav tohoto limitu.", "reaction_limit_reached_modal.body.admin": "Jejda! Zdá se, že jsme u této zprávy narazili na strop reakcí na emotikony. Nastavili jsme limit, abychom na vašem serveru zajistili hladký chod. Jako správce systému můžete tento limit upravit z systémové konzole.", "reaction_limit_reached_modal.title": "Dosáhli jste limitu reakcí", - "reaction_list.addReactionTooltip": "Přidat reakci", "remove_group_confirm_button": "Ano, odstranit skupinu a {memberCount, plural, one {člena} other {členy}}", "remove_group_confirm_message": "{memberCount, number} {memberCount, plural, one {člen} other {členů}} přidružených k této skupině budou odstraněni z týmu při další synchronizaci AD/LDAP. Jste si jisti, že chcete odstranit tuto skupinu a {memberCount} {memberCount, plural, one {člena} other {členy}}?", "remove_group_confirm_title": "Odstranit skupinu a {memberCount, number} {memberCount, plural, one {člen} other {členy}}", diff --git a/webapp/channels/src/i18n/de.json b/webapp/channels/src/i18n/de.json index f51d9b28a43d..f5e598a410ff 100644 --- a/webapp/channels/src/i18n/de.json +++ b/webapp/channels/src/i18n/de.json @@ -2903,6 +2903,7 @@ "analytics.system.infoAndSkippedIntensiveQueries1": "Verwende Daten nur für das ausgewählte Team. Schließe Nachrichten an Direktnachrichten-Kanäle aus, die nicht an dieses Team gebunden sind.", "analytics.system.infoAndSkippedIntensiveQueries2": "Um die Performanz zu maximieren, sind einige Statistiken deaktiviert. Du kannst diese in der config.json reaktivieren.", "analytics.system.monthlyActiveUsers": "Monatliche aktive Benutzer", + "analytics.system.perDayStatistics": "Erweiterte Statistik laden", "analytics.system.postTypes": "Nachrichten, Dateien und Hashtags", "analytics.system.privateGroups": "Private Kanäle", "analytics.system.publicChannels": "Öffentliche Kanäle", @@ -2912,6 +2913,8 @@ "analytics.system.totalBotPosts": "Anzahl der Nachrichten von Bots", "analytics.system.totalChannels": "Kanäle Gesamt", "analytics.system.totalCommands": "Befehle Gesamt", + "analytics.system.totalFiles": "Dateien gesamt", + "analytics.system.totalFilesSize": "Gesamtgröße der Dateien", "analytics.system.totalIncomingWebhooks": "Eingehende Webhooks", "analytics.system.totalMasterDbConnections": "Master DB Verbindungen", "analytics.system.totalOutgoingWebhooks": "Ausgehende Webhooks", @@ -4847,7 +4850,6 @@ "reaction_limit_reached_modal.body": "Ups! Es sieht so aus, als ob wir eine Obergrenze für Emoji-Reaktionen für diese Nachricht erreicht haben. Bitte wende dich an deinen Systemadministrator, wenn du dieses Limit ändern möchtest.", "reaction_limit_reached_modal.body.admin": "Ups! Es sieht so aus, als hätten wir eine Obergrenze an Emoji-Reaktionen für diese Nachricht erreicht. Wir haben ein Limit gesetzt, damit die Dinge auf deinem Server reibungslos laufen. Als Systemadministrator kannst du dieses Limit über die Systemkonsole anpassen.", "reaction_limit_reached_modal.title": "Du hast das Reaktionslimit erreicht", - "reaction_list.addReactionTooltip": "Reaktion hinzufügen", "remove_group_confirm_button": "Ja, Gruppe und {memberCount, plural, one {Mitglied} other {Mitglieder}} entfernen", "remove_group_confirm_message": "{memberCount, number} {memberCount, plural, one {Mitglied, das} other {Mitglieder, die}} dieser Gruppe zugeordnet {memberCount, plural, one {ist, wird} other {sind, werden}} aus dem Team entfernt. Bist du sicher, dass du diese Gruppe und {memberCount, number} {memberCount, plural, one {Mitglied} other {Mitglieder}} entfernen möchtest?", "remove_group_confirm_title": "Gruppe und {memberCount, number} {memberCount, plural, one {Mitglied} other {Mitglieder}} entfernen", diff --git a/webapp/channels/src/i18n/en-AU.json b/webapp/channels/src/i18n/en-AU.json index 43ac4e8d4fa9..344301239d98 100644 --- a/webapp/channels/src/i18n/en-AU.json +++ b/webapp/channels/src/i18n/en-AU.json @@ -4847,7 +4847,6 @@ "reaction_limit_reached_modal.body": "The maximum number of emoji reactions for a single message has been reached. Please contact your system administrator for any adjustments to this limit.", "reaction_limit_reached_modal.body.admin": "The maximum number of emoji reactions for a single message has been reached. A limit has been set to keep things running smoothly on your server. As a system administrator, you can adjust this limit from the system console.", "reaction_limit_reached_modal.title": "You've reached the reaction limit", - "reaction_list.addReactionTooltip": "Add a reaction", "remove_group_confirm_button": "Yes, Remove Group and {memberCount, plural, one {Member} other {Members}}", "remove_group_confirm_message": "{memberCount, number} {memberCount, plural, one {member} other {members}} associated to this group will be removed from the team. Are you sure you wish to remove this group and {memberCount} {memberCount, plural, one {member} other {members}}?", "remove_group_confirm_title": "Remove Group and {memberCount, number} {memberCount, plural, one {Member} other {Members}}", diff --git a/webapp/channels/src/i18n/es.json b/webapp/channels/src/i18n/es.json index 6ca5ceee0f01..8ac6976e0912 100644 --- a/webapp/channels/src/i18n/es.json +++ b/webapp/channels/src/i18n/es.json @@ -3842,7 +3842,6 @@ "reaction.usersAndOthersReacted": "{users} y {otherUsers, number} {otherUsers, plural, one {otro usuario} other {otros usuarios}}", "reaction.usersReacted": "{users} y {lastUser}", "reaction.you": "Tu", - "reaction_list.addReactionTooltip": "Reaccionar", "remove_group_confirm_button": "Sí, Eliminar Grupo y {memberCount, plural, one {Miembros} other {Miembros}}", "remove_group_confirm_message": "{memberCount, number} {memberCount, plural, one {miembro} other {miembros}} asociados a este grupo serán eliminados del equipo en la próxima sincronización de AD/LDAP. Está usted seguro de que desea eliminar este grupo y {memberCount} {memberCount, plural, one {miembro} other {miembros}}?", "remove_group_confirm_title": "Eliminar Grupo y {memberCount, number} {memberCount, plural, one {Miembro} other {Miembros}}", diff --git a/webapp/channels/src/i18n/fa.json b/webapp/channels/src/i18n/fa.json index 80df6fc31d40..e888b75ad1fc 100644 --- a/webapp/channels/src/i18n/fa.json +++ b/webapp/channels/src/i18n/fa.json @@ -3478,7 +3478,6 @@ "reaction.usersAndOthersReacted": "{users} and {otherUsers, number} other {otherUsers, plural, one {user} other {users}}", "reaction.usersReacted": "{users} و {lastUser}", "reaction.you": "شما", - "reaction_list.addReactionTooltip": "یک واکنش اضافه کنید", "remove_group_confirm_button": "بله، حذف گروه و {memberCount, plural, one {Member} سایر {Members}}", "remove_group_confirm_message": "{memberCount, number} {memberCount, plural, one {member} {members}} دیگر مرتبط با این گروه از تیم حذف خواهد شد. آیا مطمئن هستید که می خواهید این گروه و {memberCount} {memberCount, plural, one {member} other {members}} را حذف کنید؟", "remove_group_confirm_title": "حذف گروه و {memberCount, number} {memberCount, plural, one {Member} سایر {Members}}", diff --git a/webapp/channels/src/i18n/fr.json b/webapp/channels/src/i18n/fr.json index 2ae179eb7501..03afa42dc06a 100644 --- a/webapp/channels/src/i18n/fr.json +++ b/webapp/channels/src/i18n/fr.json @@ -4029,7 +4029,6 @@ "reaction.you": "Vous", "reaction_limit_reached_modal.body": "Oups ! Il semblerait que nous ayons atteint la limite des réactions émoticônes pour ce message. Veuillez contacter votre administrateur système pour ajuster cette limite.", "reaction_limit_reached_modal.body.admin": "Oups ! Il semblerait que nous ayons atteint la limite des réactions émoticônes pour ce message. Nous avons configuré une limite pour s'assurer que votre serveur tourne correctement. En tant qu'administrateur système, vous pouvez ajuster cette limite depuis la console système.", - "reaction_list.addReactionTooltip": "Ajouter une réaction", "remove_group_confirm_button": "Oui, supprimer le groupe et {memberCount, plural, one {membre} other {membres}}", "remove_group_confirm_message": "{memberCount, number} {memberCount, plural, one {membre associé} other {membres associés}} à ce groupe {memberCount, plural, one {va être supprimé} other {vont être supprimés}} de l'équipe. Voulez-vous vraiment supprimer ce groupe et {memberCount} {memberCount, plural, one {membre} other {membres}} ?", "remove_group_confirm_title": "Supprimer le groupe et {memberCount, number} {memberCount, plural, one {membre} other {membres}}", diff --git a/webapp/channels/src/i18n/fy.json b/webapp/channels/src/i18n/fy.json index 12e2bcc6855c..e6a86ee558ca 100644 --- a/webapp/channels/src/i18n/fy.json +++ b/webapp/channels/src/i18n/fy.json @@ -3744,7 +3744,6 @@ "reaction.usersAndOthersReacted": "{users} en {otherUsers, number} other {otherUsers, plural, one {gebruikers} other {gebruikers}}", "reaction.usersReacted": "{users} en {lastUser}", "reaction.you": "Jij", - "reaction_list.addReactionTooltip": "Reactie toevoegen", "remove_group_confirm_button": "Ja, verwijder groep en {memberCount, plural, one {lid} other {leden}}", "remove_group_confirm_message": "{memberCount, number} {memberCount, plural, one {lid} other {leden}} verbonden met deze groep worden verwijderd van het groep. Ben je zeker dat je deze groep wil verwijderen {memberCount} {memberCount, plural, one {lid} other {leden}}?", "remove_group_confirm_title": "Verwijder Groep en {memberCount, number} {memberCount, plural, one {Member} other {Members}}", diff --git a/webapp/channels/src/i18n/hr.json b/webapp/channels/src/i18n/hr.json index 2493453839a0..b3d6d3fbc9c7 100644 --- a/webapp/channels/src/i18n/hr.json +++ b/webapp/channels/src/i18n/hr.json @@ -1390,7 +1390,6 @@ "quick_switch_modal.switchChannels": "Pronađi kanale", "reaction.add.ariaLabel": "Dodaj reakciju", "reaction.container.ariaLabel": "reakcije", - "reaction_list.addReactionTooltip": "Dodaj reakciju", "removed_channel.okay": "U redu", "removed_channel.someone": "Netko", "rename_category_modal.rename": "Preimenuj", diff --git a/webapp/channels/src/i18n/hu.json b/webapp/channels/src/i18n/hu.json index cdbbefd4eeb8..c6d4c7ee13ec 100644 --- a/webapp/channels/src/i18n/hu.json +++ b/webapp/channels/src/i18n/hu.json @@ -3709,7 +3709,6 @@ "reaction.usersAndOthersReacted": "{users} és {otherUsers, number} további felhasználó", "reaction.usersReacted": "{users} és {lastUser}", "reaction.you": "Ön", - "reaction_list.addReactionTooltip": "Reakció hozzáadása", "remove_group_confirm_button": "Igen, távolítsa el a csoportot és a {memberCount, plural, one {tagját} other {tagjait}}", "remove_group_confirm_message": "Ehhez a csoporthoz rendelt {memberCount, number} tag el lesz távolítva a csapatból. Biztosan el akarja távolítani ezt a csoportot és {memberCount} tagot?", "remove_group_confirm_title": "Csoport és {memberCount, number} tag eltávolítása", diff --git a/webapp/channels/src/i18n/it.json b/webapp/channels/src/i18n/it.json index 451fcf51db7c..0df97d034a91 100644 --- a/webapp/channels/src/i18n/it.json +++ b/webapp/channels/src/i18n/it.json @@ -2902,7 +2902,6 @@ "reaction.usersAndOthersReacted": "{users} e {otherUsers, number} other {otherUsers, plural, one {user} other {users}}", "reaction.usersReacted": "{users} e {lastUser}", "reaction.you": "Tu", - "reaction_list.addReactionTooltip": "Aggiungi reazione", "remove_group_confirm_button": "Si, Cancella il gruppo e {memberCount, plural, one {Member} other {Members}}", "remove_group_confirm_message": "{memberCount, number} {memberCount, plural, one {member} other {members}} associati a questo gruppo saranno rimossi dalla squadra. Sicuro di voler rimuovere questo gruppo e {memberCount} {memberCount, plural, one {member} other {members}}?", "remove_group_confirm_title": "Rimuovi gruppo e {memberCount, number} {memberCount, plural, one {Member} other {Members}}", diff --git a/webapp/channels/src/i18n/ja.json b/webapp/channels/src/i18n/ja.json index 3d3a838f3b7f..a4aa32da4b05 100644 --- a/webapp/channels/src/i18n/ja.json +++ b/webapp/channels/src/i18n/ja.json @@ -4847,7 +4847,6 @@ "reaction_limit_reached_modal.body": "おっと!このメッセージに対する絵文字リアクションの上限に達したようです。この制限を変更する場合、システム管理者にお問い合わせください。", "reaction_limit_reached_modal.body.admin": "おっと!このメッセージの絵文字リアクションの上限に達したようです。サーバーのスムーズな稼動を維持するため、上限が設定されています。システム管理者として、システムコンソール からこの上限を調整することができます。", "reaction_limit_reached_modal.title": "リアクションの上限に達しました", - "reaction_list.addReactionTooltip": "リアクションを追加する", "remove_group_confirm_button": "はい、グループと {memberCount, plural, one {メンバー} other {メンバー}}を削除します", "remove_group_confirm_message": "このグループに関連づけられた {memberCount, number} {memberCount, plural, one {メンバー} other {メンバー}} は、このチームから削除されます。本当にこのグループと{memberCount} {memberCount, plural, one {メンバー} other {メンバー}} をこのグループから削除しますか?", "remove_group_confirm_title": "グループと{memberCount, number} {memberCount, plural, one {メンバー} other {メンバー}} を削除します", diff --git a/webapp/channels/src/i18n/ko.json b/webapp/channels/src/i18n/ko.json index 8d32ec983ae9..0d8a31311a60 100644 --- a/webapp/channels/src/i18n/ko.json +++ b/webapp/channels/src/i18n/ko.json @@ -2703,7 +2703,6 @@ "reaction.usersAndOthersReacted": "{users}와 {otherUsers, number}명의 {otherUsers, plural, one {user} other {users}}", "reaction.usersReacted": "{users} 및 {lastUser}", "reaction.you": "당신", - "reaction_list.addReactionTooltip": "반응 추가", "removed_channel.channelName": "채널", "removed_channel.okay": "확인", "removed_channel.remover": "{remover}님이 {channel}에서 당신을 내보냈습니다", diff --git a/webapp/channels/src/i18n/lt.json b/webapp/channels/src/i18n/lt.json index c5a1fdc07ebc..5b3d52a2a1ce 100644 --- a/webapp/channels/src/i18n/lt.json +++ b/webapp/channels/src/i18n/lt.json @@ -3934,7 +3934,6 @@ "reaction.usersAndOthersReacted": "{users} ir {otherUsers, number} kitas (-i) {otherUsers, plural, one {user} other {users}}", "reaction.usersReacted": "{users} ir {lastUser}", "reaction.you": "Jūs", - "reaction_list.addReactionTooltip": "Pridėti reakciją", "remove_group_confirm_button": "Taip, pašalinti grupę ir {memberCount, plural, one {Member} other {Members}}", "remove_group_confirm_message": "{memberCount, number} {memberCount, plural, one {member} other {members}} susietas su šia grupe, bus pašalintas iš komandos. Ar tikrai norite pašalinti šią grupę ir {memberCount} {memberCount, plural, one {member} other {members}}?", "remove_group_confirm_title": "Pašalinti grupę ir {memberCount, number} {memberCount, plural, dar vieną {Member} {Members}}", diff --git a/webapp/channels/src/i18n/nb-NO.json b/webapp/channels/src/i18n/nb-NO.json index ac51775226ce..d45aac5e2f33 100644 --- a/webapp/channels/src/i18n/nb-NO.json +++ b/webapp/channels/src/i18n/nb-NO.json @@ -2871,11 +2871,11 @@ "reaction.reactionVerb.you": "reagerte", "reaction.reactionVerb.youAndUsers": "reagerte", "reaction.removeReact.ariaLabel": "fjern reaksjon", + "reaction.usersReacted": "{users} og {lastUser}", "reaction.you": "Deg", "reaction_limit_reached_modal.body": "Oops! Det ser ut til at vi har truffet et tak for emoji-reaksjoner for denne meldingen. Kontakt systemadministratoren din for justeringer av denne grensen.", "reaction_limit_reached_modal.body.admin": "Oops! Det ser ut til at vi har truffet et tak for emoji-reaksjoner for denne meldingen. Vi har satt en grense for at ting skal fungere problemfritt på serveren din. Som systemadministrator kan du justere denne grensen fra systemkonsollen.", "reaction_limit_reached_modal.title": "Du har nådd reaksjonsgrensen", - "reaction_list.addReactionTooltip": "Legg til en reaksjon", "removed_channel.channelName": "kanalen", "removed_channel.from": "Fjernet fra ", "removed_channel.okay": "Ok", @@ -2884,8 +2884,24 @@ "rename_category_modal.rename": "Endre navn", "rename_category_modal.renameCategory": "Gi kategorien nytt navn", "rename_channel.cancel": "Avbryt", + "rename_channel.defaultError": " - Kan ikke endres for standardkanalen", + "rename_channel.displayName": "Visningsnavn", + "rename_channel.displayNameHolder": "Skriv inn visningsnavn", + "rename_channel.maxLength": "Dette feltet må inneholde mindre enn {maxLength, number} tegn", + "rename_channel.minLength": "Visningsnavnet må inneholde minst {minLength, number} tegn.", "rename_channel.save": "Lagre", "rename_channel.title": "Endre navn på kanalen", + "rename_channel.url": "URL", + "restricted_indicator.tooltip.mesage": "Gjennom prøveperioden kan du bruke denne funksjonen.", + "restricted_indicator.tooltip.message.blocked": "Dette er en betalt funksjon, tilgjengelig med en gratis prøveperiode på {trialLength}", + "restricted_indicator.tooltip.title": "{minimumPlanRequiredForFeature} funksjon", + "revoke_user_sessions_modal.desc": "Denne handlingen opphever alle sesjoner for {username}. De vil bli logget ut fra alle enheter. Er du sikker på at du vil oppheve alle sesjoner for {username}?", + "revoke_user_sessions_modal.revoke": "Tilbakekall", + "revoke_user_sessions_modal.title": "Tilbakekall sesjoner for {username}", + "rhs_card.jump": "Hopp", + "rhs_card.message_by": "Melding fra {avatar} {user}", + "rhs_header.back.icon": "Tilbake-ikon", + "rhs_header.backToFlaggedTooltip": "Tilbake til lagrede meldinger", "rhs_header.backToPinnedTooltip": "Tilbake til festede meldinger", "rhs_header.backToResultsTooltip": "Tilbake til søkeresultater", "rhs_header.closeSidebarTooltip": "Lukk", @@ -2911,6 +2927,7 @@ "schedule_post.custom_time_modal.cancel_button_text": "Avbryt", "schedule_post.custom_time_modal.dm_user_time": "{dmUserTime} for {dmUserName}", "schedule_post.custom_time_modal.title": "Tidspunkt for sending av melding", + "scheduled_post.action.copy_text": "Kopier tekst", "scheduled_post.action.reschedule": "Planlegg tidspunkt for innlegget på nytt", "scheduled_post.action.send_now": "Send nå", "scheduled_post.channel_indicator.link_to_scheduled_posts.text": "Se alle.", @@ -2931,6 +2948,7 @@ "scheduled_post.error_code.user_deleted": "Bruker er slettet", "scheduled_post.error_code.user_missing": "Bruker er slettet", "scheduled_post.panel.error_indicator.message": "Ett av de planlagte utkastene dine kan ikke sendes.", + "scheduled_post.panel.header.time": "Send {isTodayOrTomorrow, select, true {} other {on}} {scheduledDateTime}", "scheduled_posts.row_title_channel.placeholder": "I: {icon} Ingen destinasjon", "scheduled_posts.row_title_thread.placeholder": "Tråd til: {icon} Ingen destinasjon", "scheduled_posts.row_title_thread.placeholder_tooltip": "Kanalen eksisterer enten ikke eller så har du ikke tilgang til den.", @@ -2950,9 +2968,16 @@ "search_bar.usage.search_type_messages": "Meldinger", "search_bar.usage.search_type_question": "Hva søker du etter?", "search_bar.usage.title": "Søkealternativer", + "search_bar.usage.title_files": "Alternativer for filsøk", + "search_bar.usage.title_messages": "Alternativer for meldingssøk", + "search_bar.users": "Brukere", + "search_files_list_option.after": "Filer etter en dato", + "search_files_list_option.before": "Filer før en dato", + "search_files_list_option.exclude": "ekskluder søkeord", "search_files_list_option.from": "Filer fra en bruker", "search_files_list_option.in": "Filer i en kanal", "search_files_list_option.on": "Filer på en bestemt dato", + "search_files_list_option.phrases": "Filer med fraser", "search_header.channelFiles": "Filer", "search_header.loading": "Søker...", "search_header.pinnedMessages": "Festede meldinger", @@ -2964,6 +2989,7 @@ "search_header.title_edit.history": "Rediger historikk", "search_hint.enter_to_search": "Trykk Enter for å søke", "search_hint.enter_to_select": "Trykk Enter for å velge", + "search_hint.filter": "Filtrer søket ditt med:", "search_item.channelArchived": "Arkivert", "search_item.file_tag.direct_message": "Direktemelding", "search_item.file_tag.group_message": "Gruppemelding", diff --git a/webapp/channels/src/i18n/nl.json b/webapp/channels/src/i18n/nl.json index 1b26df412c7c..3fc70765f1a1 100644 --- a/webapp/channels/src/i18n/nl.json +++ b/webapp/channels/src/i18n/nl.json @@ -2903,6 +2903,7 @@ "analytics.system.infoAndSkippedIntensiveQueries1": "Gebruik alleen gegevens voor het gekozen team. Sluit berichten in kanalen voor rechtstreekse berichten uit die niet aan een team gebonden zijn.", "analytics.system.infoAndSkippedIntensiveQueries2": "Om de prestaties te maximaliseren, zijn sommige statistieken uitgeschakeld. Je kan deze opnieuw inschakelen in config.json.", "analytics.system.monthlyActiveUsers": "Maandelijks Actieve Gebruikers", + "analytics.system.perDayStatistics": "Geavanceerde statistieken laden", "analytics.system.postTypes": "Berichten, bestanden en hashtags", "analytics.system.privateGroups": "Privé-kanalen", "analytics.system.publicChannels": "Publieke kanalen", @@ -2912,6 +2913,8 @@ "analytics.system.totalBotPosts": "Totaal aantal berichten van Bots", "analytics.system.totalChannels": "Totaal aantal kanalen", "analytics.system.totalCommands": "Totaal aantal opdrachten", + "analytics.system.totalFiles": "Totaal Bestanden", + "analytics.system.totalFilesSize": "Totale bestandsgrootte", "analytics.system.totalIncomingWebhooks": "Inkomende webhooks", "analytics.system.totalMasterDbConnections": "Meester DB Conns", "analytics.system.totalOutgoingWebhooks": "Uitgaande webhooks", @@ -4847,7 +4850,6 @@ "reaction_limit_reached_modal.body": "Oeps! Het lijkt erop dat we het maximum aantal emoji-reacties voor dit bericht hebben bereikt. Neem contact op met jouw systeembeheerder voor eventuele aanpassingen aan deze limiet.", "reaction_limit_reached_modal.body.admin": "Oeps! Het lijkt erop dat we het maximum aantal emoji-reacties voor dit bericht hebben bereikt. We hebben een limiet ingesteld om alles soepel te laten verlopen op je server. Als systeembeheerder kan je deze limiet aanpassen via de systeemconsole.", "reaction_limit_reached_modal.title": "Je hebt de limiet voor het aantal reacties bereikt", - "reaction_list.addReactionTooltip": "Reactie toevoegen", "remove_group_confirm_button": "Ja, verwijder groep en {memberCount, plural, one {lid} other {leden}}", "remove_group_confirm_message": "{memberCount, number} {memberCount, plural, one {lid} other {leden}} verbonden met deze groep worden verwijderd van het groep. Ben je zeker dat je deze groep wil verwijderen {memberCount} {memberCount, plural, one {lid} other {leden}}?", "remove_group_confirm_title": "Verwijder Groep en {memberCount, number} {memberCount, plural, one {Member} other {Members}}", diff --git a/webapp/channels/src/i18n/pl.json b/webapp/channels/src/i18n/pl.json index a25b75329e68..54330f0acc3e 100644 --- a/webapp/channels/src/i18n/pl.json +++ b/webapp/channels/src/i18n/pl.json @@ -1887,7 +1887,8 @@ "admin.permissions.sysconsole_section_user_management_channels.name": "Kanały", "admin.permissions.sysconsole_section_user_management_groups.name": "Grupy", "admin.permissions.sysconsole_section_user_management_permissions.name": "Uprawnienia", - "admin.permissions.sysconsole_section_user_management_system_roles.name": "Role Systemowe", + "admin.permissions.sysconsole_section_user_management_system_roles.description": "Ustawienie \"Brak Dostępu\" ogranicza dostęp tylko do interfejsu konsoli systemowej. Bazowe punkty końcowe API są dostępne dla wszystkich użytkowników w stanie tylko do odczytu dla podstawowych funkcji produktu.", + "admin.permissions.sysconsole_section_user_management_system_roles.name": "Delegowana administracja szczegółowa", "admin.permissions.sysconsole_section_user_management_teams.name": "Zespoły", "admin.permissions.sysconsole_section_user_management_users.description": "Nie można zresetować haseł administratora", "admin.permissions.sysconsole_section_user_management_users.name": "Użytkownicy", @@ -2577,9 +2578,11 @@ "admin.system_properties.user_properties.table.actions": "Działania", "admin.system_properties.user_properties.table.actions.delete": "Usuń", "admin.system_properties.user_properties.table.property": "Właściwość", + "admin.system_properties.user_properties.table.property_name.input.name": "Nazwa właściwości", "admin.system_properties.user_properties.table.type": "Typ", "admin.system_properties.user_properties.table.type.text": "Tekst", "admin.system_properties.user_properties.table.validation.name_required": "Wprowadź nazwę właściwości.", + "admin.system_properties.user_properties.table.validation.name_taken": "Nazwa właściwości jest już zajęta.", "admin.system_properties.user_properties.table.validation.name_unique": "Nazwy właściwości muszą być unikalne.", "admin.system_properties.user_properties.title": "Właściwości użytkownika", "admin.system_roles_feature_discovery.copy": "Przypisz konfigurowalne role administratora, aby dać wyznaczonym użytkownikom dostęp do odczytu i/lub zapisu w wybranych sekcjach konsoli systemowej.", @@ -2900,6 +2903,7 @@ "analytics.system.infoAndSkippedIntensiveQueries1": "Użyj danych tylko dla wybranego zespołu. Wyklucz posty w kanałach wiadomości bezpośrednich, które nie są powiązane z zespołem.", "analytics.system.infoAndSkippedIntensiveQueries2": "Aby zmaksymalizować wydajność, niektóre statystyki są wyłączone. Możesz ponownie je włączyć w config.json.", "analytics.system.monthlyActiveUsers": "Miesięczna Aktywność Użytkowników", + "analytics.system.perDayStatistics": "Załaduj zaawansowane statystyki", "analytics.system.postTypes": "Wiadomości, Pliki i Hashtagi", "analytics.system.privateGroups": "Kanały prywatne", "analytics.system.publicChannels": "Kanały publiczne", @@ -2909,6 +2913,8 @@ "analytics.system.totalBotPosts": "Ilość postów dodanych przez boty", "analytics.system.totalChannels": "Wszystkie Kanały", "analytics.system.totalCommands": "Wszystkie Polecenia", + "analytics.system.totalFiles": "Pliki ogółem", + "analytics.system.totalFilesSize": "Całkowity rozmiar plików", "analytics.system.totalIncomingWebhooks": "Przychodzące Webhooki", "analytics.system.totalMasterDbConnections": "Połączenia bazą danych Master", "analytics.system.totalOutgoingWebhooks": "Wychodzące Webhooki", @@ -3279,6 +3285,7 @@ "channel_info_rhs.menu.members": "Członkowie", "channel_info_rhs.menu.notification_preferences": "Ustawienia Powiadomień", "channel_info_rhs.menu.pinned": "Przypięte wiadomości", + "channel_info_rhs.menu.title": "Działania związane z informacjami o Kanałach", "channel_info_rhs.top_buttons.add_people": "Dodaj Osoby", "channel_info_rhs.top_buttons.add_people.tooltip": "Dodaj członków zespołu do tego kanału", "channel_info_rhs.top_buttons.copied": "Skopiowane", @@ -4540,6 +4547,7 @@ "onboardingTask.checklist.main_subtitle": "Zaczynamy i działamy.", "onboardingTask.checklist.no_thanks": "Nie, dzięki", "onboardingTask.checklist.start_enterprise_now": "Rozpocznij bezpłatny okres próbny Enterprise już teraz!", + "onboardingTask.checklist.start_onboarding_process": "Rozpocznij proces Onboardingu.", "onboardingTask.checklist.task_complete_your_profile": "Uzupełnij swój profil.", "onboardingTask.checklist.task_download_mm_apps": "Pobierz aplikację desktopową i mobilną.", "onboardingTask.checklist.task_invite_team_members": "Zaproś członków zespołu do obszaru roboczego.", @@ -4652,6 +4660,9 @@ "post.ariaLabel.replyMessage": "O {time} {date}, {authorName} odpowiedział, {message}", "post.reminder.acknowledgement": "Dla tej wiadomości od {username}: {permaLink} otrzymasz przypomnienie o {reminderTime}, {reminderDate}", "post.reminder.systemBot": "Cześć, oto Twoje przypomnienie o tej wiadomości od {username}: {permaLink}", + "post.renderError.message": "Wystąpił błąd podczas renderowania tego postu.", + "post.renderError.retry": "Ponów", + "post.renderError.retryLabel": "Spróbuj ponownie wyrenderować ten post", "post_body.check_for_out_of_channel_groups_mentions.message": "nie zostali powiadomieni poprzez tą wzmiankę, ponieważ nie są na kanale. Nie można ich dodać do kanału, ponieważ nie są członkami grup połączonych. Aby dodać je do tego kanału, muszą zostać dodane do połączonych grup.", "post_body.check_for_out_of_channel_mentions.link.and": " i ", "post_body.check_for_out_of_channel_mentions.link.private": "dodaj je do tego prywatnego kanału", @@ -4837,7 +4848,6 @@ "reaction_limit_reached_modal.body": "Ups! Wygląda na to, że osiągnęliśmy limit reakcji emoji dla tej wiadomości. Prosimy o kontakt z administratorem systemu w celu dostosowania tego limitu.", "reaction_limit_reached_modal.body.admin": "Ups! Wygląda na to, że osiągnęliśmy limit reakcji emoji dla tej wiadomości. Ustawiliśmy limit , aby zapewnić płynne działanie serwera. Jako administrator systemu możesz dostosować ten limit z konsoli systemowej .", "reaction_limit_reached_modal.title": "Osiągnąłeś limit reakcji", - "reaction_list.addReactionTooltip": "Dodaj reakcję", "remove_group_confirm_button": "Tak, Usuń Grupę oraz {memberCount, plural, one {Członka} other {Członków}}", "remove_group_confirm_message": "{memberCount, number} {memberCount, plural, one {użytkownik} other {użytkowników}} przypisanych do tej grupy zostanie usuniętych z zespołu. Czy na pewno chcesz usunąć tą grupę i {memberCount} {memberCount, plural, one {użytkownika} other {użytkowników}}?", "remove_group_confirm_title": "Usuń grupę i {memberCount, number} {memberCount, plural, one {Członka} other {Członków}}", diff --git a/webapp/channels/src/i18n/pt-BR.json b/webapp/channels/src/i18n/pt-BR.json index b2073fb25be4..a7ae19af93d5 100644 --- a/webapp/channels/src/i18n/pt-BR.json +++ b/webapp/channels/src/i18n/pt-BR.json @@ -3754,7 +3754,6 @@ "reaction.usersAndOthersReacted": "{users} e {otherUsers, number} {otherUsers, plural, one {outro usuário} other {outros usuários}}", "reaction.usersReacted": "{users} e {lastUser}", "reaction.you": "Você", - "reaction_list.addReactionTooltip": "Adicionar uma reação", "remove_group_confirm_button": "Sim, Remover Grupo e {memberCount, plural, one {Membro} other {Membros}}", "remove_group_confirm_message": "{memberCount, number} {memberCount, plural, one {membro} other {membros}} associados a este grupo serão removidos da equipe. Você tem certeza que deseja remover este grupo e {memberCount} {memberCount, plural, one {membro} other {membros}}?", "remove_group_confirm_title": "Remove Grupo e {memberCount, number} {memberCount, plural, one {Membro} other {Membros}}", diff --git a/webapp/channels/src/i18n/ro.json b/webapp/channels/src/i18n/ro.json index 6b3937810fb8..38da18fab01e 100644 --- a/webapp/channels/src/i18n/ro.json +++ b/webapp/channels/src/i18n/ro.json @@ -3104,7 +3104,6 @@ "reaction.usersAndOthersReacted": "{users} și {otherUsers, number} alți {otherUsers, plural, one {membru} other {membri}}", "reaction.usersReacted": "{users} și {lastUser}", "reaction.you": "Tu", - "reaction_list.addReactionTooltip": "Adăugați reacție", "remove_group_confirm_button": "Da, eliminați grupul și {memberCount, plural, one {Member} other {Members}}", "remove_group_confirm_message": "{memberCount, number} {memberCount, plural, one {membru} other {membrii}} asociați acestui grup vor fi eliminate din echipă la următoarea sincronizare programată AD/LDAP. Sigur doriți să eliminați acest grup și {memberCount} {memberCount, plural, one {membru} other {membrii}}?", "remove_group_confirm_title": "Eliminați grupul și {memberCount, number} {memberCount, plural, one {Member} other {Members}}", diff --git a/webapp/channels/src/i18n/ru.json b/webapp/channels/src/i18n/ru.json index 065fe94c6ca8..d8272d2bac5a 100644 --- a/webapp/channels/src/i18n/ru.json +++ b/webapp/channels/src/i18n/ru.json @@ -4656,7 +4656,6 @@ "reaction_limit_reached_modal.body": "Упс! Похоже, мы превысили лимит реакций эмодзи для этого сообщения. Пожалуйста, обратитесь к системному администратору, чтобы узнать о корректировке этого лимита.", "reaction_limit_reached_modal.body.admin": "Упс! Похоже, что мы достигли максимального количества реакций эмодзи для этого сообщения. Мы установили лимит, чтобы обеспечить бесперебойную работу вашего сервера. Как системный администратор, вы можете настроить этот лимит в системной консоли .", "reaction_limit_reached_modal.title": "Вы достигли предела реакции", - "reaction_list.addReactionTooltip": "Добавить реакцию", "remove_group_confirm_button": "Да, удалить группу и {memberCount, plural, one {участника} other {участников}}", "remove_group_confirm_message": "{memberCount, number} {memberCount, plural, one {участник} few {участника} other {участников}} связанных с этой группой будут удалены из команды.Вы уверены, что хотите удалить эту группу и {memberCount} {memberCount, plural, one {участника} other {участников}}?", "remove_group_confirm_title": "Удалить группу и {memberCount, number} {memberCount, plural, one {участника} other {участников}}", diff --git a/webapp/channels/src/i18n/sv.json b/webapp/channels/src/i18n/sv.json index 60a300e5de38..fe7eaf44aade 100644 --- a/webapp/channels/src/i18n/sv.json +++ b/webapp/channels/src/i18n/sv.json @@ -1887,7 +1887,7 @@ "admin.permissions.sysconsole_section_user_management_channels.name": "Kanaler", "admin.permissions.sysconsole_section_user_management_groups.name": "Grupper", "admin.permissions.sysconsole_section_user_management_permissions.name": "Behörigheter", - "admin.permissions.sysconsole_section_user_management_system_roles.name": "Systemroller", + "admin.permissions.sysconsole_section_user_management_system_roles.name": "Delegerad finmaskig administration", "admin.permissions.sysconsole_section_user_management_teams.name": "Team", "admin.permissions.sysconsole_section_user_management_users.description": "Kan inte återställa systemadministratörers lösenord", "admin.permissions.sysconsole_section_user_management_users.name": "Användare", @@ -4843,7 +4843,6 @@ "reaction_limit_reached_modal.body": "Hoppsan! Det verkar som om vi har nått ett tak för emoji-reaktioner för det här meddelandet. Kontakta din systemadministratör för eventuella justeringar av gränsen.", "reaction_limit_reached_modal.body.admin": "Hoppsan! Det verkar som om vi har nått ett tak för emoji-reaktioner för detta meddelande. Vi har satt en gräns för att din server ska hinna med allt. Som systemadministratör kan du i systemkonsolen justera denna gräns.", "reaction_limit_reached_modal.title": "Du har nått begränsningen på antal reaktioner", - "reaction_list.addReactionTooltip": "Lägg till reaktion", "remove_group_confirm_button": "Ja, ta bort grupp och {memberCount, plural, one {medlem} other {medlemmar}}", "remove_group_confirm_message": "{memberCount, number} {memberCount, plural, one {medlem} other {medlemmar}} som är kopplade till denna grupp kommer plocaks bort från teamet. Är du säker på att du vill ta bort guppen och {memberCount} {memberCount, plural, one {medlem} other {medlemmar}}?", "remove_group_confirm_title": "Ta bort grupp och {memberCount, number} {memberCount, plural, one {Member} other {Members}}", diff --git a/webapp/channels/src/i18n/tr.json b/webapp/channels/src/i18n/tr.json index 530d806662e6..27ddb02ea52e 100644 --- a/webapp/channels/src/i18n/tr.json +++ b/webapp/channels/src/i18n/tr.json @@ -2903,6 +2903,7 @@ "analytics.system.infoAndSkippedIntensiveQueries1": "Veriler yalnızca seçilmiş takım için kullanılsın. Bir takım ile bağlantısı olmayan doğrudan ileti kanallarındaki iletiler katılmasın.", "analytics.system.infoAndSkippedIntensiveQueries2": "En iyi başarımı elde etmek için bazı istatistikler kapatılmıştır. Bu istatistikleri config.json içinden açabilirsiniz.", "analytics.system.monthlyActiveUsers": "Aylık etkin kullanıcılar", + "analytics.system.perDayStatistics": "Ayrıntılı istatistikleri yükle", "analytics.system.postTypes": "İletiler, dosyalar ve hashtaglar", "analytics.system.privateGroups": "Özel kanallar", "analytics.system.publicChannels": "Herkese açık kanallar", @@ -2912,6 +2913,8 @@ "analytics.system.totalBotPosts": "Botların toplam ileti sayısı", "analytics.system.totalChannels": "Toplam kanal", "analytics.system.totalCommands": "Toplam komut", + "analytics.system.totalFiles": "Toplam dosya", + "analytics.system.totalFilesSize": "Toplam dosya boyutu", "analytics.system.totalIncomingWebhooks": "Gelen İnternet kancaları", "analytics.system.totalMasterDbConnections": "Ana veri tabanı bağlantıları", "analytics.system.totalOutgoingWebhooks": "Giden İnternet kancaları", @@ -4847,7 +4850,6 @@ "reaction_limit_reached_modal.body": "Kusura bakmayın! Görünüşe göre bu ileti için görsel ifade tepkisi sayısı sınırına ulaştık. Sınırı değiştirmek için lütfen sistem yöneticiniz ile görüşün.", "reaction_limit_reached_modal.body.admin": "Kusura bakmayın! Görünüşe göre bu ileti için görsel ifade tepki sayısı sınırına ulaştık. Sunucunuzda işlerin sorunsuz yürümesini sağlamak için bir sınır belirledik. Bir sistem yöneticisi olarak bu sınırı sistem panosundan ayarlayabilirsiniz.", "reaction_limit_reached_modal.title": "Tepki sınırına ulaştınız", - "reaction_list.addReactionTooltip": "Tepki ekle", "remove_group_confirm_button": "Evet, grubu ve {memberCount, plural, one {üyeyi} other {üyeyi}} sil", "remove_group_confirm_message": "{memberCount, number} {memberCount, plural, one {üye} other {üye}} sonraki zamanlanmış AD/LDAP eşitlemesinde silinecek. Bu grubu ve {memberCount} {memberCount, plural, one {üyeyi} other {üyeyi}} silmek istediğinize emin misiniz?", "remove_group_confirm_title": "Grubu ve {memberCount, number} {memberCount, plural, one {üyeyi} other {üyeyi}} sil", diff --git a/webapp/channels/src/i18n/uk.json b/webapp/channels/src/i18n/uk.json index e843cf00091f..504d771dfd77 100644 --- a/webapp/channels/src/i18n/uk.json +++ b/webapp/channels/src/i18n/uk.json @@ -4816,7 +4816,6 @@ "reaction_limit_reached_modal.body": "Упс! Схоже, ми досягли ліміту реакцій на емодзі для цього повідомлення. Будь ласка, зверніться до вашого системного адміністратора, щоб змінити цей ліміт.", "reaction_limit_reached_modal.body.admin": "Упс! Схоже, ми досягли ліміту реакцій на емодзі для цього повідомлення. Ми встановили ліміт, щоб забезпечити безперебійну роботу вашого сервера. Як системний адміністратор, ви можете налаштувати цей ліміт за допомогою системної консолі.", "reaction_limit_reached_modal.title": "Ви досягли ліміту реакцій", - "reaction_list.addReactionTooltip": "Додати реакцію", "remove_group_confirm_button": "Так, видалити групу та {memberCount, plural, one {учасника} other {учасників}}", "remove_group_confirm_message": "{memberCount, number} {memberCount, plural, one {учасник} few {учасники} other {учасників}}, пов’язаних із цією групою, буде видалено з команди. Ви впевнені, що хочете видалити цю групу та {memberCount} {memberCount, plural, one {учасника} few {учасників} other {учасників}}?", "remove_group_confirm_title": "Видалити групу та {memberCount, number} {memberCount, plural, one {учасника} few {учасників} other {учасників}}", diff --git a/webapp/channels/src/i18n/vi.json b/webapp/channels/src/i18n/vi.json index 14dfe54bbbfc..435de4ecc7b1 100644 --- a/webapp/channels/src/i18n/vi.json +++ b/webapp/channels/src/i18n/vi.json @@ -3922,7 +3922,6 @@ "reaction.usersAndOthersReacted": "{users} và {otherUsers, number} khác {otherUsers, số nhiều, một {user} khác {users}}", "reaction.usersReacted": "{users} và {lastUser}", "reaction.you": "Bạn", - "reaction_list.addReactionTooltip": "Thêm phản ứng", "remove_group_confirm_button": "Có, Xóa Nhóm và {memberCount, số nhiều, một {Thành viên} khác {Thành viên}}", "remove_group_confirm_message": "{memberCount, number} {memberCount, số nhiều, một {member} khác {member}} được liên kết với nhóm này sẽ bị xóa khỏi nhóm. Bạn có chắc chắn muốn xóa nhóm này và {memberCount} {memberCount không , số nhiều, một {thành viên} khác {thành viên}}?", "remove_group_confirm_title": "Xóa Nhóm và {memberCount, number} {memberCount, số nhiều, một {Member} khác {Member}}", diff --git a/webapp/channels/src/i18n/zh-CN.json b/webapp/channels/src/i18n/zh-CN.json index f43e82f2bc53..752bbbb8c636 100644 --- a/webapp/channels/src/i18n/zh-CN.json +++ b/webapp/channels/src/i18n/zh-CN.json @@ -483,7 +483,7 @@ "admin.channel_settings.channel_details.archiveChannel": "归档频道", "admin.channel_settings.channel_details.isDefaultDescr": "此默认频道无法转换为私有频道。", "admin.channel_settings.channel_details.isPublic": "公共频道或私有频道", - "admin.channel_settings.channel_details.isPublicDescr": "如果为`公共`,频道可以被发现且任何用户都可以加入。{br}或如果为`私有`,则需要被邀请才能加入。{br}切换以转换公共频道为私有。当组同步开启时,私有频道不能转换为公共频道。", + "admin.channel_settings.channel_details.isPublicDescr": "如果频道可以被任何用户发现并加入,选择公共。{br}如果需要频道邀请才能加入,则选择私有。{br}使用此开关以将此频道从公共切换为私有,或从私有切换为公共。", "admin.channel_settings.channel_details.syncGroupMembers": "同步群组成员", "admin.channel_settings.channel_details.syncGroupMembersDescr": "当启用时,从群组添加或移除用户将从此频道添加或移除。添加用户到群组将是唯一邀请成员到此频道的方法。了解更多", "admin.channel_settings.channel_details.unarchiveChannel": "取消归档频道", @@ -1495,6 +1495,7 @@ "admin.logs.showErrors": "显示过去{n}个错误", "admin.logs.title": "服务器日志", "admin.manage_roles.additionalRoles": "选择此账号的额外权限。阅读更多关于身份与权限。", + "admin.manage_roles.additionalRoles_warning": "注意:无论使用会话 cookie 还是个人访问令牌进行验证,上方授予的权限都应用于整个账号。例如,选择 post:all 将允许该账号向其未加入的频道发送消息,甚至无需使用个人访问令牌。", "admin.manage_roles.allowUserAccessTokens": "允许此账号生成个人访问令牌。", "admin.manage_roles.allowUserAccessTokensDesc": "删除权限不会删除现有令牌。请到用户的管理令牌菜单删除它们。", "admin.manage_roles.botAdditionalRoles": "为该账户选择附加权限。阅读有关角色和权限的更多信息。", @@ -1595,8 +1596,10 @@ "admin.password.preview": "错误消息预览", "admin.password.symbol": "至少有一个符号 (例如:”~!@#$%^&*()”)", "admin.password.uppercase": "至少有一个大写字母", + "admin.permissions.group.convert_private_channel_to_public.description": "转换私有频道为公共", + "admin.permissions.group.convert_private_channel_to_public.name": "转换为公共", "admin.permissions.group.convert_public_channel_to_private.description": "转换公共频道为私有", - "admin.permissions.group.convert_public_channel_to_private.name": "转换频道", + "admin.permissions.group.convert_public_channel_to_private.name": "转换为私有", "admin.permissions.group.custom_groups.description": "创建、编辑、删除和管理自定义组的成员。", "admin.permissions.group.custom_groups.name": "自定义组", "admin.permissions.group.delete_posts.description": "删除自己和其他人的消息。", @@ -1656,9 +1659,9 @@ "admin.permissions.permission.assign_system_admin_role.description": "指派系统管理员身份", "admin.permissions.permission.assign_system_admin_role.name": "指派系统管理员身份", "admin.permissions.permission.convert_private_channel_to_public.description": "转换私有频道为公共", - "admin.permissions.permission.convert_private_channel_to_public.name": "转换频道", + "admin.permissions.permission.convert_private_channel_to_public.name": "转换为公共", "admin.permissions.permission.convert_public_channel_to_private.description": "转换公共频道为私有", - "admin.permissions.permission.convert_public_channel_to_private.name": "转换频道", + "admin.permissions.permission.convert_public_channel_to_private.name": "转换为私有", "admin.permissions.permission.create_custom_group.description": "创建自定义组。", "admin.permissions.permission.create_custom_group.name": "创建", "admin.permissions.permission.create_direct_channel.description": "创建私信频道", @@ -1884,7 +1887,8 @@ "admin.permissions.sysconsole_section_user_management_channels.name": "频道", "admin.permissions.sysconsole_section_user_management_groups.name": "群组", "admin.permissions.sysconsole_section_user_management_permissions.name": "权限", - "admin.permissions.sysconsole_section_user_management_system_roles.name": "系统身份", + "admin.permissions.sysconsole_section_user_management_system_roles.description": "选择“无访问”只限制系统控制台界面。为保证基本产品功能,底层的 API 端点可以在只读状态被所有用户访问。", + "admin.permissions.sysconsole_section_user_management_system_roles.name": "委托精细管理", "admin.permissions.sysconsole_section_user_management_teams.name": "团队", "admin.permissions.sysconsole_section_user_management_users.description": "无法重设管理员密码", "admin.permissions.sysconsole_section_user_management_users.name": "用户", @@ -2574,9 +2578,11 @@ "admin.system_properties.user_properties.table.actions": "操作", "admin.system_properties.user_properties.table.actions.delete": "删除", "admin.system_properties.user_properties.table.property": "属性", + "admin.system_properties.user_properties.table.property_name.input.name": "属性名称", "admin.system_properties.user_properties.table.type": "类型", "admin.system_properties.user_properties.table.type.text": "文本", "admin.system_properties.user_properties.table.validation.name_required": "请输入属性名。", + "admin.system_properties.user_properties.table.validation.name_taken": "属性名称已被占用。", "admin.system_properties.user_properties.table.validation.name_unique": "属性名必须唯一。", "admin.system_properties.user_properties.title": "用户属性", "admin.system_roles_feature_discovery.copy": "分配可自定义的管理员角色以授予指定用户读取和/或写入系统控制台选定部分的权限。", @@ -2897,6 +2903,7 @@ "analytics.system.infoAndSkippedIntensiveQueries1": "仅使用所选团队的数据。排除与团队无关的直接消息频道中的消息。", "analytics.system.infoAndSkippedIntensiveQueries2": "为了提高性能,某些统计信息已禁用。您可以在在 config.json 中重新启用它们。", "analytics.system.monthlyActiveUsers": "每月活动用户", + "analytics.system.perDayStatistics": "加载高级统计数据", "analytics.system.postTypes": "发文,文件和标签", "analytics.system.privateGroups": "私有频道", "analytics.system.publicChannels": "公共频道", @@ -2906,6 +2913,8 @@ "analytics.system.totalBotPosts": "机器人总消息数", "analytics.system.totalChannels": "频道总数", "analytics.system.totalCommands": "命令总数", + "analytics.system.totalFiles": "总文件", + "analytics.system.totalFilesSize": "总文件大小", "analytics.system.totalIncomingWebhooks": "传入 Webhook", "analytics.system.totalMasterDbConnections": "父数据库连接数", "analytics.system.totalOutgoingWebhooks": "传出 Webhook", @@ -3225,7 +3234,8 @@ "channel_header.closeChannelInfo": "关闭详情", "channel_header.convert": "转换至私有频道", "channel_header.delete": "归档频道", - "channel_header.directchannel.you": "{displayname} (您) ", + "channel_header.directchannel": "{displayName}(您)频道菜单", + "channel_header.directchannel.you": "{displayname}(您) ", "channel_header.flagged": "保存的消息", "channel_header.groupMessageHasGuests": "此群组消息有访客", "channel_header.headerText.addNewButton": "添加频道标题", @@ -3237,6 +3247,7 @@ "channel_header.mute": "静音频道", "channel_header.muteConversation": "静音对话", "channel_header.openChannelInfo": "频道详情", + "channel_header.otherchannel": "{displayName}频道菜单", "channel_header.pinnedPosts": "标注消息", "channel_header.recentMentions": "最近提及", "channel_header.rename": "重命名频道", @@ -3274,6 +3285,7 @@ "channel_info_rhs.menu.members": "成员", "channel_info_rhs.menu.notification_preferences": "通知偏好", "channel_info_rhs.menu.pinned": "标注消息", + "channel_info_rhs.menu.title": "频道信息操作", "channel_info_rhs.top_buttons.add_people": "添加成员", "channel_info_rhs.top_buttons.add_people.tooltip": "添加团队成员到此频道", "channel_info_rhs.top_buttons.copied": "已复制", @@ -3698,6 +3710,8 @@ "email_verify.return": "返回登录", "email_verify.sending": "正在发送电子邮件…", "email_verify.sent": "验证邮件已发送", + "emoji_gif_picker.dialog.emojis": "表情符选择器", + "emoji_gif_picker.dialog.gifs": "GIF 选择器", "emoji_gif_picker.tabs.emojis": "表情符号", "emoji_gif_picker.tabs.gifs": "GIF 图像", "emoji_list.actions": "操作", @@ -4648,7 +4662,7 @@ "post.reminder.systemBot": "您好,这是您的关于这条来自 {username} 的消息:{permaLink} 的提醒", "post.renderError.message": "渲染此条发言时发生错误。", "post.renderError.retry": "重试", - "post.renderError.retryLabel": "重新渲染这条信息", + "post.renderError.retryLabel": "重新尝试渲染这条信息", "post_body.check_for_out_of_channel_groups_mentions.message": "未被此提及通知,因为他们不在此频道。他们无法被添加到此频道,因为他们不是关联的组的成员。他们必须被添加到关联的组才能被添加到此频道。", "post_body.check_for_out_of_channel_mentions.link.and": " 以及 ", "post_body.check_for_out_of_channel_mentions.link.private": "添加他们到此私有频道", @@ -4805,6 +4819,8 @@ "pricing_modal.title": "选择一个套餐", "pricing_modal.wantToTry": "想要试用吗? ", "pricing_modal.wantToUpgrade": "想要升级吗? ", + "profile_popover.aria_label.with_username": "{userName} 的个人资料弹出窗口", + "profile_popover.aria_label.without_username": "个人资料弹出窗口", "promote_to_user_modal.desc": "此操作将升级访客 {username} 至成员。这将允许用户加入公共频道已经与当前分配的频道里的成员以外的用户进行互动。您确定要升级 {username} 至成员?", "promote_to_user_modal.promote": "升级", "promote_to_user_modal.title": "提升访客 {username} 为成员", @@ -4834,7 +4850,6 @@ "reaction_limit_reached_modal.body": "哎呀!看来我们达到此消息的表情符反应上限了。请联系您的系统管理员以调整此限制。", "reaction_limit_reached_modal.body.admin": "哎呀!看来我们达到此消息的表情符反应上限了。我们对此设置了限制以保持您的服务器运行流畅。作为系统管理员,您可以在系统控制台中调整此限制。", "reaction_limit_reached_modal.title": "您已达到反应上限", - "reaction_list.addReactionTooltip": "添加反应", "remove_group_confirm_button": "是,移除组和{memberCount, plural, other {成员}}", "remove_group_confirm_message": "关联到此群组的 {memberCount, number} {memberCount, plural, other {位成员}}将被从此团队移除。您确定要移除此群组以及 {memberCount} {memberCount, plural, other {位成员}}吗?", "remove_group_confirm_title": "移除群组以及 {memberCount, number} {memberCount, plural, other {位成员}}", @@ -4958,6 +4973,7 @@ "search_hint.enter_to_search": "按回车键搜索", "search_hint.enter_to_select": "按回车键选择", "search_hint.filter": "过滤您的搜索:", + "search_hint.reset_filters": "您的过滤器已重置,因为您选择了另一个团队", "search_item.channelArchived": "已归档", "search_item.direct": "私信 (与 {username})", "search_item.file_tag.direct_message": "直接消息", @@ -4973,6 +4989,9 @@ "search_list_option.on": "指定日期的消息", "search_list_option.phrases": "含有语句的消息", "search_results.channel-files-header": "最近文件", + "search_teams_selector.all_teams": "所有团队", + "search_teams_selector.search_teams": "搜索团队", + "search_teams_selector.your_teams": "您的团队", "sectionNotice.dismiss": "隐藏通知", "select_team.icon": "选择团队图标", "select_team.join.icon": "加入团队图标", diff --git a/webapp/channels/src/i18n/zh-TW.json b/webapp/channels/src/i18n/zh-TW.json index 771dba8490bf..909a23345076 100644 --- a/webapp/channels/src/i18n/zh-TW.json +++ b/webapp/channels/src/i18n/zh-TW.json @@ -4777,7 +4777,6 @@ "reaction_limit_reached_modal.body": "哎呀!看來我們對此訊息的表情符號反應已達到上限。如需對此限制進行任何調整,請聯絡您的系統管理員。", "reaction_limit_reached_modal.body.admin": "哎呀!看來我們對此訊息的表情符號反應已達到上限。我們設定了限制,以保持伺服器順利運作。身為系統管理員,您可以從 系統控制台 調整此限制。", "reaction_limit_reached_modal.title": "您已達到反應上限", - "reaction_list.addReactionTooltip": "新增反應", "remove_group_confirm_button": "是的,移除群組跟 {memberCount, plural, one {位成員} other {位成員}}", "remove_group_confirm_message": "連結至此群組的 {memberCount, number} 位成員將一併被移除,確定移除此群組和 {memberCount} 位成員?", "remove_group_confirm_title": "移除群組跟 {memberCount, number} 位成員", From ff566e243c1c328b6b919f61557f1c2ee0e1b428 Mon Sep 17 00:00:00 2001 From: Amy Blais <29708087+amyblais@users.noreply.github.com> Date: Tue, 18 Feb 2025 09:20:09 +0200 Subject: [PATCH 054/164] Updated minimum supported Edge and Chrome versions (#30030) Automatic Merge --- server/i18n/en.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/i18n/en.json b/server/i18n/en.json index bbed00cc9853..7e48fa7714c1 100644 --- a/server/i18n/en.json +++ b/server/i18n/en.json @@ -10342,11 +10342,11 @@ }, { "id": "web.error.unsupported_browser.min_browser_version.chrome", - "translation": "Version 130+" + "translation": "Version 132+" }, { "id": "web.error.unsupported_browser.min_browser_version.edge", - "translation": "Version 130+" + "translation": "Version 132+" }, { "id": "web.error.unsupported_browser.min_browser_version.firefox", From 2b5275d87136f07e016c8eca09a2f004b31afc8a Mon Sep 17 00:00:00 2001 From: Christopher Speller Date: Tue, 18 Feb 2025 04:18:36 -0800 Subject: [PATCH 055/164] chore: Update Playbooks plugin to v2.1.1 (#29996) --- server/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/Makefile b/server/Makefile index 79929f677ec4..d99d781ecb42 100644 --- a/server/Makefile +++ b/server/Makefile @@ -141,7 +141,7 @@ PLUGIN_PACKAGES += mattermost-plugin-gitlab-v1.9.1 PLUGIN_PACKAGES += mattermost-plugin-jira-v4.2.0 # We need to prepackage both versions of playbooks and install the correct one based on the server license. See MM-60025. PLUGIN_PACKAGES += mattermost-plugin-playbooks-v1.40.0 -PLUGIN_PACKAGES += mattermost-plugin-playbooks-v2.0.1 +PLUGIN_PACKAGES += mattermost-plugin-playbooks-v2.1.1 PLUGIN_PACKAGES += mattermost-plugin-nps-v1.3.3 PLUGIN_PACKAGES += mattermost-plugin-servicenow-v2.3.4 PLUGIN_PACKAGES += mattermost-plugin-zoom-v1.8.0 From 89490a1093714cdb2631dfc32e6a52f366b08b0e Mon Sep 17 00:00:00 2001 From: Christopher Speller Date: Tue, 18 Feb 2025 07:16:11 -0800 Subject: [PATCH 056/164] Update Copilot plugin to v1.1.0 (#30098) Co-authored-by: Mattermost Build --- server/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/Makefile b/server/Makefile index d99d781ecb42..b018d76b84e4 100644 --- a/server/Makefile +++ b/server/Makefile @@ -145,7 +145,7 @@ PLUGIN_PACKAGES += mattermost-plugin-playbooks-v2.1.1 PLUGIN_PACKAGES += mattermost-plugin-nps-v1.3.3 PLUGIN_PACKAGES += mattermost-plugin-servicenow-v2.3.4 PLUGIN_PACKAGES += mattermost-plugin-zoom-v1.8.0 -PLUGIN_PACKAGES += mattermost-plugin-ai-v1.0.0 +PLUGIN_PACKAGES += mattermost-plugin-ai-v1.1.1 PLUGIN_PACKAGES += mattermost-plugin-boards-v9.1.1 PLUGIN_PACKAGES += mattermost-plugin-msteams-v2.1.0 PLUGIN_PACKAGES += mattermost-plugin-user-survey-v1.1.1 From ce61ed8f52c5c6ff388146607563ffac0d644379 Mon Sep 17 00:00:00 2001 From: Devin Binnie <52460000+devinbinnie@users.noreply.github.com> Date: Tue, 18 Feb 2025 12:29:26 -0500 Subject: [PATCH 057/164] [MM-55090] Threads created by users should not be auto-followed on reply by the creator when they've left the channel (#30181) Co-authored-by: Mattermost Build Co-authored-by: Caleb Roseland --- server/channels/app/notification.go | 4 ++- server/channels/app/notification_test.go | 42 ++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/server/channels/app/notification.go b/server/channels/app/notification.go index 5f1c8e06ee35..7475d3afeb8c 100644 --- a/server/channels/app/notification.go +++ b/server/channels/app/notification.go @@ -229,7 +229,9 @@ func (a *App) SendNotifications(c request.CTX, post *model.Post, team *model.Tea if parentPostList != nil { rootPost := parentPostList.Posts[parentPostList.Order[0]] if rootPost.GetProp("from_webhook") != "true" { - threadParticipants[rootPost.UserId] = true + if _, ok := profileMap[rootPost.UserId]; ok { + threadParticipants[rootPost.UserId] = true + } } if channel.Type != model.ChannelTypeDirect { rootMentions = getExplicitMentions(rootPost, keywords) diff --git a/server/channels/app/notification_test.go b/server/channels/app/notification_test.go index a22dd395bdef..53cdfc8e6d4f 100644 --- a/server/channels/app/notification_test.go +++ b/server/channels/app/notification_test.go @@ -2914,6 +2914,48 @@ func TestReplyPostNotificationsWithCRT(t *testing.T) { assert.Error(t, err) assert.Nil(t, membership) }) + + t.Run("should not auto follow when the original poster is no longer a channel member", func(t *testing.T) { + th := Setup(t).InitBasic() + defer th.TearDown() + + u1 := th.BasicUser + u2 := th.BasicUser2 + c1 := th.BasicChannel + th.AddUserToChannel(u2, c1) + + // Enable CRT + th.App.UpdateConfig(func(cfg *model.Config) { + *cfg.ServiceSettings.ThreadAutoFollow = true + *cfg.ServiceSettings.CollapsedThreads = model.CollapsedThreadsDefaultOn + }) + + rootPost := &model.Post{ + ChannelId: c1.Id, + Message: "root post by user1", + UserId: u1.Id, + } + rpost, appErr := th.App.CreatePost(th.Context, rootPost, c1, model.CreatePostFlags{SetOnline: true}) + require.Nil(t, appErr) + + // Remove user1 from the channel + appErr = th.App.RemoveUserFromChannel(th.Context, u1.Id, u1.Id, c1) + require.Nil(t, appErr) + + replyPost := &model.Post{ + ChannelId: c1.Id, + Message: "reply post by user2", + UserId: u2.Id, + RootId: rpost.Id, + } + _, appErr = th.App.CreatePost(th.Context, replyPost, c1, model.CreatePostFlags{SetOnline: true}) + require.Nil(t, appErr) + + // Ensure user1 is not auto-following the thread + threadMembership, appErr := th.App.GetThreadMembershipForUser(u1.Id, rpost.Id) + require.NotNil(t, appErr) + require.Nil(t, threadMembership) + }) } func TestChannelAutoFollowThreads(t *testing.T) { From b604930cf68546c4b69e1f50d263b7469bb97054 Mon Sep 17 00:00:00 2001 From: Devin Binnie <52460000+devinbinnie@users.noreply.github.com> Date: Tue, 18 Feb 2025 14:04:06 -0500 Subject: [PATCH 058/164] [MM-60555] Use channel memberships to calculate total unread status instead of team memberships (#30166) * [MM-60555] Use channel memberships to calculate total unread status instead of team memberships * Fix lint --------- Co-authored-by: Mattermost Build --- .../src/selectors/entities/channels.test.ts | 65 ++----------------- .../src/selectors/entities/channels.ts | 41 ++---------- 2 files changed, 9 insertions(+), 97 deletions(-) diff --git a/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/channels.test.ts b/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/channels.test.ts index 3d33497e68bc..1d9679e5ae96 100644 --- a/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/channels.test.ts +++ b/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/channels.test.ts @@ -2189,7 +2189,7 @@ describe('Selectors.Channels.getUnreadStatus', () => { }); it('get unreads', () => { - expect(Selectors.getUnreadStatus(testState)).toBe(69); + expect(Selectors.getUnreadStatus(testState)).toBe(5); }); it('get unreads with a missing profile entity', () => { @@ -2208,7 +2208,7 @@ describe('Selectors.Channels.getUnreadStatus', () => { }, }; - expect(Selectors.getUnreadStatus(newState)).toBe(69); + expect(Selectors.getUnreadStatus(newState)).toBe(5); }); it('get unreads with a deactivated user', () => { @@ -2230,7 +2230,7 @@ describe('Selectors.Channels.getUnreadStatus', () => { }, }, }; - expect(Selectors.getUnreadStatus(newState)).toBe(69); + expect(Selectors.getUnreadStatus(newState)).toBe(5); }); it('get unreads with a deactivated channel', () => { @@ -2253,17 +2253,15 @@ describe('Selectors.Channels.getUnreadStatus', () => { }, }; - expect(Selectors.getUnreadStatus(newState)).toBe(65); + expect(Selectors.getUnreadStatus(newState)).toBe(1); }); }); describe('Selectors.Channels.getUnreadStatus', () => { const team1 = {id: 'team1', delete_at: 0}; - const team2 = {id: 'team2', delete_at: 0}; const channelA = {id: 'channelA', name: 'channelA', team_id: 'team1', delete_at: 0}; const channelB = {id: 'channelB', name: 'channelB', team_id: 'team1', delete_at: 0}; - const channelC = {id: 'channelB', name: 'channelB', team_id: 'team2', delete_at: 0}; const dmChannel = {id: 'dmChannel', name: 'user1__user2', team_id: '', delete_at: 0, type: General.DM_CHANNEL}; const gmChannel = {id: 'gmChannel', name: 'gmChannel', team_id: 'team1', delete_at: 0, type: General.GM_CHANNEL}; @@ -2497,61 +2495,6 @@ describe('Selectors.Channels.getUnreadStatus', () => { expect(unreadMeta.isUnread).toBe(true); // gmChannel is unread expect(unreadMeta.unreadMentionCount).toBe(gmMember.mention_count); }); - - test('should count mentions and messages for other teams from team members', () => { - const myMemberA = {mention_count: 2, msg_count: 3, notify_props: {mark_unread: 'all'}}; - const myMemberC = {mention_count: 5, msg_count: 7, notify_props: {mark_unread: 'all'}}; - - const teamMember1 = {msg_count: 1, mention_count: 2}; - const teamMember2 = {msg_count: 3, mention_count: 6}; - - const state = { - entities: { - general: {config: {}}, - preferences: { - myPreferences: {}, - }, - threads: { - counts: {}, - }, - channels: { - channels: { - channelA, - channelC, - }, - messageCounts: { - channelA: {total: 11}, - channelC: {total: 17}, - }, - myMembers: { - channelA: myMemberA, - channelC: myMemberC, - }, - }, - teams: { - currentTeamId: 'team1', - myMembers: { - team1: teamMember1, - team2: teamMember2, - }, - teams: { - team1, - team2, - }, - }, - users: { - currentUserId: 'user1', - profiles: {}, - }, - }, - } as unknown as GlobalState; - - const unreadStatus = Selectors.getUnreadStatus(state); - const unreadMeta = Selectors.basicUnreadMeta(unreadStatus); - - expect(unreadMeta.isUnread).toBe(true); // channelA and channelC are unread - expect(unreadMeta.unreadMentionCount).toBe(myMemberA.mention_count + teamMember2.mention_count); - }); }); describe('Selectors.Channels.getUnreadStatus', () => { diff --git a/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/channels.ts b/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/channels.ts index 0cbd74f29749..b87a97094e11 100644 --- a/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/channels.ts +++ b/webapp/channels/src/packages/mattermost-redux/src/selectors/entities/channels.ts @@ -42,7 +42,7 @@ import { haveICurrentChannelPermission, haveITeamPermission, } from 'mattermost-redux/selectors/entities/roles'; -import {getCurrentTeamId, getMyTeams, getTeamMemberships} from 'mattermost-redux/selectors/entities/teams'; +import {getCurrentTeamId, getMyTeams} from 'mattermost-redux/selectors/entities/teams'; import { getCurrentUserId, getStatusForUserId, @@ -612,8 +612,6 @@ export const getUnreadStatus: (state: GlobalState) => BasicUnreadStatus = create getUsers, getCurrentUserId, getCurrentTeamId, - getMyTeams, - getTeamMemberships, isCollapsedThreadsEnabled, getThreadCounts, getThreadCountsIncludingDirect, @@ -624,15 +622,13 @@ export const getUnreadStatus: (state: GlobalState) => BasicUnreadStatus = create users, currentUserId, currentTeamId, - myTeams, - myTeamMemberships, collapsedThreads, threadCounts, threadCountsIncludingDirect, ) => { const { - messages: currentTeamUnreadMessages, - mentions: currentTeamUnreadMentions, + messages: unreadMessages, + mentions: unreadMentions, } = Object.entries(myMembers).reduce((counts, [channelId, membership]) => { const channel = channels[channelId]; @@ -640,15 +636,6 @@ export const getUnreadStatus: (state: GlobalState) => BasicUnreadStatus = create return counts; } - if ( - // other-team non-DM/non-GM channels - channel.team_id !== currentTeamId && - channel.type !== General.DM_CHANNEL && - channel.type !== General.GM_CHANNEL - ) { - return counts; - } - const channelExists = channel.type === General.DM_CHANNEL ? users[getUserIdFromChannelName(currentUserId, channel.name)]?.delete_at === 0 : channel.delete_at === 0; if (!channelExists) { return counts; @@ -670,26 +657,8 @@ export const getUnreadStatus: (state: GlobalState) => BasicUnreadStatus = create mentions: 0, }); - // Includes mention count and message count from teams other than the current team - // This count does not include GM's and DM's - const { - messages: otherTeamsUnreadMessages, - mentions: otherTeamsUnreadMentions, - } = myTeams.reduce((acc, team) => { - if (currentTeamId !== team.id) { - const member = myTeamMemberships[team.id]; - acc.messages += collapsedThreads ? member.msg_count_root : member.msg_count; - acc.mentions += collapsedThreads ? member.mention_count_root : member.mention_count; - } - - return acc; - }, { - messages: 0, - mentions: 0, - }); - - const totalUnreadMessages = currentTeamUnreadMessages + otherTeamsUnreadMessages; - let totalUnreadMentions = currentTeamUnreadMentions + otherTeamsUnreadMentions; + const totalUnreadMessages = unreadMessages; + let totalUnreadMentions = unreadMentions; let anyUnreadThreads = false; // when collapsed threads are enabled, we start with root-post counts from channels, then From bd376d3473eb113aee0cbc1a7bdffd81ad1c7859 Mon Sep 17 00:00:00 2001 From: Devin Binnie <52460000+devinbinnie@users.noreply.github.com> Date: Tue, 18 Feb 2025 14:04:33 -0500 Subject: [PATCH 059/164] [MM-62945] Load new channels for users added even if they're on a different team (#30167) Co-authored-by: Mattermost Build --- webapp/channels/src/actions/websocket_actions.jsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/webapp/channels/src/actions/websocket_actions.jsx b/webapp/channels/src/actions/websocket_actions.jsx index ed6e1fa0ec5a..9b09b1b9596d 100644 --- a/webapp/channels/src/actions/websocket_actions.jsx +++ b/webapp/channels/src/actions/websocket_actions.jsx @@ -1042,9 +1042,8 @@ function handleUserAddedEvent(msg) { } // Load the channel so that it appears in the sidebar - const currentTeamId = getCurrentTeamId(doGetState()); const currentUserId = getCurrentUserId(doGetState()); - if (currentTeamId === msg.data.team_id && currentUserId === msg.data.user_id) { + if (currentUserId === msg.data.user_id) { doDispatch(fetchChannelAndAddToSidebar(msg.broadcast.channel_id)); } From b0b379e167f92dcd219047d343ef7334e3a10637 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20V=C3=A9lez?= Date: Wed, 19 Feb 2025 00:26:41 -0500 Subject: [PATCH 060/164] MM-61634 - adjust rhs a11y structure (#30168) Co-authored-by: Mattermost Build --- webapp/channels/src/components/channel_info_rhs/header.tsx | 4 +++- .../channels/src/components/channel_members_rhs/header.tsx | 4 +++- .../__snapshots__/post_edit_history.test.tsx.snap | 2 ++ .../components/resizable_sidebar/resizable_rhs/index.tsx | 7 ++++++- .../src/components/rhs_card_header/rhs_card_header.tsx | 5 ++++- .../src/components/rhs_header_post/rhs_header_post.tsx | 5 ++++- .../search_results_header/search_results_header.tsx | 5 ++++- .../src/components/sidebar_right/sidebar_right.tsx | 4 ++++ 8 files changed, 30 insertions(+), 6 deletions(-) diff --git a/webapp/channels/src/components/channel_info_rhs/header.tsx b/webapp/channels/src/components/channel_info_rhs/header.tsx index 0e7cab25a852..8533a16ae040 100644 --- a/webapp/channels/src/components/channel_info_rhs/header.tsx +++ b/webapp/channels/src/components/channel_info_rhs/header.tsx @@ -41,7 +41,9 @@ const Header = ({channel, isArchived, isMobile, onClose}: Props) => { /> )} - + { )} - + Edit History
Edit History
{ children: React.ReactNode; rightWidthHolderRef: React.RefObject; + ariaLabel?: string; + ariaLabeledby?: string; } function ResizableRhs({ @@ -22,6 +24,8 @@ function ResizableRhs({ id, className, rightWidthHolderRef, + ariaLabel, + ariaLabeledby, }: Props) { const containerRef = useRef(null); @@ -91,7 +95,8 @@ function ResizableRhs({ className={className} role={role} ref={containerRef} - aria-labelledby='rhsPanelTitle' + aria-label={ariaLabel} + aria-labelledby={ariaLabeledby || 'rhsPanelTitle'} > {children} { return (
- + {back} { return (
- + {back} - + {props.canGoBack && (