diff --git a/.browserslistrc b/.browserslistrc index 6367e4d358..0135379d6e 100644 --- a/.browserslistrc +++ b/.browserslistrc @@ -1,10 +1,6 @@ -[production] defaults > 0.2% firefox >= 78 ios >= 15.6 not dead not OperaMini all - -[development] -supports es6-module diff --git a/.github/ISSUE_TEMPLATE/2.server_bug_report.yml b/.github/ISSUE_TEMPLATE/2.server_bug_report.yml index 0552e985f7..45bd689f1e 100644 --- a/.github/ISSUE_TEMPLATE/2.server_bug_report.yml +++ b/.github/ISSUE_TEMPLATE/2.server_bug_report.yml @@ -59,7 +59,7 @@ body: Any additional technical details you may have, like logs or error traces value: | If this is happening on your own Mastodon server, please fill out those: - - Ruby version: (from `ruby --version`, eg. v3.3.5) + - Ruby version: (from `ruby --version`, eg. v3.4.1) - Node.js version: (from `node --version`, eg. v20.18.0) validations: required: false diff --git a/.github/ISSUE_TEMPLATE/3.troubleshooting.yml b/.github/ISSUE_TEMPLATE/3.troubleshooting.yml index 7c6914bd7b..1f70046ed4 100644 --- a/.github/ISSUE_TEMPLATE/3.troubleshooting.yml +++ b/.github/ISSUE_TEMPLATE/3.troubleshooting.yml @@ -60,7 +60,7 @@ body: value: | Please at least include those informations: - Operating system: (eg. Ubuntu 22.04) - - Ruby version: (from `ruby --version`, eg. v3.3.5) + - Ruby version: (from `ruby --version`, eg. v3.4.1) - Node.js version: (from `node --version`, eg. v20.18.0) validations: required: false diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 3d3499922e..8a10676283 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -14,11 +14,6 @@ // If we do not want a package to be grouped with others, we need to set its groupName // to `null` after any other rule set it to something. dependencyDashboardHeader: 'This issue lists Renovate updates and detected dependencies. Read the [Dependency Dashboard](https://docs.renovatebot.com/key-concepts/dashboard/) docs to learn more. Before approving any upgrade: read the description and comments in the [`renovate.json5` file](https://github.com/mastodon/mastodon/blob/main/.github/renovate.json5).', - constraints: { - // Mastodon should work on Ruby 3.4, but its test dependencies are currently uninstallable on Ruby 3.4. - // TODO: remove this once https://github.com/briandunn/flatware/issues/103 is fixed - ruby: '3.3', - }, postUpdateOptions: ['yarnDedupeHighest'], packageRules: [ { diff --git a/.github/workflows/check-i18n.yml b/.github/workflows/check-i18n.yml index 4f87f0fe5f..c46090c1b5 100644 --- a/.github/workflows/check-i18n.yml +++ b/.github/workflows/check-i18n.yml @@ -18,7 +18,7 @@ permissions: jobs: check-i18n: - runs-on: ubuntu-24.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/crowdin-download-stable.yml b/.github/workflows/crowdin-download-stable.yml index 3d888f179e..191eb5bc2c 100644 --- a/.github/workflows/crowdin-download-stable.yml +++ b/.github/workflows/crowdin-download-stable.yml @@ -51,7 +51,7 @@ jobs: # Create or update the pull request - name: Create Pull Request - uses: peter-evans/create-pull-request@v7.0.5 + uses: peter-evans/create-pull-request@v7.0.6 with: commit-message: 'New Crowdin translations' title: 'New Crowdin Translations for ${{ github.base_ref || github.ref_name }} (automated)' diff --git a/.github/workflows/crowdin-download.yml b/.github/workflows/crowdin-download.yml index 781fd5080f..1fdd1e08b4 100644 --- a/.github/workflows/crowdin-download.yml +++ b/.github/workflows/crowdin-download.yml @@ -53,7 +53,7 @@ jobs: # Create or update the pull request - name: Create Pull Request - uses: peter-evans/create-pull-request@v7.0.5 + uses: peter-evans/create-pull-request@v7 with: commit-message: 'New Crowdin translations' title: 'New Crowdin Translations (automated)' diff --git a/.github/workflows/lint-haml.yml b/.github/workflows/lint-haml.yml index 499be2010a..9361358078 100644 --- a/.github/workflows/lint-haml.yml +++ b/.github/workflows/lint-haml.yml @@ -43,4 +43,4 @@ jobs: - name: Run haml-lint run: | echo "::add-matcher::.github/workflows/haml-lint-problem-matcher.json" - bin/haml-lint --reporter github + bin/haml-lint --parallel --reporter github diff --git a/.github/workflows/test-ruby.yml b/.github/workflows/test-ruby.yml index 08b50e2680..4deb08d328 100644 --- a/.github/workflows/test-ruby.yml +++ b/.github/workflows/test-ruby.yml @@ -125,6 +125,7 @@ jobs: matrix: ruby-version: - '3.2' + - '3.3' - '.ruby-version' steps: - uses: actions/checkout@v4 @@ -174,7 +175,7 @@ jobs: test-libvips: name: Libvips tests - runs-on: ubuntu-24.04 + runs-on: ubuntu-latest needs: - build @@ -226,6 +227,7 @@ jobs: matrix: ruby-version: - '3.2' + - '3.3' - '.ruby-version' steps: - uses: actions/checkout@v4 @@ -304,6 +306,7 @@ jobs: matrix: ruby-version: - '3.2' + - '3.3' - '.ruby-version' steps: @@ -420,6 +423,7 @@ jobs: matrix: ruby-version: - '3.2' + - '3.3' - '.ruby-version' search-image: - docker.elastic.co/elasticsearch/elasticsearch:7.17.13 diff --git a/.nvmrc b/.nvmrc index 35d2d08ea1..fb0a135541 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -22.12 +22.13 diff --git a/.rubocop/style.yml b/.rubocop/style.yml index 7dd4299c3e..f59340d452 100644 --- a/.rubocop/style.yml +++ b/.rubocop/style.yml @@ -29,9 +29,6 @@ Style/IfUnlessModifier: Style/KeywordArgumentsMerging: Enabled: false -Style/MultipleComparison: - Enabled: false - Style/NumericLiterals: AllowedPatterns: - \d{4}_\d{2}_\d{2}_\d{6} @@ -61,3 +58,6 @@ Style/TrailingCommaInArrayLiteral: Style/TrailingCommaInHashLiteral: EnforcedStyleForMultiline: comma + +Style/WordArray: + MinSize: 3 # Override default of 2 diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 8890879b74..d6cb4792e1 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,6 +1,6 @@ # This configuration was generated by # `rubocop --auto-gen-config --auto-gen-only-exclude --no-offense-counts --no-auto-gen-timestamp` -# using RuboCop version 1.69.1. +# using RuboCop version 1.70.0. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new @@ -8,7 +8,7 @@ Lint/NonLocalExitFromIterator: Exclude: - - 'app/helpers/jsonld_helper.rb' + - 'app/helpers/json_ld_helper.rb' # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes. Metrics/AbcSize: @@ -69,20 +69,11 @@ Style/MapToHash: Exclude: - 'app/models/status.rb' -# This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: literals, strict -Style/MutableConstant: - Exclude: - - 'app/models/tag.rb' - - 'app/services/delete_account_service.rb' - - 'lib/mastodon/migration_warning.rb' - # Configuration parameters: AllowedMethods. # AllowedMethods: respond_to_missing? Style/OptionalBooleanParameter: Exclude: - - 'app/helpers/jsonld_helper.rb' + - 'app/helpers/json_ld_helper.rb' - 'app/lib/admin/system_check/message.rb' - 'app/lib/request.rb' - 'app/lib/webfinger.rb' @@ -103,10 +94,3 @@ Style/RedundantConstantBase: Exclude: - 'config/environments/production.rb' - 'config/initializers/sidekiq.rb' - -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: WordRegex. -# SupportedStyles: percent, brackets -Style/WordArray: - EnforcedStyle: percent - MinSize: 3 diff --git a/.ruby-version b/.ruby-version index 9c25013dbb..47b322c971 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -3.3.6 +3.4.1 diff --git a/CHANGELOG.md b/CHANGELOG.md index ea18b3cb92..ef6a87ebb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,24 @@ All notable changes to this project will be documented in this file. +## [4.3.3] - 2025-01-16 + +### Security + +- Fix insufficient validation of account URIs ([GHSA-5wxh-3p65-r4g6](https://github.com/mastodon/mastodon/security/advisories/GHSA-5wxh-3p65-r4g6)) +- Update dependencies + +### Fixed + +- Fix `libyaml` missing from `Dockerfile` build stage (#33591 by @vmstan) +- Fix incorrect notification settings migration for non-followers (#33348 by @ClearlyClaire) +- Fix down clause for notification policy v2 migrations (#33340 by @jesseplusplus) +- Fix error decrementing status count when `FeaturedTags#last_status_at` is `nil` (#33320 by @ClearlyClaire) +- Fix last paginated notification group only including data on a single notification (#33271 by @ClearlyClaire) +- Fix processing of mentions for post edits with an existing corresponding silent mention (#33227 by @ClearlyClaire) +- Fix deletion of unconfirmed users with Webauthn set (#33186 by @ClearlyClaire) +- Fix empty authors preview card serialization (#33151, #33466 by @mjankowski and @ClearlyClaire) + ## [4.3.2] - 2024-12-03 ### Added diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 66aa01ffe4..2b49bbeb98 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -51,7 +51,7 @@ You can contribute in the following ways: If your contributions are accepted into Mastodon, you can request to be paid through [our OpenCollective](https://opencollective.com/mastodon). Please review the org-level [contribution guidelines] for high-level acceptance -criteria guidance. +criteria guidance and the [DEVELOPMENT] guide for environment-specific details. [contribution guidelines]: https://github.com/mastodon/.github/blob/main/CONTRIBUTING.md @@ -94,3 +94,5 @@ It is not always possible to phrase every change in such a manner, but it is des The [Mastodon documentation](https://docs.joinmastodon.org) is a statically generated site. You can [submit merge requests to mastodon/documentation](https://github.com/mastodon/documentation). + +[DEVELOPMENT]: docs/DEVELOPMENT.md diff --git a/Dockerfile b/Dockerfile index d80a4e1555..deeac8b466 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,9 +10,9 @@ ARG TARGETPLATFORM=${TARGETPLATFORM} ARG BUILDPLATFORM=${BUILDPLATFORM} -# Ruby image to use for base image, change with [--build-arg RUBY_VERSION="3.3.x"] +# Ruby image to use for base image, change with [--build-arg RUBY_VERSION="3.4.x"] # renovate: datasource=docker depName=docker.io/ruby -ARG RUBY_VERSION="3.3.6" +ARG RUBY_VERSION="3.4.1" # # Node version to use in base image, change with [--build-arg NODE_MAJOR_VERSION="20"] # renovate: datasource=node-version depName=node ARG NODE_MAJOR_VERSION="22" @@ -20,7 +20,7 @@ ARG NODE_MAJOR_VERSION="22" ARG DEBIAN_VERSION="bookworm" # Node image to use for base image based on combined variables (ex: 20-bookworm-slim) FROM docker.io/node:${NODE_MAJOR_VERSION}-${DEBIAN_VERSION}-slim AS node -# Ruby image to use for base image based on combined variables (ex: 3.3.x-slim-bookworm) +# Ruby image to use for base image based on combined variables (ex: 3.4.x-slim-bookworm) FROM docker.io/ruby:${RUBY_VERSION}-slim-${DEBIAN_VERSION} AS ruby # Resulting version string is vX.X.X-MASTODON_VERSION_PRERELEASE+MASTODON_VERSION_METADATA @@ -153,6 +153,7 @@ RUN \ libpq-dev \ libssl-dev \ libtool \ + libyaml-dev \ meson \ nasm \ pkg-config \ diff --git a/Gemfile b/Gemfile index ad174cfdc4..0efcc6562c 100644 --- a/Gemfile +++ b/Gemfile @@ -1,7 +1,7 @@ # frozen_string_literal: true source 'https://rubygems.org' -ruby '>= 3.2.0', '< 3.5' +ruby '>= 3.2.0', '< 3.5.0' gem 'propshaft' gem 'puma', '~> 6.3' @@ -94,7 +94,7 @@ gem 'twitter-text', '~> 3.1.0' gem 'tzinfo-data', '~> 1.2023' gem 'webauthn', '~> 3.0' gem 'webpacker', '~> 5.4' -gem 'webpush', github: 'ClearlyClaire/webpush', ref: 'f14a4d52e201128b1b00245d11b6de80d6cfdcd9' +gem 'webpush', github: 'mastodon/webpush', ref: '9631ac63045cfabddacc69fc06e919b4c13eb913' gem 'json-ld' gem 'json-ld-preloaded', '~> 3.2' @@ -108,7 +108,7 @@ group :opentelemetry do gem 'opentelemetry-instrumentation-active_model_serializers', '~> 0.21.0', require: false gem 'opentelemetry-instrumentation-concurrent_ruby', '~> 0.21.2', require: false gem 'opentelemetry-instrumentation-excon', '~> 0.22.0', require: false - gem 'opentelemetry-instrumentation-faraday', '~> 0.24.1', require: false + gem 'opentelemetry-instrumentation-faraday', '~> 0.25.0', require: false gem 'opentelemetry-instrumentation-http', '~> 0.23.2', require: false gem 'opentelemetry-instrumentation-http_client', '~> 0.22.3', require: false gem 'opentelemetry-instrumentation-net_http', '~> 0.22.4', require: false @@ -125,7 +125,7 @@ group :test do gem 'flatware-rspec' # Adds RSpec Error/Warning annotations to GitHub PRs on the Files tab - gem 'rspec-github', '~> 2.4', require: false + gem 'rspec-github', '~> 3.0', require: false # RSpec helpers for email specs gem 'email_spec' diff --git a/Gemfile.lock b/Gemfile.lock index f49de6bf3b..a4c5f8ad98 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,9 +1,9 @@ GIT - remote: https://github.com/ClearlyClaire/webpush.git - revision: f14a4d52e201128b1b00245d11b6de80d6cfdcd9 - ref: f14a4d52e201128b1b00245d11b6de80d6cfdcd9 + remote: https://github.com/mastodon/webpush.git + revision: 9631ac63045cfabddacc69fc06e919b4c13eb913 + ref: 9631ac63045cfabddacc69fc06e919b4c13eb913 specs: - webpush (0.3.8) + webpush (1.1.0) hkdf (~> 0.2) jwt (~> 2.0) @@ -94,7 +94,7 @@ GEM ast (2.4.2) attr_required (1.0.2) aws-eventstream (1.3.0) - aws-partitions (1.1029.0) + aws-partitions (1.1032.0) aws-sdk-core (3.214.1) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.992.0) @@ -103,7 +103,7 @@ GEM aws-sdk-kms (1.96.0) aws-sdk-core (~> 3, >= 3.210.0) aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.176.1) + aws-sdk-s3 (1.177.0) aws-sdk-core (~> 3, >= 3.210.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) @@ -160,7 +160,7 @@ GEM cocoon (1.2.15) color_diff (0.1) concurrent-ruby (1.3.4) - connection_pool (2.4.1) + connection_pool (2.5.0) cose (1.3.1) cbor (~> 0.5.9) openssl-signature_algorithm (~> 1.0) @@ -233,16 +233,16 @@ GEM faraday-net_http (3.4.0) net-http (>= 0.5.0) fast_blank (1.0.1) - fastimage (2.3.1) + fastimage (2.4.0) ffi (1.17.1) ffi-compiler (1.3.2) ffi (>= 1.15.5) rake - flatware (2.3.3) + flatware (2.3.4) drb thor (< 2.0) - flatware-rspec (2.3.3) - flatware (= 2.3.3) + flatware-rspec (2.3.4) + flatware (= 2.3.4) rspec (>= 3.6) fog-core (2.5.0) builder @@ -490,7 +490,7 @@ GEM opentelemetry-instrumentation-active_job (0.7.8) opentelemetry-api (~> 1.0) opentelemetry-instrumentation-base (~> 0.22.1) - opentelemetry-instrumentation-active_model_serializers (0.21.0) + opentelemetry-instrumentation-active_model_serializers (0.21.1) opentelemetry-api (~> 1.0) opentelemetry-instrumentation-active_support (>= 0.7.0) opentelemetry-instrumentation-base (~> 0.22.1) @@ -510,7 +510,7 @@ GEM opentelemetry-instrumentation-excon (0.22.5) opentelemetry-api (~> 1.0) opentelemetry-instrumentation-base (~> 0.22.1) - opentelemetry-instrumentation-faraday (0.24.8) + opentelemetry-instrumentation-faraday (0.25.0) opentelemetry-api (~> 1.0) opentelemetry-instrumentation-base (~> 0.22.1) opentelemetry-instrumentation-http (0.23.5) @@ -522,14 +522,14 @@ GEM opentelemetry-instrumentation-net_http (0.22.8) opentelemetry-api (~> 1.0) opentelemetry-instrumentation-base (~> 0.22.1) - opentelemetry-instrumentation-pg (0.29.1) + opentelemetry-instrumentation-pg (0.29.2) opentelemetry-api (~> 1.0) opentelemetry-helpers-sql-obfuscation opentelemetry-instrumentation-base (~> 0.22.1) opentelemetry-instrumentation-rack (0.25.0) opentelemetry-api (~> 1.0) opentelemetry-instrumentation-base (~> 0.22.1) - opentelemetry-instrumentation-rails (0.34.0) + opentelemetry-instrumentation-rails (0.34.1) opentelemetry-api (~> 1.0) opentelemetry-instrumentation-action_mailer (~> 0.3.0) opentelemetry-instrumentation-action_pack (~> 0.10.0) @@ -538,6 +538,7 @@ GEM opentelemetry-instrumentation-active_record (~> 0.8.0) opentelemetry-instrumentation-active_support (~> 0.7.0) opentelemetry-instrumentation-base (~> 0.22.1) + opentelemetry-instrumentation-concurrent_ruby (~> 0.21.4) opentelemetry-instrumentation-redis (0.25.7) opentelemetry-api (~> 1.0) opentelemetry-instrumentation-base (~> 0.22.1) @@ -555,7 +556,7 @@ GEM opentelemetry-api (~> 1.0) orm_adapter (0.5.0) ostruct (0.6.1) - ox (2.14.19) + ox (2.14.20) bigdecimal (>= 3.0) parallel (1.26.3) parser (3.3.6.0) @@ -690,7 +691,7 @@ GEM rspec-expectations (3.13.3) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) - rspec-github (2.4.0) + rspec-github (3.0.0) rspec-core (~> 3.0) rspec-mocks (3.13.2) diff-lcs (>= 1.2.0, < 2.0) @@ -709,7 +710,7 @@ GEM rspec-mocks (~> 3.0) sidekiq (>= 5, < 8) rspec-support (3.13.2) - rubocop (1.69.2) + rubocop (1.70.0) json (~> 2.3) language_server-protocol (>= 3.17.0) parallel (~> 1.10) @@ -723,7 +724,7 @@ GEM parser (>= 3.3.1.0) rubocop-capybara (2.21.0) rubocop (~> 1.41) - rubocop-performance (1.23.0) + rubocop-performance (1.23.1) rubocop (>= 1.48.1, < 2.0) rubocop-ast (>= 1.31.1, < 2.0) rubocop-rails (2.28.0) @@ -744,7 +745,7 @@ GEM ruby-vips (2.2.2) ffi (~> 1.12) logger - rubyzip (2.3.2) + rubyzip (2.4.1) rufus-scheduler (3.9.2) fugit (~> 1.1, >= 1.11.1) safety_net_attestation (0.4.0) @@ -793,7 +794,7 @@ GEM simplecov-html (0.13.1) simplecov-lcov (0.8.0) simplecov_json_formatter (0.1.4) - stackprof (0.2.26) + stackprof (0.2.27) stoplight (4.1.0) redlock (~> 1.0) stringio (3.1.2) @@ -809,7 +810,7 @@ GEM unicode-display_width (>= 1.1.1, < 3) terrapin (1.0.1) climate_control - test-prof (1.4.3) + test-prof (1.4.4) thor (1.3.2) tilt (2.5.0) timeout (0.4.3) @@ -963,7 +964,7 @@ DEPENDENCIES opentelemetry-instrumentation-active_model_serializers (~> 0.21.0) opentelemetry-instrumentation-concurrent_ruby (~> 0.21.2) opentelemetry-instrumentation-excon (~> 0.22.0) - opentelemetry-instrumentation-faraday (~> 0.24.1) + opentelemetry-instrumentation-faraday (~> 0.25.0) opentelemetry-instrumentation-http (~> 0.23.2) opentelemetry-instrumentation-http_client (~> 0.22.3) opentelemetry-instrumentation-net_http (~> 0.22.4) @@ -994,7 +995,7 @@ DEPENDENCIES redis (~> 4.5) redis-namespace (~> 1.10) rqrcode (~> 2.2) - rspec-github (~> 2.4) + rspec-github (~> 3.0) rspec-rails (~> 7.0) rspec-sidekiq (~> 5.0) rubocop @@ -1034,7 +1035,7 @@ DEPENDENCIES xorcist (~> 1.1) RUBY VERSION - ruby 3.3.6p108 + ruby 3.4.1p0 BUNDLED WITH 2.6.2 diff --git a/README.md b/README.md index b0ef1b0d52..f1c6cd4322 100644 --- a/README.md +++ b/README.md @@ -14,27 +14,27 @@ Mastodon Glitch Edition is a fork of [Mastodon](https://github.com/mastodon/mast --- -
+> [!NOTE] +> Want to learn more about Mastodon? +> Click below to find out more in a video. -[![GitHub release](https://img.shields.io/github/release/mastodon/mastodon.svg)][releases] -[![Ruby Testing](https://github.com/mastodon/mastodon/actions/workflows/test-ruby.yml/badge.svg)](https://github.com/mastodon/mastodon/actions/workflows/test-ruby.yml) -[![Crowdin](https://d322cqt584bo4o.cloudfront.net/mastodon/localized.svg)][crowdin] ++ + + +
-[releases]: https://github.com/mastodon/mastodon/releases -[crowdin]: https://crowdin.com/project/mastodon + Mastodon is a **free, open-source social network server** based on ActivityPub where users can follow friends and discover new ones. On Mastodon, users can publish anything they want: links, pictures, text, and video. All Mastodon servers are interoperable as a federated network (users on one server can seamlessly communicate with users from another one, including non-Mastodon software that implements ActivityPub!) -Click below to **learn more** in a video: - -[![Screenshot](https://blog.joinmastodon.org/2018/06/why-activitypub-is-the-future/ezgif-2-60f1b00403.gif)][youtube_demo] - -[youtube_demo]: https://www.youtube.com/watch?v=IPSbNdBmWKE - ## Navigation - [Project homepage 🐘](https://joinmastodon.org) @@ -53,25 +53,15 @@ Click below to **learn more** in a video: -### No vendor lock-in: Fully interoperable with any conforming platform +**No vendor lock-in: Fully interoperable with any conforming platform** - It doesn't have to be Mastodon; whatever implements ActivityPub is part of the social network! [Learn more](https://blog.joinmastodon.org/2018/06/why-activitypub-is-the-future/) -It doesn't have to be Mastodon; whatever implements ActivityPub is part of the social network! [Learn more](https://blog.joinmastodon.org/2018/06/why-activitypub-is-the-future/) +**Real-time, chronological timeline updates** - updates of people you're following appear in real-time in the UI via WebSockets. There's a firehose view as well! -### Real-time, chronological timeline updates +**Media attachments like images and short videos** - upload and view images and WebM/MP4 videos attached to the updates. Videos with no audio track are treated like GIFs; normal videos loop continuously! -Updates of people you're following appear in real-time in the UI via WebSockets. There's a firehose view as well! +**Safety and moderation tools** - Mastodon includes private posts, locked accounts, phrase filtering, muting, blocking, and all sorts of other features, along with a reporting and moderation system. [Learn more](https://blog.joinmastodon.org/2018/07/cage-the-mastodon/) -### Media attachments like images and short videos - -Upload and view images and WebM/MP4 videos attached to the updates. Videos with no audio track are treated like GIFs; normal videos loop continuously! - -### Safety and moderation tools - -Mastodon includes private posts, locked accounts, phrase filtering, muting, blocking, and all sorts of other features, along with a reporting and moderation system. [Learn more](https://blog.joinmastodon.org/2018/07/cage-the-mastodon/) - -### OAuth2 and a straightforward REST API - -Mastodon acts as an OAuth2 provider, so 3rd party apps can use the REST and Streaming APIs. This results in a rich app ecosystem with a lot of choices! +**OAuth2 and a straightforward REST API** - Mastodon acts as an OAuth2 provider, so 3rd party apps can use the REST and Streaming APIs. This results in a rich app ecosystem with a lot of choices! ## Deployment @@ -90,85 +80,40 @@ Mastodon acts as an OAuth2 provider, so 3rd party apps can use the REST and Stre The repository includes deployment configurations for **Docker and docker-compose** as well as specific platforms like **Heroku**, and **Scalingo**. For Helm charts, reference the [mastodon/chart repository](https://github.com/mastodon/chart). The [**standalone** installation guide](https://docs.joinmastodon.org/admin/install/) is available in the documentation. -## Development - -### Vagrant - -A **Vagrant** configuration is included for development purposes. To use it, complete the following steps: - -- Install Vagrant and Virtualbox -- Install the `vagrant-hostsupdater` plugin: `vagrant plugin install vagrant-hostsupdater` -- Run `vagrant up` -- Run `vagrant ssh -c "cd /vagrant && bin/dev"` -- Open `http://mastodon.local` in your browser - -### macOS - -To set up **macOS** for native development, complete the following steps: - -- Install [Homebrew] and run `brew install postgresql@14 redis imagemagick -libidn nvm` to install the required project dependencies -- Use a Ruby version manager to activate the ruby in `.ruby-version` and run - `nvm use` to activate the node version from `.nvmrc` -- Run the `bin/setup` script, which will install the required ruby gems and node - packages and prepare the database for local development -- Finally, run the `bin/dev` script which will launch services via `overmind` - (if installed) or `foreman` - -### Docker - -For production hosting and deployment with **Docker**, use the `Dockerfile` and -`docker-compose.yml` in the project root directory. - -For local development, install and launch [Docker], and run: - -```shell -docker compose -f .devcontainer/compose.yaml up -d -docker compose -f .devcontainer/compose.yaml exec app bin/setup -docker compose -f .devcontainer/compose.yaml exec app bin/dev -``` - -### Dev Containers - -Within IDEs that support the [Development Containers] specification, start the -"Mastodon on local machine" container from the editor. The necessary `docker -compose` commands to build and setup the container should run automatically. For -**Visual Studio Code** this requires installing the [Dev Container extension]. - -### GitHub Codespaces - -[GitHub Codespaces] provides a web-based version of VS Code and a cloud hosted -development environment configured with the software needed for this project. - -[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)][codespace] - -- Click the button to create a new codespace, and confirm the options -- Wait for the environment to build (takes a few minutes) -- When the editor is ready, run `bin/dev` in the terminal -- Wait for an _Open in Browser_ prompt. This will open Mastodon -- On the _Ports_ tab "stream" setting change _Port visibility_ → _Public_ - ## Contributing Mastodon is **free, open-source software** licensed under **AGPLv3**. -You can open issues for bugs you've found or features you think are missing. You can also submit pull requests to this repository or submit translations using Crowdin. To get started, take a look at [CONTRIBUTING.md](CONTRIBUTING.md). If your contributions are accepted into Mastodon, you can request to be paid through [our OpenCollective](https://opencollective.com/mastodon). +You can open issues for bugs you've found or features you think are missing. You +can also submit pull requests to this repository or translations via Crowdin. To +get started, look at the [CONTRIBUTING] and [DEVELOPMENT] guides. For changes +accepted into Mastodon, you can request to be paid through our [OpenCollective]. -**IRC channel**: #mastodon on irc.libera.chat +**IRC channel**: #mastodon on [`irc.libera.chat`](https://libera.chat) ## License -Copyright (C) 2016-2024 Eugen Rochko & other Mastodon contributors (see [AUTHORS.md](AUTHORS.md)) +Copyright (c) 2016-2024 Eugen Rochko (+ [`mastodon authors`](AUTHORS.md)) -This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +Licensed under GNU Affero General Public License as stated in the [LICENSE](LICENSE): -This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. +``` +Copyright (c) 2016-2024 Eugen Rochko & other Mastodon contributors -You should have received a copy of the GNU Affero General Public License along with this program. If not, seerel="me"
, která brání proti napodování vás na webových stránkách s obsahem vytvořeným uživatelem. Můžete dokonce použít odkaz
v záhlaví stránky místo a
, ale HTML musí být přístupné bez spuštění JavaScript.
@@ -1904,6 +2116,7 @@ cs:
instructions_html: Zkopírujte a vložte níže uvedený kód do HTML vašeho webu. Poté přidejte adresu vašeho webu do jednoho z extra políček na vašem profilu na záložce "Upravit profil" a uložte změny.
verification: Ověření
verified_links: Vaše ověřené odkazy
+ website_verification: Ověření webové stránky
webauthn_credentials:
add: Přidat nový bezpečnostní klíč
create:
diff --git a/config/locales/devise.nan.yml b/config/locales/devise.nan.yml
index 512c65fe8b..6f47b4f652 100644
--- a/config/locales/devise.nan.yml
+++ b/config/locales/devise.nan.yml
@@ -1 +1,15 @@
+---
nan:
+ devise:
+ failure:
+ locked: Lí ê口座hőng鎖定ah。
+ not_found_in_database: Bô ha̍p規定ê %{authentication_keys} á是密碼。
+ pending: Lí ê口座iáu teh審查。
+ timeout: Lí ê作業階段kàu期ah。請koh登入,繼續完成。
+ mailer:
+ two_factor_disabled:
+ title: 2FA關掉ah
+ two_factor_enabled:
+ title: 2FA啟用ah
+ two_factor_recovery_codes_changed:
+ title: 2FA驗證碼改ah
diff --git a/config/locales/doorkeeper.cs.yml b/config/locales/doorkeeper.cs.yml
index 882be66ee0..1ee73c2cb9 100644
--- a/config/locales/doorkeeper.cs.yml
+++ b/config/locales/doorkeeper.cs.yml
@@ -60,6 +60,7 @@ cs:
error:
title: Vyskytla se chyba
new:
+ prompt_html: "%{client_name} by chtěl oprávnění k přístupu k vašemu účtu. Schvalte tuto žádost pouze pokud rozpoznáte a důvěřujete tomuto zdroji."
review_permissions: Zkontrolujte oprávnění
title: Je vyžadována autorizace
show:
diff --git a/config/locales/doorkeeper.nan.yml b/config/locales/doorkeeper.nan.yml
index 512c65fe8b..554d991641 100644
--- a/config/locales/doorkeeper.nan.yml
+++ b/config/locales/doorkeeper.nan.yml
@@ -1 +1,17 @@
+---
nan:
+ activerecord:
+ attributes:
+ doorkeeper/application:
+ name: 應用程式ê名
+ redirect_uri: 重轉ê URI
+ scopes: 範圍
+ website: 應用程式ê網站
+ errors:
+ models:
+ doorkeeper/application:
+ attributes:
+ redirect_uri:
+ invalid_uri: Tio̍h愛是合規定ê URI。
+ relative_uri: Tio̍h愛是絕對ê URI。
+ secured_uri: Tio̍h愛是HTTPS/SSL URI。
diff --git a/config/locales/doorkeeper.sk.yml b/config/locales/doorkeeper.sk.yml
index 774a2648f9..ba9a440a11 100644
--- a/config/locales/doorkeeper.sk.yml
+++ b/config/locales/doorkeeper.sk.yml
@@ -134,6 +134,7 @@ sk:
media: Mediálne prílohy
mutes: Stíšenia
notifications: Upozornenia
+ profile: Váš Mastodon profil
push: Upozornenia push
reports: Hlásenia
search: Vyhľadávanie
diff --git a/config/locales/eo.yml b/config/locales/eo.yml
index c456a424dc..400b03958e 100644
--- a/config/locales/eo.yml
+++ b/config/locales/eo.yml
@@ -936,13 +936,22 @@ eo:
generate: Uzi ŝablonon
generates:
action: Generi
+ chance_to_review_html: "La generataj kondiĉoj de uzado ne aŭtomate publikiĝos. Estos oportuni por vi kontroli la rezultojn. Bonvole entajpu la necesajn detalojn por daŭrigi."
+ explanation_html: La modelo por la kondiĉoj de la servo disponeblas sole por informi. Ĝi nepre ne estas leĝa konsilo pri iu ajn temo. Bonvole konsultu vian propran leĝan konsilanton pri via situacio kaj iuj leĝaj neklarecoj.
title: Agordo de kondiĉoj de uzado
+ history: Historio
+ live: Antaŭmontro
no_history: Ankoraŭ ne estas registritaj ŝanĝoj de la kondiĉoj de la servo.
no_terms_of_service_html: Vi nuntempe ne havas iujn ajn kondiĉojn de la servo agordita. La kondiĉoj de la servo celas doni klarecon kaj protekti vin kontraŭ eblaj respondecoj en disputoj kun viaj uzantoj.
+ notified_on_html: Uzantojn sciigis je %{date}
+ notify_users: Informu uzantojn
preview:
+ explanation_html: 'La retmesaĝo estos alsendata al %{display_count} uzantoj, kiuj kreis konton antaŭ %{date}. La sekvonta teksto inkluziviĝos en la retmesaĝo:'
+ send_preview: Sendu antaŭrigardon al %{email}
send_to_all:
one: Sendi %{display_count} retpoŝton
other: Sendi %{display_count} retpoŝtojn
+ title: Antaŭmontri sciigon pri la kondiĉoj de la servo
publish: Publikigi
published_on_html: Publikigita je %{date}
save_draft: Konservi malneton
@@ -1926,6 +1935,10 @@ eo:
subject: Via konto estas alirita de nova IP-adreso
title: Nova saluto
terms_of_service_changed:
+ agreement: Se vi daŭrige uzos %{domain}, vi aŭtomate interkonsentos pri ĉi tiuj kondiĉoj. Se vi malkonsentas pri la novaj kondiĉoj, vi ĉiutempe rajtas nuligi la interkonsenton kun %{domain} per forigi vian konton.
+ changelog: 'Facile dirite, la ŝanĝoj estas la jenaj:'
+ description: 'Vi ricevas ĉi tiun retmesaĝon ĉar ni faras iujn ŝanĝojn al niaj servokondiĉoj ĉe %{domain}. Ni instigas vin revizii la ĝisdatigitajn kondiĉojn tute ĉi tie:'
+ description_html: Vi ricevas ĉi tiun retmesaĝon ĉar ni faras iujn ŝanĝojn al niaj servokondiĉoj ĉe %{domain}. Ni instigas vin revizii la ĝisdatigitajn kondiĉojn plene ĉi tie.
sign_off: La teamo de %{domain}
subject: Ĝisdatigoj al niaj kondiĉoj de servo
subtitle: La kondiĉoj de la servo de %{domain} ŝanĝiĝas
diff --git a/config/locales/fo.yml b/config/locales/fo.yml
index 00ffed90bb..441217ec0e 100644
--- a/config/locales/fo.yml
+++ b/config/locales/fo.yml
@@ -214,6 +214,7 @@ fo:
enable_user: Ger brúkara virknan
memorialize_account: Minnst til Konto
promote_user: Vís fram Brúkara
+ publish_terms_of_service: Útgev tænastutreytir
reject_appeal: Avvís mótmali
reject_user: Avvís Brúkara
remove_avatar_user: Sletta Avatar
@@ -278,6 +279,7 @@ fo:
enable_user_html: "%{name} gjørdi innritan virkna fyri brúkaran %{target}"
memorialize_account_html: "%{name} broytti kontuna hjá %{target} til eina minnissíðu"
promote_user_html: "%{name} flutti brúkaran %{target} fram"
+ publish_terms_of_service_html: "%{name} útgav dagføringar til tænastutreytirnar"
reject_appeal_html: "%{name} avvísti umsjónaráheitan frá %{target}"
reject_user_html: "%{name} avvísti skráseting hjá %{target}"
remove_avatar_user_html: "%{name} strikaði eftirgjørda skapningin hjá %{target}"
@@ -925,6 +927,32 @@ fo:
search: Leita
title: Frámerki
updated_msg: Frámerkjastillingar dagførdar
+ terms_of_service:
+ back: Aftur til tænastutreytir
+ changelog: Hvat er broytt
+ create: Brúka tínar egnu
+ current: Núverandi
+ draft: Kladda
+ generate: Brúka leist
+ generates:
+ action: Framleið
+ chance_to_review_html: "Framleiddu tænastutreytirnar verða ikki útgivnar av sær sjálvum. Tú fær møguleika at eftirhyggja úrslitini. Vinarliga útfyll neyðugu smálutirnar fyri at halda fram."
+ explanation_html: Leisturin við tænastutreytum er einans til kunningar og skal ikki fatast sum løgfrøðislig ráðgeving yvirhøvur. Vinarliga spyr tín egna løgfrøðisliga ráðgeva um tína støðu og ítøkiligu løgfrøðisligu spurningarnar hjá tær.
+ title: Uppseting av tænastutreytum
+ history: Søga
+ live: Beinleiðis
+ no_history: Enn eru ongar skrásettar broytingar í tænastutreytunum.
+ no_terms_of_service_html: Í løtuni hevur tú ongar tænastutreytir uppsettar. Hugsanin við tænastutreytum er at veita greidleika og at verja teg ímóti møguligum ábyrgdum í ósemjum við tínar brúkarar.
+ notified_on_html: Fráboðan latin brúkarum %{date}
+ notify_users: Gev brúkarum fráboðan
+ preview:
+ explanation_html: 'Teldubrævið verður sent til %{display_count} brúkarar, sum hava stovna kontu áðrenn %{date}. Fylgjandi tekstur kemur við í teldubrævið:'
+ send_preview: Send undanvísing til %{email}
+ title: Undanvís fráboðan um tænastutreytir
+ publish: Útgev
+ published_on_html: Útgivið %{date}
+ save_draft: Goym kladdu
+ title: Tænastutreytir
title: Umsiting
trends:
allow: Loyv
@@ -1156,6 +1184,7 @@ fo:
set_new_password: Áset nýtt loyniorð
setup:
email_below_hint_html: Kekka mappuna við ruskposti ella bið um ein annan. Tú kanst rætta teldupostadressuna, um hon er skeiv.
+ email_settings_hint_html: Trýst á leinkið, sum vit sendu til %{email} fyri at byrja at brúka Mastodon. Vit bíða beint her.
link_not_received: Fekk tú einki leinki?
new_confirmation_instructions_sent: Tú fer at móttaka eitt nýtt teldubræv við váttanarleinkinum um nakrar fáar minuttir!
title: Kekka innbakkan hjá tær
@@ -1164,6 +1193,7 @@ fo:
title: Rita inn á %{domain}
sign_up:
manual_review: Tilmeldingar til %{domain} fara ígjøgnum eina manuella eftirkanning av okkara kjakleiðarum. Fyri at hjálpa okkum at skunda undir skrásetingina, skriva eitt sindur um teg sjálva/n og hví tú vil hava eina kontu á %{domain}.
+ preamble: Við eini kontu á hesum Mastodon ambætaranum ber til hjá tær at fylgja ein og hvønn annan persón á fediversinum, óansæð hvar teirra konta er hýst.
title: Latum okkum fáa teg settan upp á %{domain}.
status:
account_status: Kontustøða
@@ -1175,6 +1205,7 @@ fo:
view_strikes: Vís eldri atsóknir móti tíni kontu
too_fast: Oyðublaðið innsent ov skjótt, royn aftur.
use_security_key: Brúka trygdarlykil
+ user_agreement_html: Eg havi lisið og taki undir við tænastutreytunum og privatlívspolitikkinum
author_attribution:
example_title: Tekstadømi
hint_html: Skrivar tú tíðindi ella greinar til bloggin uttanfyri Mastodon? Her kanst tú stýra, hvussu tú verður tilsipað/ur, tá ið títt tilfar verður deilt á Mastodon.
@@ -1836,6 +1867,8 @@ fo:
too_late: Tað er ov seint at kæra hesa atsókn
tags:
does_not_match_previous_name: samsvarar ikki við undanfarna navnið
+ terms_of_service:
+ title: Tænastutreytir
themes:
contrast: Mastodon (høgur kontrastur)
default: Mastodon (myrkt)
@@ -1896,6 +1929,15 @@ fo:
further_actions_html: Var hetta ikki tú, so mæla vit til, at tú %{action} beinan vegin og at tú ger váttan í tveimum stigum virkna fyri at konta tín kann vera trygg.
subject: Atgongd er fingin til kontu tína frá eini nýggjari IP adressu
title: Ein nýggj innritan
+ terms_of_service_changed:
+ agreement: Við framhaldandi at brúka %{domain} góðtekur tú hesar treytir. Tekur tú ikki undir við dagførdu treytunum, so kanst tú til einhvørja tíð uppsiga avtaluna við %{domain} við at strika kontu tína.
+ changelog: 'Í stuttum merkir henda dagføringin:'
+ description: 'Tú móttekur hetta teldubrævið, tí at vit gera nakrar broytingar í okkara tænastutreytum á %{domain}. Vit eggja tær til at eftirhyggja dagførdu treytirnar her:'
+ description_html: Tú móttekur hetta teldubrævið, tí at vit gera nakrar broytingar í okkara tænastutreytum á %{domain}. Vit eggja tær til at eftirhyggja dagførdu og samlaðu treytirnar her.
+ sign_off: "%{domain} toymið"
+ subject: Dagføringar til okkara tænastutreytir
+ subtitle: Tænastutreytirnar hjá %{domain} eru við at verða broyttar
+ title: Týdningarmikil dagføring
warning:
appeal: Innsend eina kæru
appeal_description: Trýrt tú, at hetta er ein feilur, so kanst tú senda eina kæru til starvsfólkini á %{instance}.
diff --git a/config/locales/gl.yml b/config/locales/gl.yml
index ac6581c8c9..63a2b9b340 100644
--- a/config/locales/gl.yml
+++ b/config/locales/gl.yml
@@ -1859,9 +1859,9 @@ gl:
'63113904': 2 anos
'7889238': 3 meses
min_age_label: Límite temporal
- min_favs: Manter as publicacións favorecidas polo menos
+ min_favs: Manter publicacións favorecidas polo menos
min_favs_hint: Non elimina ningunha das túas publicacións que recibiron alomenos esta cantidade de favorecementos. Deixa en branco para eliminar publicacións independentemente do número de favorecementos
- min_reblogs: Manter publicacións promovidas máis de
+ min_reblogs: Manter publicacións promovidas polo menos
min_reblogs_hint: Non elimina ningunha das túas publicacións se foron promovidas máis deste número de veces. Deixa en branco para eliminar publicacións independentemente do seu número de promocións
stream_entries:
sensitive_content: Contido sensible
diff --git a/config/locales/lv.yml b/config/locales/lv.yml
index 2102e84c99..2d256b475e 100644
--- a/config/locales/lv.yml
+++ b/config/locales/lv.yml
@@ -217,6 +217,7 @@ lv:
enable_user: Ieslēgt Lietotāju
memorialize_account: Saglabāt Kontu Piemiņai
promote_user: Izceltt Lietotāju
+ publish_terms_of_service: Publicēt pakalpojuma izmantošanas noteikumus
reject_appeal: Noraidīt Apelāciju
reject_user: Noraidīt lietotāju
remove_avatar_user: Noņemt profila attēlu
@@ -273,6 +274,7 @@ lv:
enable_user_html: "%{name} iespējoja pieteikšanos lietotājam %{target}"
memorialize_account_html: "%{name} pārvērta %{target} kontu par atmiņas lapu"
promote_user_html: "%{name} paaugstināja lietotāju %{target}"
+ publish_terms_of_service_html: "%{name} padarīja pieejamus pakalpojuma izmantošanas noteikumu atjauninājumus"
reject_appeal_html: "%{name} noraidīja satura pārraudzības lēmuma iebildumu no %{target}"
reject_user_html: "%{name} noraidīja reģistrēšanos no %{target}"
remove_avatar_user_html: "%{name} noņēma %{target} profila attēlu"
@@ -641,7 +643,7 @@ lv:
create_and_resolve: Atrisināt ar piezīmi
create_and_unresolve: Atvērt atkārtoti ar piezīmi
delete: Dzēst
- placeholder: Apraksti veiktās darbības vai citus saistītus atjauninājumus...
+ placeholder: Jāapraksta veiktās darbības vai jebkuri citi saistītie atjauninājumi...
title: Piezīmes
notes_description_html: Skati un atstāj piezīmes citiem moderatoriem un sev nākotnei
processed_msg: 'Pārskats #%{id} veiksmīgi apstrādāts'
@@ -746,13 +748,13 @@ lv:
rules:
add_new: Pievienot noteikumu
delete: Dzēst
- description_html: Lai gan lielākā daļa apgalvo, ka ir izlasījuši pakalpojumu sniegšanas noteikumus un piekrīt tiem, parasti cilvēki to izlasa tikai pēc problēmas rašanās. Padariet vienkāršāku sava servera noteikumu uztveršanu, veidojot tos vienkāršā sarakstā pa punktiem. Centieties, lai atsevišķi noteikumi būtu īsi un vienkārši, taču arī nesadaliet tos daudzos atsevišķos vienumos.
+ description_html: Kaut arī lielākā daļa apgalvo, ka ir lasījuši un piekrīt pakalpojuma izmantošanas noteikumiem, parasti cilvēki tos neizlasa, līdz rodas sarežģījumi. Padari vienkāršāku sava servera noteikumu pārskatīšanu, sniedzot tos vienkāršā uzsvēruma punktu sarakstā! Jāmēģina atsevišķus noteikumus veidot īsus un vienkāršus, bet jāmēģina arī tos nesadalīt daudzos atsevišķos vienumos.
edit: Labot nosacījumu
- empty: Servera noteikumi vēl nav definēti.
+ empty: Vēl nav pievienots neviens servera noteikums.
title: Servera noteikumi
settings:
about:
- manage_rules: Pārvaldīt servera nosacījumus
+ manage_rules: Pārvaldīt servera noteikumus
preamble: Sniedz padziļinātu informāciju par to, kā serveris tiek darbināts, moderēts un finansēts.
rules_hint: Noteikumiem, kas taviem lietotājiem ir jāievēro, ir īpaša sadaļa.
title: Par
@@ -821,6 +823,7 @@ lv:
back_to_account: Atpakaļ uz konta lapu
back_to_report: Atpakaļ uz paziņojumu lapu
batch:
+ add_to_report: 'Pievienot atskaitei #%{id}'
remove_from_report: Noņemt no ziņojuma
report: Ziņojums
contents: Saturs
@@ -832,13 +835,17 @@ lv:
media:
title: Multivide
metadata: Metadati
+ no_history: Šis ieraksts nav bijis labots
no_status_selected: Neviena ziņa netika mainīta, jo neviena netika atlasīta
open: Atvērt ziņu
original_status: Oriģinālā ziņa
reblogs: Reblogi
+ replied_to_html: Atbildēja %{acct_link}
status_changed: Ziņa mainīta
status_title: Publicēja @%{name}
+ title: Konta ieraksti - @%{name}
trending: Aktuāli
+ view_publicly: Skatīt publiski
visibility: Redzamība
with_media: Ar multividi
strikes:
@@ -876,8 +883,8 @@ lv:
message_html: 'Nesaderīga Elasticsearch versija: %{value}'
version_comparison: Darbojas Elasticsearch %{running_version}, tomēr ir nepieciešama %{required_version}
rules_check:
- action: Pārvaldīt servera nosacījumus
- message_html: Tu neesi definējis nevienu servera nosacījumu.
+ action: Pārvaldīt servera noteikumus
+ message_html: Nav pievienots neviens servera noteikums.
sidekiq_process_check:
message_html: Rindā(s) %{value} nedarbojas neviens Sidekiq process. Lūdzu, pārskati savu Sidekiq konfigurāciju
software_version_check:
@@ -907,11 +914,42 @@ lv:
name: Nosaukums
newest: Jaunākie
oldest: Vecākie
+ open: Apskatīt publiski
reset: Atiestatīt
review: Pārskatīt stāvokli
search: Meklēt
title: Tēmturi
updated_msg: Tēmtura iestatījumi ir veiksmīgi atjaunināti
+ terms_of_service:
+ back: Atpakaļ uz pakalpojuma izmantošanas noteikumiem
+ changelog: Kas ir mainījies
+ create: Izmantot savus
+ current: Pašreizējie
+ draft: Melnraksts
+ generate: Izmantot sagatavi
+ generates:
+ action: Izveidot
+ chance_to_review_html: "Izveidotie pakalpojuma izmantošanas noteikumi netiks automātiski publicēti. Būs iespēja izskatīt iznākumu. Lūgums norādīt nepieciešamo informāciju, lai turpinātu."
+ explanation_html: Pakalpojuma izmantošanas noteikumu sagatave tiek piedāvāta tikai izzināšanas nolūkam, un to nevajadzētu izmantot kā juridisku padomu jebkurā jautājumā. Lūgums sazināties ar savu juridisko padomdevēju par saviem apstākļiem un noteiktiem juridiskiem jautājumiem.
+ title: Pakalpojuma izmantošānas noteikumu uzstādīšana
+ history: Vēsture
+ live: Darbībā
+ no_history: Nav ierakstu par pakalpojuma izmantošanas noteikumu izmaiņām.
+ no_terms_of_service_html: Pašlaik nav uzstādīti pakalpojuma izmantošanas noteikumi. Tie ir paredzēti, lai sniegtu skaidrību un aizsargātu no iespējamas atbildības strīdos ar lietotājiem.
+ notified_on_html: Lietotājiem paziņots %{date}
+ notify_users: Paziņot lietotājiem
+ preview:
+ explanation_html: 'E-pasta ziņojums tiks nosūtīts %{display_count} lietotājiem, kuri ir reģistrējušies pirms %{date}. Šis teksts tiks iekļauts e-pasta ziņojumā:'
+ send_preview: Nosūtīt priekšskatījumu uz %{email}
+ send_to_all:
+ one: Nosūtīt %{display_count} e-pasta ziņojumu
+ other: Nosūtīt %{display_count} e-pasta ziņojumus
+ zero: Nosūtīt %{display_count} e-pasta ziņojumu
+ title: Priekškatīt pakalpojuma izmantošanas noteikumu paziņojumu
+ publish: Publicēt
+ published_on_html: Publicēti %{date}
+ save_draft: Saglabāt melnrakstu
+ title: Pakalpojuma izmantošanas noteikumi
title: Pārvaldība
trends:
allow: Atļaut
@@ -1152,6 +1190,7 @@ lv:
view_strikes: Skati iepriekšējos brīdinājumus par savu kontu
too_fast: Veidlapa ir iesniegta pārāk ātri, mēģini vēlreiz.
use_security_key: Lietot drošības atslēgu
+ user_agreement_html: Es esmu izlasījis un piekrītu pakalpojuma izmantošanas noteikumiem un privātuma nosacījumiem
author_attribution:
example_title: Parauga teksts
more_from_html: Vairāk no %{name}
@@ -1609,6 +1648,7 @@ lv:
scheduled_statuses:
over_daily_limit: Tu esi pārsniedzis šodien ieplānoto %{limit} ziņu ierobežojumu
over_total_limit: Tu esi pārsniedzis ieplānoto %{limit} ziņu ierobežojumu
+ too_soon: datumam jābūt nākotnē
self_destruct:
lead_html: Diemžēl domēns %{domain} tiek neatgriezeniski slēgts. Ja tev tur bija konts, tu nevarēsi turpināt to lietot, taču joprojām vari pieprasīt savu datu kopiju.
title: Šis serveris tiek slēgts
@@ -1771,6 +1811,8 @@ lv:
too_late: Brīdinājuma apstrīdēšanas laiks ir nokavēts
tags:
does_not_match_previous_name: nesakrīt ar iepriekšējo nosaukumu
+ terms_of_service:
+ title: Pakalpojuma izmantošanas noteikumi
themes:
contrast: Mastodon (Augsts kontrasts)
default: Mastodon (Tumšs)
@@ -1820,6 +1862,15 @@ lv:
further_actions_html: Ja tas nebiji tu, iesakām nekavējoties %{action} un iespējot divu faktoru autentifikāciju, lai tavs konts būtu drošībā.
subject: Tavam kontam ir piekļūts no jaunas IP adreses
title: Jauna pieteikšanās
+ terms_of_service_changed:
+ agreement: Ar %{domain} izmantošanas tuprināšanu tiek piekrists šiem noteikumiem. Ja ir iebildumi pret atjauninātajiem noteikumiem, savu piekrišanu var atcelt jebkurā laikā ar sava konta izdzēšanu.
+ changelog: 'Šeit īsumā ir aprakstīts, ko šis atjauninājums nozīmē:'
+ description: 'Šis e-pasta ziņojums tika saņemts, jo mēs veicam dažas izmaiņas savos pakalpojuma izmantošanas noteikumos %{domain}. Mēs aicinām pārskatīt pilnus atjauninātos noteikumus šeit:'
+ description_html: Šis e-pasta ziņojums tika saņemts, jo mēs veicam dažas izmaiņas savos pakalpojuma izmantošanas noteikumos %{domain}. Mēs aicinām pārskatīt pilnus atjauninātos noteikumus šeit.
+ sign_off: "%{domain} komanda"
+ subject: Mūsu pakalpojuma izmantošanas noteikumu atjauninājumi
+ subtitle: Mainās %{domain} pakalpojuma izmantošanas noteikumi
+ title: Svarīgs atjauninājums
warning:
appeal: Iesniegt apelāciju
appeal_description: Ja uzskatāt, ka tā ir kļūda, varat iesniegt apelāciju %{instance} darbiniekiem.
diff --git a/config/locales/nan.yml b/config/locales/nan.yml
index d46de7249c..9180b7b064 100644
--- a/config/locales/nan.yml
+++ b/config/locales/nan.yml
@@ -1,6 +1,44 @@
---
nan:
+ about:
+ about_mastodon_html: 社交網路ê未來:Bô廣告、bô企業監控、設計有道德,兼非中心化!加入Mastodon,保有lí ê資料!
+ contact_missing: Iáu bē設定
+ contact_unavailable: 無開放
+ hosted_on: 佇 %{domain} 運作 ê Mastodon站
+ title: 關係本站
+ accounts:
+ followers:
+ other: 跟tuè ê
+ following: Leh跟tuè
+ last_active: 頂kái活動ê時間
+ link_verified_on: Tsit ê連結ê所有權佇 %{date} 受檢查
+ posts:
+ other: PO文
+ posts_tab_heading: PO文
admin:
+ account_moderation_notes:
+ create: 留記錄
+ created_msg: 管理記錄成功建立!
+ destroyed_msg: 管理記錄成功thâi掉!
+ accounts:
+ deleted: Thâi掉ah
+ demote: 降級
+ destroyed_msg: Teh-beh thâi掉 %{username} ê資料
+ disable: 冷凍
+ disable_sign_in_token_auth: 停止用電子phue ê token認證
+ disable_two_factor_authentication: 停止用2FA
+ disabled: 冷凍起來ah
+ display_name: 顯示ê名
+ domain: 域名
+ edit: 編輯
+ email: 電子phue箱
+ email_status: 電子phue ê狀態
+ enable: 取消冷凍
+ location:
+ all: Kui ê
+ local: 本地
+ remote: 別ê站
+ title: 位置
instances:
dashboard:
instance_languages_dimension: Tsia̍p用ê語言
@@ -14,3 +52,6 @@ nan:
too_soon: Tio̍h用未來ê日期。
statuses:
default_language: Kap界面ê語言sio kâng
+ user_mailer:
+ welcome:
+ sign_in_action: 登入
diff --git a/config/locales/pl.yml b/config/locales/pl.yml
index 0110fe540d..83a9674f84 100644
--- a/config/locales/pl.yml
+++ b/config/locales/pl.yml
@@ -956,6 +956,8 @@ pl:
updated_msg: Pomyślnie uaktualniono ustawienia hashtagów
terms_of_service:
draft: Szkic
+ generate: Użyj szablonu
+ save_draft: Zapisz wersję roboczą
title: Administracja
trends:
allow: Zezwól
@@ -1214,6 +1216,7 @@ pl:
view_strikes: Zobacz dawne ostrzeżenia nałożone na twoje konto
too_fast: Zbyt szybko przesłano formularz, spróbuj ponownie.
use_security_key: Użyj klucza bezpieczeństwa
+ user_agreement_html: Przeczytałem i akceptuję warunki korzystania z usługi oraz politykę prywatności
author_attribution:
example_title: Przykładowy tekst
hint_html: Piszesz wiadomości albo bloga poza Mastodonem? Kontroluj jak będą ci przypisywane podczas dizielenia się nimi na Mastodonie.
diff --git a/config/locales/pt-PT.yml b/config/locales/pt-PT.yml
index 41fc9fbecc..7dcf0977f3 100644
--- a/config/locales/pt-PT.yml
+++ b/config/locales/pt-PT.yml
@@ -10,7 +10,7 @@ pt-PT:
followers:
one: Seguidor
other: Seguidores
- following: A seguir
+ following: Seguindo
instance_actor_flash: Esta conta é um ator virtual utilizado para representar o servidor em si e não um utilizador individual. É utilizada para efeitos de federação e não deve ser suspensa.
last_active: última atividade
link_verified_on: A posse desta hiperligação foi verificada em %{date}
@@ -352,7 +352,7 @@ pt-PT:
not_permitted: Não estás autorizado a executar esta ação
overwrite: Substituir
shortcode: Código de atalho
- shortcode_hint: Pelo menos 2 caracteres, apenas caracteres alfanuméricos e traços inferiores
+ shortcode_hint: Pelo menos 2 caracteres, apenas caracteres alfanuméricos e traços inferiores (_)
title: Emojis personalizados
uncategorized: Não categorizados
unlist: Não listar
@@ -1826,8 +1826,8 @@ pt-PT:
private_long: Mostrar só aos seguidores
public: Público
public_long: Todos podem ver
- unlisted: Não inventariado
- unlisted_long: Todos podem ver, mas não será inventariado nas cronologias públicas
+ unlisted: Não listado
+ unlisted_long: Todos podem ver, mas não aparecerá nas cronologias públicas
statuses_cleanup:
enabled: Eliminar publicações antigas automaticamente
enabled_hint: Elimina automaticamente as tuas publicações assim que atingirem um certo limite de tempo, a não ser que correspondam a uma das seguintes exceções
@@ -1945,7 +1945,7 @@ pt-PT:
appeal: Submeter uma contestação
appeal_description: Se achas que isto é um erro, podes submeter uma contestação para a equipa de %{instance}.
categories:
- spam: Spam
+ spam: Publicidade indesejada / spam
violation: O conteúdo infringe as seguintes diretrizes da comunidade
explanation:
delete_statuses: Algumas das tuas mensagens foram consideradas como violando uma ou mais diretrizes da comunidade e foram subsequentemente removidas pelos moderadores do %{instance}.
diff --git a/config/locales/simple_form.ca.yml b/config/locales/simple_form.ca.yml
index 2fecc78c79..58cc0a034c 100644
--- a/config/locales/simple_form.ca.yml
+++ b/config/locales/simple_form.ca.yml
@@ -130,6 +130,9 @@ ca:
show_application: Sempre podràs veure quina aplicació ha publicat els teus tuts.
tag:
name: Només pots canviar la caixa de les lletres, per exemple, per fer-la més llegible
+ terms_of_service_generator:
+ domain: Identificació única del servei en línia que oferiu.
+ jurisdiction: Indiqueu el país on resideix qui paga les factures. Si és una empresa o una altra entitat, indiqueu el país en què està registrada, així com la ciutat, regió, territori o estat, segons calqui.
user:
chosen_languages: Quan estigui marcat, només es mostraran els tuts de les llengües seleccionades en les línies de temps públiques
role: El rol controla quins permisos té l'usuari.
diff --git a/config/locales/simple_form.cs.yml b/config/locales/simple_form.cs.yml
index ef82b3232a..22b92a4434 100644
--- a/config/locales/simple_form.cs.yml
+++ b/config/locales/simple_form.cs.yml
@@ -3,12 +3,14 @@ cs:
simple_form:
hints:
account:
+ attribution_domains_as_text: Jeden na řádek. Chrání před falešným připisování autorství.
discoverable: Vaše veřejné příspěvky a profil mohou být zobrazeny nebo doporučeny v různých oblastech Mastodonu a váš profil může být navrhován ostatním uživatelům.
display_name: Vaše celé jméno nebo přezdívka.
fields: Vaše domovská stránka, zájmena, věk, cokoliv chcete.
indexable: Vaše veřejné příspěvky se mohou objevit ve výsledcích vyhledávání na Mastodonu. Lidé, kteří s vašimi příspěvky interagovaly, je mohou stále vyhledávat.
note: 'Můžete @zmínit jiné osoby nebo #hashtagy.'
show_collections: Lidé budou moci procházet skrz sledující. Lidé, které sledujete, uvidí, že je sledujete bezohledně.
+ unlocked: Lidé vás budou moci sledovat, aniž by vás žádali o schválení. Zrušte zaškrtnutí, pokud chcete kontrolovat požadavky o sledování a vybírat si, zda přijmete nebo odmítnete nové sledující.
account_alias:
acct: Zadejte svůj účet, ze kterého se chcete přesunout jinam, ve formátu přezdívka@doména
account_migration:
@@ -58,6 +60,7 @@ cs:
setting_display_media_default: Skrývat média označená jako citlivá
setting_display_media_hide_all: Vždy skrývat média
setting_display_media_show_all: Vždy zobrazovat média
+ setting_system_scrollbars_ui: Platí pouze pro desktopové prohlížeče založené na Safari nebo Chrome
setting_use_blurhash: Gradienty jsou vytvořeny na základě barvev skrytých médií, ale zakrývají veškeré detaily
setting_use_pending_items: Aktualizovat časovou osu až po kliknutí namísto automatického rolování kanálu
username: Pouze písmena, číslice a podtržítka
@@ -127,8 +130,20 @@ cs:
show_application: I tak budete vždy moci vidět, která aplikace zveřejnila váš příspěvek.
tag:
name: Můžete měnit pouze velikost písmen, například kvůli lepší čitelnosti
+ terms_of_service:
+ changelog: Může být strukturováno pomocí Markdown syntaxu.
+ text: Může být strukturováno pomocí Markdown syntaxu.
+ terms_of_service_generator:
+ admin_email: Právní oznámení zahrnují protioznámení, soudní příkazy, žádosti o stáhnutí příspěvků a žádosti od právních vymahačů.
+ arbitration_address: Může být stejné jako výše uvedená fyzická adresa, nebo „N/A“, pokud používáte e-mail
+ arbitration_website: Může být webový formulář nebo „N/A“, pokud používáte e-mail
+ dmca_address: V případě provozovatelů v USA použijte adresu zapsanou v DMCA Designated Agent Directory. Na přímou žádost je možné použít namísto domovské adresy PO box, použijte DMCA Designated Agent Post Office Box Waiver Request k e-mailovaní Copyright Office a řekněte jim, že jste malým moderátorem obsahu, který se obavá pomsty nabo odplaty za Vaši práci a potřebujete použít PO box, abyste schovali Vaši domovskou adresu před širokou veřejností.
+ dmca_email: Může být stejný e-mail použitý pro „E-mail adresy pro právní upozornění“
+ domain: Jedinečná identifikace online služby, kterou poskytujete.
+ jurisdiction: Uveďte zemi, kde žije ten, kdo platí účty. Pokud je to společnost nebo jiný subjekt, uveďte zemi, v níž je zapsán do obchodního rejstříku, a město, region, území, případně stát, pokud je to povinné.
user:
chosen_languages: Po zaškrtnutí budou ve veřejných časových osách zobrazeny pouze příspěvky ve zvolených jazycích
+ role: Role určuje, která oprávnění uživatel má.
user_role:
color: Barva, která má být použita pro roli v celém UI, jako RGB v hex formátu
highlighted: Toto roli učiní veřejně viditelnou
@@ -141,6 +156,7 @@ cs:
url: Kam budou události odesílány
labels:
account:
+ attribution_domains_as_text: Webové stránky s povolením Vám připsat autorství
discoverable: Zobrazovat profil a příspěvky ve vyhledávacích algoritmech
fields:
name: Označení
@@ -219,6 +235,7 @@ cs:
setting_hide_network: Skrýt mou síť
setting_reduce_motion: Omezit pohyb v animacích
setting_system_font_ui: Použít výchozí písmo systému
+ setting_system_scrollbars_ui: Použít výchozí posuvník systému
setting_theme: Vzhled stránky
setting_trends: Zobrazit dnešní trendy
setting_unfollow_modal: Před zrušením sledování zobrazovat potvrzovací okno
@@ -313,6 +330,17 @@ cs:
name: Hashtag
trendable: Povolit zobrazení tohoto hashtagu mezi populárními
usable: Povolit příspěvkům používat tento hashtag lokálně
+ terms_of_service:
+ changelog: Co se změnilo?
+ text: Podmínky užití
+ terms_of_service_generator:
+ admin_email: E-mailová adresa pro právní upozornění
+ arbitration_address: Fyzická adresa pro upozornění na arbitrační řízení
+ arbitration_website: Webová stránka pro zasílání arbitračních upozornění
+ dmca_address: Fyzická adresa pro oznámení DMCA/porušení autorských práv
+ dmca_email: E-mailová adresa pro oznámení DMCA/porušení autorských práv
+ domain: Doména
+ jurisdiction: Právní příslušnost
user:
role: Role
time_zone: Časové pásmo
diff --git a/config/locales/simple_form.de.yml b/config/locales/simple_form.de.yml
index 201af831ad..ea0681e1af 100644
--- a/config/locales/simple_form.de.yml
+++ b/config/locales/simple_form.de.yml
@@ -137,6 +137,7 @@ de:
admin_email: Rechtliche Hinweise umfassen Gegendarstellungen, Gerichtsbeschlüsse, Anfragen zum Herunternehmen von Inhalten und Anfragen von Strafverfolgungsbehörden.
arbitration_address: Kann wie die Anschrift hierüber sein oder „N/A“, falls eine E-Mail verwendet wird
arbitration_website: Kann ein Webformular sein oder „N/A“, falls eine E-Mail verwendet wird
+ dmca_address: US-Betreiber sollten die im „DMCA Designated Agent Directory“ eingetragene Adresse verwenden. Eine Postfachadresse ist auf direkte Anfrage verfügbar. Verwenden Sie die „DMCA Designated Agent Post Box Waiver“-Anfrage, um per E-Mail die Urheberrechtsbehörde darüber zu unterrichten, dass Sie Inhalte per Heimarbeit moderieren, eventuelle Rache oder Vergeltung für Ihre Handlungen befürchten und deshalb eine Postfachadresse benötigen, um Ihre Privatadresse nicht preiszugeben.
dmca_email: Kann dieselbe E-Mail wie bei „E-Mail-Adresse für rechtliche Hinweise“ sein
domain: Einzigartige Identifizierung des angebotenen Online-Services.
jurisdiction: Gib das Land an, in dem die Person lebt, die alle Rechnungen bezahlt. Falls es sich dabei um ein Unternehmen oder eine andere Einrichtung handelt, gib das Land mit dem Sitz an, sowie die Stadt oder Region.
diff --git a/config/locales/simple_form.eo.yml b/config/locales/simple_form.eo.yml
index a741040477..59fad146d4 100644
--- a/config/locales/simple_form.eo.yml
+++ b/config/locales/simple_form.eo.yml
@@ -130,6 +130,17 @@ eo:
show_application: Vi ĉiam povos vidi kiu aplikaĵo publikigis vian afiŝon ĉiaokaze.
tag:
name: Vi povas ŝanĝi nur la majuskladon de la literoj, ekzemple, por igi ĝin pli legebla
+ terms_of_service:
+ changelog: Povas esti strukturita per sintakso Markdown-a.
+ text: Povas esti strukturita per sintakso Markdown-a.
+ terms_of_service_generator:
+ admin_email: Legalaj sciigoj povas esti kontraŭsciigoj, postulaĵoj de tribunalo, postulaĵoj pri forigo, kaj postulaĵoj de la policaro.
+ arbitration_address: Povas esti la sama kiel Fizika adreso supre, aŭ "N/A" se oni uzas retpoŝton
+ arbitration_website: Povas esti retformo, aŭ "N/A" se oni uzas retpoŝton
+ dmca_address: Por tenantoj en Usono, uzu la adreson registritan en la DMCA Designated Agenŭ Directory. Registrolibro de poŝtskatoloj haveblas per direkta postulo, uzu la DMCA Designated Agent Post Office Box Waiver Request por retpoŝti la Ofico de Kopirajto kaj priskribu, ke vi estas hejm-trovigita administranto por enhavo kaj devas uzi Poŝtskatolon por forigi vian hejmadreson de publika vido.
+ dmca_email: Povas esti la sama retpoŝtadreso uzita por "Retpoŝtadreso por legalaj sciigoj" supre
+ domain: Unika identigilo de la retaj servicoj, ke vi provizas.
+ jurisdiction: Enlistigu la landon, kie loĝas kiu pagas la fakturojn. Se ĝi estas kompanio aŭ alia ento, listigu la landon, kie ĝi estas enkorpigita, kaj la urbon, regionon, teritorion aŭ ŝtaton laŭeble.
user:
chosen_languages: Kun tio markita nur mesaĝoj en elektitaj lingvoj aperos en publikaj tempolinioj
role: La rolo kontrolas kiujn permesojn la uzanto havas.
@@ -323,7 +334,13 @@ eo:
changelog: Kio ŝanĝiĝis?
text: Kondiĉoj de uzado
terms_of_service_generator:
+ admin_email: Retpoŝtadreso por laŭleĝaj avizoj
+ arbitration_address: Fizika adreso por arbitraciaj avizoj
+ arbitration_website: Retejo por sendi arbitraciajn avizojn
+ dmca_address: Fizika adreso por DMCA/kopirajto-avizoj
+ dmca_email: Retpoŝtadreso por DMCA/kopirajto-avizoj
domain: Domajno
+ jurisdiction: Laŭleĝa jurisdikcio
user:
role: Rolo
time_zone: Horzono
diff --git a/config/locales/simple_form.fo.yml b/config/locales/simple_form.fo.yml
index 0c9ec51722..99bb1ccaa0 100644
--- a/config/locales/simple_form.fo.yml
+++ b/config/locales/simple_form.fo.yml
@@ -130,6 +130,17 @@ fo:
show_application: Óansæð, so er altíð møguligt hjá tær at síggja, hvør app postaði tín post.
tag:
name: Tú kanst einans broyta millum stórar og smáar stavir, til dømis fyri at gera tað meira lesiligt
+ terms_of_service:
+ changelog: Kunnu vera uppbygdar við Markdown syntaksi.
+ text: Kunnu vera uppbygdar við Markdown syntaksi.
+ terms_of_service_generator:
+ admin_email: Løgfrøðisligar fráboðanir fata um rættarligar mótfráboðanir, úrskurðir, áheitanir um at taka niður og løgfrøðisligar áheitanir.
+ arbitration_address: Kann vera tann sami sum fysiski bústaðurin omanfyri ella "N/A", um teldupostur verður brúktur
+ arbitration_website: Kann vera ein vevformularur ella "N/A", um teldupostur verður brúktur
+ dmca_address: Fyristøðufólk og -feløg í USA brúka bústaðin, ið er skrásettur í DMCA Designated Agent Directory. Ein postsmoguskráseting er tøk, um biðið verður um hana beinleiðis. Brúka DMCA Designated Agent Post Office Box Waiver Request fyri at senda teldubræv til Copyright Office og greið frá, at tú er ein kjakleiðari, sum virkar heimanifrá, og at tú er bangin fyri hevnd ella afturløning fyri tí, tú ger, og at tú hevur tørv á at brúka eina postsmogu fyri at fjala heimabústaðin fyri almenninginum.
+ dmca_email: Kann vera sami teldupoststaður, sum er brúktur til "Teldupoststaður fyri løgfrøðisligar fráboðanir" omanfyri
+ domain: Makaleys eyðmerking av nettænastuni, sum tú veitir.
+ jurisdiction: Lista landið, har sum tann, ið rindar rokningarnar, livir. Er tað eitt felag ella ein onnur eind, lista landið, har tað er skrásett, umframt býin, økið, umveldið ella statin, alt eftir hvat er hóskandi.
user:
chosen_languages: Tá hetta er valt, verða einans postar í valdum málum vístir á almennum tíðarlinjum
role: Leikluturin stýrir hvørji rættindi, brúkarin hevur.
@@ -319,6 +330,17 @@ fo:
name: Tvíkrossur
trendable: Loyv hesum frámerki at síggjast undir rákum
usable: Loyv postum at brúka hetta frámerki lokalt
+ terms_of_service:
+ changelog: Hvat er broytt?
+ text: Tænastutreytir
+ terms_of_service_generator:
+ admin_email: Teldupoststaður fyri løgfrøðisligar fráboðanir
+ arbitration_address: Fysisk adressa fyri gerðarrættarfráboðanir
+ arbitration_website: Heimasíða har gerðarrættarfráboðanir kunnu innlatast
+ dmca_address: Fysiskur bústaður fyri DMCA/copyright fráboðanir
+ dmca_email: Teldubústaður fyri DMCA/copyright fráboðanir
+ domain: Navnaøki
+ jurisdiction: Løgdømi
user:
role: Leiklutur
time_zone: Tíðarsona
diff --git a/config/locales/simple_form.fr.yml b/config/locales/simple_form.fr.yml
index 1b0469f7a3..62dd23f930 100644
--- a/config/locales/simple_form.fr.yml
+++ b/config/locales/simple_form.fr.yml
@@ -174,7 +174,7 @@ fr:
admin_account_action:
include_statuses: Inclure les messages signalés dans le courriel
send_email_notification: Notifier l’utilisateur par courriel
- text: Attention personnalisée
+ text: Avertissement personnalisé
type: Action
types:
disable: Désactiver
diff --git a/config/locales/simple_form.lv.yml b/config/locales/simple_form.lv.yml
index 2f4a05dca4..7c7b11c1b3 100644
--- a/config/locales/simple_form.lv.yml
+++ b/config/locales/simple_form.lv.yml
@@ -53,7 +53,7 @@ lv:
locale: Lietotāja saskarnes, e-pasta ziņojumu un push paziņojumu valoda
password: Izmanto vismaz 8 rakstzīmes
phrase: Tiks saskaņots neatkarīgi no ziņas teksta reģistra vai satura brīdinājuma
- scopes: Kuriem API lietojumprogrammai būs atļauta piekļuve. Ja izvēlies augstākā līmeņa tvērumu, tev nav jāatlasa atsevišķi vienumi.
+ scopes: Kuriem API lietotnei būs ļauts piekļūt. Ja atlasa augstākā līmeņa tvērumu, nav nepieciešamas atlasīt atsevišķus.
setting_aggregate_reblogs: Nerādīt jaunus izcēlumus ziņām, kas nesen tika palielinātas (ietekmē tikai nesen saņemtos palielinājumus)
setting_always_send_emails: Parasti e-pasta paziņojumi netiek sūtīti, kad aktīvi izmantojat Mastodon
setting_default_sensitive: Sensitīva multivide pēc noklusējuma ir paslēpti, un tos var atklāt, noklikšķinot
@@ -119,8 +119,8 @@ lv:
sign_up_requires_approval: Jaunām reģistrācijām būs nepieciešams tavs apstiprinājums
severity: Izvēlies, kas notiks ar pieprasījumiem no šīs IP adreses
rule:
- hint: Izvēles. Sniedz vairāk informācijas par nosacījumu
- text: Apraksti nosacījumus vai prasības šī servera lietotājiem. Centies, lai tas būtu īss un vienkāršs
+ hint: Izvēles. Sniedz vairāk informācijas par noteikumu
+ text: Jāapraksta nosacījums vai prasība šī servera lietotājiem. Jāmēģina to veidot īsu un vienkāršu
sessions:
otp: 'Ievadi divfaktoru kodu, ko ģenerējusi tava tālruņa lietotne, vai izmanto kādu no atkopšanas kodiem:'
webauthn: Ja tā ir USB atslēga, noteikti ievieto to un, ja nepieciešams, pieskaries tai.
@@ -317,6 +317,11 @@ lv:
name: Tēmturis
trendable: Atļaut šim tēmturim parādīties zem tendencēm
usable: Ļaut ierakstos vietēji izmantot šo tēmturi
+ terms_of_service:
+ changelog: Kas ir mainījies?
+ text: Pakalpojuma izmantošanas nosacījumi
+ terms_of_service_generator:
+ domain: Domēna vārds
user:
role: Loma
time_zone: Laika josla
diff --git a/config/locales/simple_form.nan.yml b/config/locales/simple_form.nan.yml
index 512c65fe8b..d9049a784b 100644
--- a/config/locales/simple_form.nan.yml
+++ b/config/locales/simple_form.nan.yml
@@ -1 +1,11 @@
+---
nan:
+ simple_form:
+ hints:
+ account:
+ display_name: Lí ê全名á是別號。
+ fields: Lí ê頭頁、代名詞、年歲,kap其他beh分享ê。
+ defaults:
+ password: 用 8 ê字元以上
+ setting_display_media_hide_all: 一直khàm掉媒體
+ setting_display_media_show_all: 一直展示媒體
diff --git a/config/locales/simple_form.pt-PT.yml b/config/locales/simple_form.pt-PT.yml
index efc64d6dc6..207d77f38b 100644
--- a/config/locales/simple_form.pt-PT.yml
+++ b/config/locales/simple_form.pt-PT.yml
@@ -63,7 +63,7 @@ pt-PT:
setting_system_scrollbars_ui: Aplica-se apenas a navegadores de desktop baseados no Safari e Chrome
setting_use_blurhash: Os gradientes são baseados nas cores das imagens escondidas, mas ofuscam quaisquer pormenores
setting_use_pending_items: Ocultar as atualizações da cronologia após um clique em vez de percorrer automaticamente a cronologia
- username: Pode utilizar letras, números e sublinhados
+ username: Pode utilizar letras, números e traços inferiores (_)
whole_word: Quando a palavra-chave ou expressão-chave é somente alfanumérica, ela só será aplicada se corresponder à palavra completa
domain_allow:
domain: Este domínio será capaz de obter dados desta instância e os dados dele recebidos serão processados e armazenados
@@ -161,7 +161,7 @@ pt-PT:
fields:
name: Rótulo
value: Conteúdo
- indexable: Incluir mensagens públicas nos resultados da pesquisa
+ indexable: Incluir mensagens públicas nos resultados de pesquisas
show_collections: Mostrar quem sigo e os meus seguidores no perfil
unlocked: Aceitar automaticamente novos seguidores
account_alias:
@@ -205,7 +205,7 @@ pt-PT:
email: Endereço de correio electrónico
expires_in: Expira em
fields: Metadados de perfil
- header: Cabeçalho
+ header: Imagem de cabeçalho
honeypot: "%{label} (não preencher)"
inbox_url: URL da caixa de entrada do repetidor
irreversible: Expandir em vez de esconder
diff --git a/config/locales/simple_form.sv.yml b/config/locales/simple_form.sv.yml
index 72c3f000f6..2d2c1c0060 100644
--- a/config/locales/simple_form.sv.yml
+++ b/config/locales/simple_form.sv.yml
@@ -60,6 +60,7 @@ sv:
setting_display_media_default: Dölj media markerad som känslig
setting_display_media_hide_all: Dölj alltid all media
setting_display_media_show_all: Visa alltid media markerad som känslig
+ setting_system_scrollbars_ui: Gäller endast för webbläsare som är baserade på Safari och Chrome
setting_use_blurhash: Gradienter är baserade på färgerna av de dolda objekten men fördunklar alla detaljer
setting_use_pending_items: Dölj tidslinjeuppdateringar bakom ett klick istället för att automatiskt bläddra i flödet
username: Du kan använda bokstäver, siffror och understreck
@@ -129,6 +130,14 @@ sv:
show_application: Du kommer alltid att kunna se vilken app som publicerat ditt inlägg oavsett.
tag:
name: Du kan bara ändra skriftläget av bokstäverna, till exempel, för att göra det mer läsbart
+ terms_of_service:
+ changelog: Kan struktureras med Markdown syntax.
+ text: Kan struktureras med Markdown syntax.
+ terms_of_service_generator:
+ arbitration_address: Kan vara samma som fysisk adress ovan, eller “N/A” om du använder e-post
+ arbitration_website: Kan vara ett webbformulär, eller ”N/A” om du använder e-post
+ dmca_email: Kan vara samma e-postadress som används för “E-postadress för juridiska meddelanden” ovan
+ jurisdiction: Lista det land där vem som än betalar räkningarna bor. Om det är ett företag eller annan enhet, lista landet där det är inkorporerat, och staden, regionen, territoriet eller staten på lämpligt sätt.
user:
chosen_languages: Vid aktivering visas bara inlägg på dina valda språk i offentliga tidslinjer
role: Rollen styr vilka behörigheter användaren har.
@@ -150,7 +159,7 @@ sv:
name: Etikett
value: Innehåll
indexable: Inkludera offentliga inlägg i sökresultaten
- show_collections: Göm följare och följeslagare på profilen
+ show_collections: Visa följare och följeslagare på profilen
unlocked: Godkänn nya följare automatiskt
account_alias:
acct: Namnet på det gamla kontot
@@ -223,6 +232,7 @@ sv:
setting_hide_network: Göm ditt nätverk
setting_reduce_motion: Minska rörelser i animationer
setting_system_font_ui: Använd systemets standardfont
+ setting_system_scrollbars_ui: Använd systemets standardrullningsfält
setting_theme: Sidans tema
setting_trends: Visa dagens trender
setting_unfollow_modal: Visa bekräftelse innan du slutar följa någon
@@ -317,6 +327,14 @@ sv:
name: Hashtagg
trendable: Tillåt denna hashtagg att visas under trender
usable: Tillåt inlägg att använda denna fyrkantstagg
+ terms_of_service:
+ changelog: Vad har ändrats?
+ text: Användarvillkor
+ terms_of_service_generator:
+ admin_email: E-postadress för juridiska meddelanden
+ dmca_address: Fysisk adress för meddelanden om DMCA/upphovsrätt
+ dmca_email: Fysisk adress för meddelanden om DMCA/upphovsrätt
+ domain: Domän
user:
role: Roll
time_zone: Tidszon
diff --git a/config/locales/simple_form.uk.yml b/config/locales/simple_form.uk.yml
index 49f191bcdd..9ecd12517b 100644
--- a/config/locales/simple_form.uk.yml
+++ b/config/locales/simple_form.uk.yml
@@ -130,6 +130,17 @@ uk:
show_application: Ви завжди зможете побачити, з якого застосунку опубліковано ваш допис.
tag:
name: Тут ви можете лише змінювати регістр літер, щоб підвищити читабельність
+ terms_of_service:
+ changelog: Можна структурувати за допомогою синтаксису Markdown.
+ text: Можна структурувати за допомогою синтаксису Markdown.
+ terms_of_service_generator:
+ admin_email: Юридичні повідомлення містять зустрічні повідомлення, ухвали суду, запити на видалення та запити правоохоронних органів.
+ arbitration_address: Може бути таким самим, як і фізична адреса вище, або "N/A", якщо використано електронну пошту
+ arbitration_website: Може бути вебформою або "N/A", якщо використано електронну пошту
+ dmca_address: Для американських операторів використовуйте адресу, зареєстровану в довіднику призначених агентів DMCA. П.О. Перелік скриньок доступний за прямим запитом. Скористайтеся запитом на відмову від поштової скриньки призначеного агента Закону про захист авторських прав у цифрову епоху, щоб надіслати електронний лист до Бюро авторських прав, і опишіть, що ви домашній модератор вмісту, який боїться помсти чи відплати за свої дії та потребує використання P.O. Box, щоб видалити вашу домашню адресу з публічного перегляду.
+ dmca_email: Це може бути та сама адреса електронної пошти, яку використано в розділі «Електронна адреса для юридичних повідомлень» вище
+ domain: Унікальна ідентифікація онлайн-сервісу, який ви надаєте.
+ jurisdiction: Укажіть країну, де живе той, хто платить за рахунками. Якщо це компанія чи інша організація, вкажіть країну, де вона зареєстрована, а також місто, регіон, територію чи штат відповідно.
user:
chosen_languages: У глобальних стрічках будуть показані дописи тільки вибраними мовами
role: Роль визначає, які права має користувач.
@@ -214,7 +225,7 @@ uk:
setting_default_privacy: Видимість дописів
setting_default_sensitive: Позначати медіа делікатними
setting_delete_modal: Показувати діалог підтвердження під час видалення допису
- setting_disable_hover_cards: Вимкнути попередній перегляд профілю при наведенні
+ setting_disable_hover_cards: Вимкнути попередній перегляд профілю під час наведення мишки
setting_disable_swiping: Вимкнути рух посування
setting_display_media: Показ медіа
setting_display_media_default: За промовчанням
@@ -318,12 +329,18 @@ uk:
listable: Дозволити появу цього хештеґа у каталозі пошуку і пропозицій
name: Хештеґ
trendable: Дозволити появу цього хештеґа у списку популярних хештеґів
- usable: Дозволити дописам використовувати цей хештег локально
+ usable: Дозволити дописам використовувати цей гештеґ локально
terms_of_service:
changelog: Що змінилося?
text: Умови використання
terms_of_service_generator:
+ admin_email: Адреса електронної пошти для юридичних повідомлень
+ arbitration_address: Фізична адреса для арбітражних повідомлень
+ arbitration_website: Сайт для надсилання арбітражних повідомлень
+ dmca_address: Фізична адреса для сповіщень про DMCA/авторські права
+ dmca_email: Фізична адреса для сповіщень про DMCA/авторські права
domain: Домен
+ jurisdiction: Правова юрисдикція
user:
role: Роль
time_zone: Часовий пояс
diff --git a/config/locales/sk.yml b/config/locales/sk.yml
index 72ff7af37f..e7fb218e2d 100644
--- a/config/locales/sk.yml
+++ b/config/locales/sk.yml
@@ -28,6 +28,7 @@ sk:
admin:
account_actions:
action: Vykonaj
+ already_silenced: Tento účet už bol obmedzený.
title: Vykonaj moderovací úkon voči %{acct}
account_moderation_notes:
create: Zanechaj poznámku
@@ -204,6 +205,7 @@ sk:
enable_user: Povoľ užívateľa
memorialize_account: Zmena na „in memoriam“
promote_user: Povýš užívateľskú rolu
+ publish_terms_of_service: Zverejni podmienky prevozu
reject_appeal: Zamietni námietku
reject_user: Zamietni užívateľa
remove_avatar_user: Vymaž avatar
diff --git a/config/locales/sv.yml b/config/locales/sv.yml
index aa2de7de32..7855c911d1 100644
--- a/config/locales/sv.yml
+++ b/config/locales/sv.yml
@@ -214,6 +214,7 @@ sv:
enable_user: Aktivera användare
memorialize_account: Minnesmärk konto
promote_user: Befordra användare
+ publish_terms_of_service: Publicera användarvillkor
reject_appeal: Avvisa överklagande
reject_user: Avvisa användare
remove_avatar_user: Ta bort avatar
@@ -278,6 +279,7 @@ sv:
enable_user_html: "%{name} aktiverade inloggning för användaren %{target}"
memorialize_account_html: "%{name} gjorde %{target}s konto till en minnessida"
promote_user_html: "%{name} befordrade användaren %{target}"
+ publish_terms_of_service_html: "%{name} publicerade uppdateringar till användarvillkoren"
reject_appeal_html: "%{name} avvisade överklagande av modereringsbeslut från %{target}"
reject_user_html: "%{name} avvisade registrering från %{target}"
remove_avatar_user_html: "%{name} tog bort %{target}s avatar"
@@ -926,8 +928,33 @@ sv:
title: Hashtaggar
updated_msg: Hashtagg-inställningarna har uppdaterats
terms_of_service:
+ back: Tillbaka till användarvillkoren
+ changelog: Vad har ändrats
+ create: Använd dina egna
+ current: Nuvarande
draft: Utkast
+ generate: Använd mall
+ generates:
+ action: Generera
+ chance_to_review_html: "De genererade villkoren för tjänsten kommer inte att publiceras automatiskt. Du kommer att ha en chans att granska resultatet. Vänligen fyll i nödvändig information för att fortsätta."
+ explanation_html: Användarvillkorsmallen tillhandahålls endast i informationssyfte, och skall inte tolkas som juridisk rådgivning i något ämne. Rådgör med ditt eget juridiska ombud om din situation och specifika juridiska frågor du har.
+ title: Inställningar för användarvillkor
+ history: Historik
+ live: Aktuella
+ no_history: Det finns inga sparade ändringar i användarvillkoren ännu.
+ no_terms_of_service_html: Du har för närvarande inte några användarvillkor. Användarvillkoren är avsedda att ge klarhet och skydda dig i tvister med dina användare.
+ notified_on_html: Användare meddelade på %{date}
+ notify_users: Meddela användare
+ preview:
+ explanation_html: 'E-postmeddelandet kommer att skickas till %{display_count} användare som har registrerat sig före %{date}. Följande text kommer att inkluderas i meddelandet:'
+ send_preview: Skicka till %{email} för förhandsgranskning
+ send_to_all:
+ one: Skicka %{display_count} meddelande
+ other: Skicka %{display_count} meddelanden
+ title: Förhandsgranska användarvillkorsmeddelande
publish: Publicera
+ published_on_html: Publicerade den %{date}
+ save_draft: Spara utkast
title: Användarvillkor
title: Administration
trends:
@@ -1160,6 +1187,7 @@ sv:
set_new_password: Skriv in nytt lösenord
setup:
email_below_hint_html: Kolla din skräppost-mapp eller begär en ny. Du kan korrigera din e-postadress om den är fel.
+ email_settings_hint_html: Klicka på länken vi skickade till %{email} för att börja använda Mastodon. Vi väntar här.
link_not_received: Fick du ingen länk?
new_confirmation_instructions_sent: Du kommer att få ett nytt e-postmeddelande med bekräftelselänken om några minuter!
title: Kolla din inkorg
@@ -1168,6 +1196,7 @@ sv:
title: Logga in på %{domain}
sign_up:
manual_review: Registreringar på %{domain} går igenom manuell granskning av våra moderatorer. För att hjälpa oss att hantera din registrering, skriv lite om dig själv och varför du vill ha ett konto på %{domain}.
+ preamble: Med ett konto på denna Mastodon-server kan du följa alla andra personer i fediversum, oavsett vilken server deras konto tillhör.
title: Låt oss få igång dig på %{domain}.
status:
account_status: Kontostatus
@@ -1179,6 +1208,7 @@ sv:
view_strikes: Visa tidigare prickar på ditt konto
too_fast: Formuläret har skickats för snabbt, försök igen.
use_security_key: Använd säkerhetsnyckel
+ user_agreement_html: Jag har läst och godkänner användarvillkoren och integritetspolicy
author_attribution:
example_title: Exempeltext
hint_html: Skriver du nyheter eller bloggartiklar utanför Mastodon? Kontrollera hur du får krediteras när de delas på Mastodon.
@@ -1677,6 +1707,7 @@ sv:
scheduled_statuses:
over_daily_limit: Du har överskridit dygnsgränsen på %{limit} schemalagda inlägg
over_total_limit: Du har överskridit gränsen på %{limit} schemalagda inlägg
+ too_soon: datumet måste vara i framtiden
self_destruct:
lead_html: Tyvärr stänger %{domain} för gott. Om du hade ett konto där kommer du inte längre kunna använda det, men du kan fortfarande begära en säkerhetskopia av din data.
title: Denna server stänger ned
@@ -1901,6 +1932,15 @@ sv:
further_actions_html: Om detta inte var du, rekommenderar vi att du snarast %{action} och aktiverar tvåfaktorsautentisering för att hålla ditt konto säkert.
subject: Ditt konto har nåtts från en ny IP-adress
title: En ny inloggning
+ terms_of_service_changed:
+ agreement: Genom att fortsätta använda %{domain} godkänner du dessa villkor. Om du inte håller med om de uppdaterade villkoren kan du när som helst säga upp ditt avtal med %{domain} genom att radera ditt konto.
+ changelog: 'I korthet, här är vad denna uppdatering innebär för dig:'
+ description: 'Du får detta e-postmeddelande eftersom vi gör vissa ändringar i våra användarvillkor på %{domain}. Vi uppmanar dig att granska de uppdaterade villkoren i sin helhet här:'
+ description_html: Du får detta e-postmeddelande eftersom vi gör vissa ändringar i våra användarvillkor på %{domain}. Vi uppmanar dig att granska de uppdaterade villkoren i sin helhet här.
+ sign_off: "%{domain} teamet"
+ subject: Uppdateringar till våra användarvillkor
+ subtitle: Villkoren för tjänsten på %{domain} ändras
+ title: Viktig uppdatering
warning:
appeal: Skicka överklagan
appeal_description: Om du anser detta felaktigt kan du skicka överklagan till administratörerna av %{instance}.
diff --git a/config/locales/uk.yml b/config/locales/uk.yml
index 0478ce6d3b..6f0ddbd81f 100644
--- a/config/locales/uk.yml
+++ b/config/locales/uk.yml
@@ -58,7 +58,7 @@ uk:
title: Змінити роль для %{username}
confirm: Зберегти
confirmed: Підтверджено
- confirming: Зберігається
+ confirming: Підтверджується
custom: Власне
delete: Видалити дані
deleted: Видалено
@@ -92,8 +92,8 @@ uk:
title: Розміщення
login_status: Стан входу
media_attachments: Мультимедійні вкладення
- memorialize: Меморіалізувати
- memorialized: Перетворено на пам'ятник
+ memorialize: Увічнити
+ memorialized: Увічнено
memorialized_msg: "%{username} успішно перетворено на пам'ятний обліковий запис"
moderation:
active: Активний
@@ -193,6 +193,7 @@ uk:
create_domain_block: Створити блокування домену
create_email_domain_block: Створити блокування домену е-пошти
create_ip_block: Створити правило IP
+ create_relay: Створити реле
create_unavailable_domain: Створити недоступний домен
create_user_role: Створити роль
demote_user: Понизити користувача
@@ -204,18 +205,22 @@ uk:
destroy_email_domain_block: Видалити блокування домену е-пошти
destroy_instance: Очистити домен
destroy_ip_block: Видалити правило IP
+ destroy_relay: Видалити реле
destroy_status: Видалити допис
destroy_unavailable_domain: Видалити недоступний домен
destroy_user_role: Знищити роль
disable_2fa_user: Вимкнути 2FA
disable_custom_emoji: Вимкнути користувацькі емодзі
+ disable_relay: Вимкнути реле
disable_sign_in_token_auth_user: Вимкнути автентифікацію за допомогою токена е-пошти для користувача
disable_user: Відключити користувача
enable_custom_emoji: Увімкнути користувацькі емодзі
+ enable_relay: Увімкнути реле
enable_sign_in_token_auth_user: Увімкнути автентифікацію за допомогою токена е-пошти для користувача
enable_user: Активувати користувача
memorialize_account: Меморіалізувати акаунт
promote_user: Підвищити користувача
+ publish_terms_of_service: Опублікувати Умови використання
reject_appeal: Відхилити апеляцію
reject_user: Відхилити користувача
remove_avatar_user: Видалити аватар
@@ -253,6 +258,7 @@ uk:
create_domain_block_html: "%{name} блокує домен %{target}"
create_email_domain_block_html: "%{name} блокує домен електронної пошти %{target}"
create_ip_block_html: "%{name} створює правило для IP %{target}"
+ create_relay_html: "%{name} створив реле %{target}"
create_unavailable_domain_html: "%{name} зупиняє доставляння на домен %{target}"
create_user_role_html: "%{name} створює роль %{target}"
demote_user_html: "%{name} понижує користувача %{target}"
@@ -264,18 +270,22 @@ uk:
destroy_email_domain_block_html: "%{name} розблоковує домен електронної пошти %{target}"
destroy_instance_html: "%{name} очищує домен %{target}"
destroy_ip_block_html: "%{name} видаляє правило для IP %{target}"
+ destroy_relay_html: "%{name} видалив реле %{target}"
destroy_status_html: "%{name} вилучає допис %{target}"
destroy_unavailable_domain_html: "%{name} відновлює доставляння на домен %{target}"
destroy_user_role_html: "%{name} видаляє роль %{target}"
disable_2fa_user_html: "%{name} вимикає двоетапну перевірку для користувача %{target}"
disable_custom_emoji_html: "%{name} вимикає емодзі %{target}"
+ disable_relay_html: "%{name} вимкнув реле %{target}"
disable_sign_in_token_auth_user_html: "%{name} вимикає автентифікацію через токен е-пошти для %{target}"
disable_user_html: "%{name} вимикає вхід для користувача %{target}"
enable_custom_emoji_html: "%{name} вмикає емодзі %{target}"
+ enable_relay_html: "%{name} увімкнув реле %{target}"
enable_sign_in_token_auth_user_html: "%{name} вмикає автентифікацію через токен е-пошти для %{target}"
enable_user_html: "%{name} вмикає вхід для користувача %{target}"
memorialize_account_html: "%{name} перетворює обліковий запис %{target} на сторінку пам'яті"
promote_user_html: "%{name} підвищує користувача %{target}"
+ publish_terms_of_service_html: "%{name} опублікував оновлення умов використання"
reject_appeal_html: "%{name} відхилили звернення на оскарження рішення від %{target}"
reject_user_html: "%{name} відхиляє реєстрацію від %{target}"
remove_avatar_user_html: "%{name} прибирає аватар %{target}"
@@ -846,8 +856,10 @@ uk:
back_to_account: Назад до сторінки облікового запису
back_to_report: Повернутися до сторінки скарги
batch:
+ add_to_report: 'Додати до звіту #%{id}'
remove_from_report: Вилучити зі скарги
report: Скарга
+ contents: Вміст
deleted: Видалено
favourites: Вподобане
history: Історія версій
@@ -856,12 +868,17 @@ uk:
media:
title: Медіа
metadata: Метадані
+ no_history: Цей допис ще не редагували
no_status_selected: Жодного допису не було змінено, оскільки жодного з них не було вибрано
open: Відкрити допис
original_status: Оригінальний допис
reblogs: Поширення
+ replied_to_html: Відповів %{acct_link}
status_changed: Допис змінено
+ status_title: "@%{name} опублікував допис"
+ title: Дописи облікового запису - @%{name}
trending: Популярне
+ view_publicly: Переглянути привселюдно
visibility: Видимість
with_media: З медіа
strikes:
@@ -922,7 +939,7 @@ uk:
moderation:
not_trendable: Не трендові
not_usable: Невикористовувані
- pending_review: Очікує розгляду
+ pending_review: Очікує на розгляд
review_requested: Запит на розгляд
reviewed: Розглянуто
title: Статус
@@ -936,8 +953,34 @@ uk:
reset: Скинути
review: Переглянути допис
search: Пошук
- title: Хештеги
+ title: Гештеґи
updated_msg: Параметри хештеґів успішно оновлені
+ terms_of_service:
+ back: Назад до умов використання
+ changelog: Що змінилося
+ create: Використовувати власний
+ current: Поточний
+ draft: Чернетка
+ generate: Використовувати шаблон
+ generates:
+ action: Згенерувати
+ chance_to_review_html: "Створені умови використання не опублікуються автоматично. Ви матимете змогу переглянути результати. Будь ласка, заповніть потрібні дані, щоб перейти далі."
+ explanation_html: Наданий шаблон умов надання послуг призначений лише для інформаційних цілей і не має розглядатися як юридична консультація з будь-якого питання. Проконсультуйтеся зі своїм юридичним радником щодо вашої ситуації та конкретних юридичних питань, які у вас постали.
+ title: Налаштування Умов обслуговування
+ history: Історія
+ live: Наживо
+ no_history: Змін умов обслуговування поки що не зафіксовано.
+ no_terms_of_service_html: Наразі у вас не налаштовано умов використання. Умови використання призначені для забезпечення ясності та захисту від потенційних зобов’язань у суперечках із користувачами.
+ notified_on_html: Користувачі сповіщені %{date}
+ notify_users: Сповістити користувачів
+ preview:
+ explanation_html: 'Електронний лист буде надіслано %{display_count} користувачам, які зареєструвалися до %{date}. Розміщений нижче текст додасться до електронного листа:'
+ send_preview: Надіслати попередній перегляд на %{email}
+ title: Попередній перегляд сповіщення про умови використання
+ publish: Опублікувати
+ published_on_html: Опубліковано %{date}
+ save_draft: Зберегти чернетку
+ title: Умови використання
title: Адміністрування
trends:
allow: Дозволити
@@ -1186,6 +1229,7 @@ uk:
title: Увійти до %{domain}
sign_up:
manual_review: Реєстрація на %{domain} проходить через ручний розгляд нашими модераторами. Щоб допомогти нам завершити вашу реєстрацію, напишіть трохи про себе і чому ви хочете зареєструватися на %{domain}.
+ preamble: За допомогою облікового запису на цьому сервері Mastodon ви зможете стежити за будь-якою іншою людиною у федіверсі незалежно від того, де розміщений обліковий запис.
title: Налаштуймо вас на %{domain}.
status:
account_status: Стан облікового запису
@@ -1197,6 +1241,7 @@ uk:
view_strikes: Переглянути попередні попередження вашому обліковому запису
too_fast: Форму подано занадто швидко, спробуйте ще раз.
use_security_key: Використовувати ключ безпеки
+ user_agreement_html: Я прочитав і приймаю умови використання та політику конфіденційності< /a>
author_attribution:
example_title: Зразок тексту
hint_html: Ви пишете новини чи статті в блозі за межами Mastodon? Контролюйте, як вони підписуються, коли ними діляться на Mastodon.
@@ -1844,6 +1889,8 @@ uk:
too_late: Запізно оскаржувати це попередження
tags:
does_not_match_previous_name: не збігається з попереднім ім'ям
+ terms_of_service:
+ title: Умови використання
themes:
contrast: Mastodon (Висока контрастність)
default: Mastodon (Темна)
@@ -1904,6 +1951,15 @@ uk:
further_actions_html: Якщо це були не ви. Радимо вам негайно %{action} й увімкнути двоетапну перевірку, щоб уберегти свій обліковий запис.
subject: До вашого облікового запису отримано доступ з нової IP-адреси
title: Новий вхід
+ terms_of_service_changed:
+ agreement: Далі використовуючи %{domain}, ви погоджуєтеся з цими умовами. Якщо ви не згодні з оновленими умовами, ви можете припинити свою угоду з %{domain} будь-якої миті, видаливши ваш обліковий запис.
+ changelog: 'Коротко, ось що це оновлення означає для вас:'
+ description: 'Ви отримали цього електронного листа, тому що ми впроваджуємо деякі зміни в наші умови обслуговування в %{domain}. Радимо переглянути оновлені умови повністю тут:'
+ description_html: Ви отримали цього електронного листа, тому що ми впроваджуємо деякі зміни до наших умов обслуговування в %{domain}. Радимо переглянути повністю оновлені умови тут.
+ sign_off: Команда %{domain}
+ subject: Оновлення до наших умов обслуговування
+ subtitle: Умови використання %{domain} змінюються
+ title: Важливе оновлення
warning:
appeal: Подати апеляцію
appeal_description: Якщо ви вважаєте, що це помилка, ви можете надіслати оскаржити дії персоналу %{instance}.
diff --git a/config/mastodon.yml b/config/mastodon.yml
index 2c09c59e0a..a8870f6757 100644
--- a/config/mastodon.yml
+++ b/config/mastodon.yml
@@ -2,3 +2,10 @@
shared:
self_destruct_value: <%= ENV.fetch('SELF_DESTRUCT', nil) %>
software_update_url: <%= ENV.fetch('UPDATE_CHECK_URL', 'https://api.joinmastodon.org/update-check') %>
+ source:
+ base_url: <%= ENV.fetch('SOURCE_BASE_URL', nil) %>
+ repository: <%= ENV.fetch('GITHUB_REPOSITORY', 'glitch-soc/mastodon') %>
+ tag: <%= ENV.fetch('SOURCE_TAG', nil) %>
+ version:
+ metadata: <%= ['glitch', ENV.fetch('MASTODON_VERSION_METADATA', nil)].compact_blank.join('.') %>
+ prerelease: <%= ENV.fetch('MASTODON_VERSION_PRERELEASE', nil) %>
diff --git a/config/navigation.rb b/config/navigation.rb
index d4244e25af..6a514201f5 100644
--- a/config/navigation.rb
+++ b/config/navigation.rb
@@ -17,7 +17,7 @@ SimpleNavigation::Configuration.run do |navigation|
n.item :preferences, safe_join([material_symbol('settings'), t('settings.preferences')]), settings_preferences_path, if: -> { current_user.functional? && !self_destruct } do |s|
s.item :appearance, safe_join([material_symbol('computer'), t('settings.appearance')]), settings_preferences_appearance_path
s.item :notifications, safe_join([material_symbol('mail'), t('settings.notifications')]), settings_preferences_notifications_path
- s.item :other, safe_join([material_symbol('settings'), t('preferences.other')]), settings_preferences_other_path
+ s.item :other, safe_join([material_symbol('tune'), t('preferences.other')]), settings_preferences_other_path
end
n.item :flavours, safe_join([material_symbol('brush'), t('settings.flavours')]), settings_flavours_path do |flavours|
@@ -71,7 +71,7 @@ SimpleNavigation::Configuration.run do |navigation|
n.item :admin, safe_join([material_symbol('manufacturing'), t('admin.title')]), nil, if: -> { current_user.can?(:view_dashboard, :manage_settings, :manage_rules, :manage_announcements, :manage_custom_emojis, :manage_webhooks, :manage_federation) && !self_destruct } do |s|
s.item :dashboard, safe_join([material_symbol('speed'), t('admin.dashboard.title')]), admin_dashboard_path, if: -> { current_user.can?(:view_dashboard) }
- s.item :settings, safe_join([material_symbol('manufacturing'), t('admin.settings.title')]), admin_settings_path, if: -> { current_user.can?(:manage_settings) }, highlights_on: %r{/admin/settings}
+ s.item :settings, safe_join([material_symbol('tune'), t('admin.settings.title')]), admin_settings_path, if: -> { current_user.can?(:manage_settings) }, highlights_on: %r{/admin/settings}
s.item :terms_of_service, safe_join([material_symbol('description'), t('admin.terms_of_service.title')]), admin_terms_of_service_index_path, highlights_on: %r{/admin/terms_of_service}, if: -> { current_user.can?(:manage_rules) }
s.item :rules, safe_join([material_symbol('gavel'), t('admin.rules.title')]), admin_rules_path, highlights_on: %r{/admin/rules}, if: -> { current_user.can?(:manage_rules) }
s.item :warning_presets, safe_join([material_symbol('warning'), t('admin.warning_presets.title')]), admin_warning_presets_path, highlights_on: %r{/admin/warning_presets}, if: -> { current_user.can?(:manage_settings) }
diff --git a/config/routes.rb b/config/routes.rb
index 3909dd1b77..5adec04c7d 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -55,7 +55,8 @@ Rails.application.routes.draw do
get 'manifest', to: 'manifests#show', defaults: { format: 'json' }
get 'intent', to: 'intents#show'
- get 'custom.css', to: 'custom_css#show', as: :custom_css
+ get 'custom.css', to: 'custom_css#show'
+ resources :custom_css, only: :show, path: :css
get 'remote_interaction_helper', to: 'remote_interaction_helper#index'
diff --git a/db/migrate/20250108111200_add_standard_to_push_subscription.rb b/db/migrate/20250108111200_add_standard_to_push_subscription.rb
new file mode 100644
index 0000000000..eb72f9c62e
--- /dev/null
+++ b/db/migrate/20250108111200_add_standard_to_push_subscription.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class AddStandardToPushSubscription < ActiveRecord::Migration[8.0]
+ def change
+ add_column :web_push_subscriptions, :standard, :boolean, null: false, default: false
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 23b30287cf..eb3af833ec 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,9 +10,9 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[7.2].define(version: 2024_12_16_224825) do
+ActiveRecord::Schema[8.0].define(version: 2025_01_08_111200) do
# These are extensions that must be enabled in order to support this database
- enable_extension "plpgsql"
+ enable_extension "pg_catalog.plpgsql"
create_table "account_aliases", force: :cascade do |t|
t.bigint "account_id", null: false
@@ -1217,6 +1217,7 @@ ActiveRecord::Schema[7.2].define(version: 2024_12_16_224825) do
t.datetime "updated_at", precision: nil, null: false
t.bigint "access_token_id"
t.bigint "user_id"
+ t.boolean "standard", default: false, null: false
t.index ["access_token_id"], name: "index_web_push_subscriptions_on_access_token_id", where: "(access_token_id IS NOT NULL)"
t.index ["user_id"], name: "index_web_push_subscriptions_on_user_id"
end
diff --git a/docker-compose.yml b/docker-compose.yml
index 6048129318..6c44977562 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -59,7 +59,7 @@ services:
web:
# You can uncomment the following line if you want to not use the prebuilt image, for example if you have local code changes
# build: .
- image: ghcr.io/mastodon/mastodon:v4.3.2
+ image: ghcr.io/mastodon/mastodon:v4.3.3
restart: always
env_file: .env.production
command: bundle exec puma -C config/puma.rb
@@ -83,7 +83,7 @@ services:
# build:
# dockerfile: ./streaming/Dockerfile
# context: .
- image: ghcr.io/mastodon/mastodon-streaming:v4.3.2
+ image: ghcr.io/mastodon/mastodon-streaming:v4.3.3
restart: always
env_file: .env.production
command: node ./streaming/index.js
@@ -100,8 +100,9 @@ services:
- redis
sidekiq:
- build: .
- image: ghcr.io/mastodon/mastodon:v4.3.2
+ # You can uncomment the following line if you want to not use the prebuilt image, for example if you have local code changes
+ # build: .
+ image: ghcr.io/mastodon/mastodon:v4.3.3
restart: always
env_file: .env.production
command: bundle exec sidekiq
diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md
new file mode 100644
index 0000000000..39f57dd402
--- /dev/null
+++ b/docs/DEVELOPMENT.md
@@ -0,0 +1,75 @@
+# Development
+
+## Overview
+
+Before starting local development, read the [CONTRIBUTING] guide to understand
+what changes are desirable and what general processes to use.
+
+## Environments
+
+### Vagrant
+
+A **Vagrant** configuration is included for development purposes. To use it,
+complete the following steps:
+
+- Install Vagrant and Virtualbox
+- Install the `vagrant-hostsupdater` plugin:
+ `vagrant plugin install vagrant-hostsupdater`
+- Run `vagrant up`
+- Run `vagrant ssh -c "cd /vagrant && bin/dev"`
+- Open `http://mastodon.local` in your browser
+
+### macOS
+
+To set up **macOS** for native development, complete the following steps:
+
+- Install [Homebrew] and run:
+ `brew install postgresql@14 redis imagemagick libidn nvm`
+ to install the required project dependencies
+- Use a Ruby version manager to activate the ruby in `.ruby-version` and run
+ `nvm use` to activate the node version from `.nvmrc`
+- Run the `bin/setup` script, which will install the required ruby gems and node
+ packages and prepare the database for local development
+- Finally, run the `bin/dev` script which will launch services via `overmind`
+ (if installed) or `foreman`
+
+### Docker
+
+For production hosting and deployment with **Docker**, use the `Dockerfile` and
+`docker-compose.yml` in the project root directory.
+
+For local development, install and launch [Docker], and run:
+
+```shell
+docker compose -f .devcontainer/compose.yaml up -d
+docker compose -f .devcontainer/compose.yaml exec app bin/setup
+docker compose -f .devcontainer/compose.yaml exec app bin/dev
+```
+
+### Dev Containers
+
+Within IDEs that support the [Development Containers] specification, start the
+"Mastodon on local machine" container from the editor. The necessary `docker
+compose` commands to build and setup the container should run automatically. For
+**Visual Studio Code** this requires installing the [Dev Container extension].
+
+### GitHub Codespaces
+
+[GitHub Codespaces] provides a web-based version of VS Code and a cloud hosted
+development environment configured with the software needed for this project.
+
+[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)][codespace]
+
+- Click the button to create a new codespace, and confirm the options
+- Wait for the environment to build (takes a few minutes)
+- When the editor is ready, run `bin/dev` in the terminal
+- Wait for an _Open in Browser_ prompt. This will open Mastodon
+- On the _Ports_ tab "stream" setting change _Port visibility_ → _Public_
+
+[codespace]: https://codespaces.new/mastodon/mastodon?quickstart=1&devcontainer_path=.devcontainer%2Fcodespaces%2Fdevcontainer.json
+[CONTRIBUTING]: CONTRIBUTING.md
+[Dev Container extension]: https://containers.dev/supporting#dev-containers
+[Development Containers]: https://containers.dev/supporting
+[Docker]: https://docs.docker.com
+[GitHub Codespaces]: https://docs.github.com/en/codespaces
+[Homebrew]: https://brew.sh
diff --git a/lib/mastodon/cli/maintenance.rb b/lib/mastodon/cli/maintenance.rb
index 532fbc328a..32ee35c7c7 100644
--- a/lib/mastodon/cli/maintenance.rb
+++ b/lib/mastodon/cli/maintenance.rb
@@ -192,6 +192,7 @@ module Mastodon::CLI
verify_schema_version!
verify_sidekiq_not_active!
verify_backup_warning!
+ disable_timeout!
end
def process_deduplications
@@ -251,6 +252,13 @@ module Mastodon::CLI
fail_with_message 'Maintenance process stopped.' unless yes?('Continue? (Yes/No)')
end
+ def disable_timeout!
+ # Remove server-configured timeout if present
+ database_connection.execute(<<~SQL.squish)
+ SET statement_timeout = 0
+ SQL
+ end
+
def deduplicate_accounts!
remove_index_if_exists!(:accounts, 'index_accounts_on_username_and_domain_lower')
diff --git a/lib/mastodon/migration_warning.rb b/lib/mastodon/migration_warning.rb
index 227f6705d3..b90ceb2916 100644
--- a/lib/mastodon/migration_warning.rb
+++ b/lib/mastodon/migration_warning.rb
@@ -4,7 +4,7 @@ module Mastodon
module MigrationWarning
WARNING_SECONDS = 10
- DEFAULT_WARNING = <<~WARNING_MESSAGE
+ DEFAULT_WARNING = <<~WARNING_MESSAGE.freeze
WARNING: This migration may take a *long* time for large instances.
It will *not* lock tables for any significant time, but it may run
for a very long time. We will pause for #{WARNING_SECONDS} seconds to allow you to
diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb
index a16b3d550f..3aa93bbba6 100644
--- a/lib/mastodon/version.rb
+++ b/lib/mastodon/version.rb
@@ -17,15 +17,15 @@ module Mastodon
end
def default_prerelease
- 'alpha.1'
+ 'alpha.2'
end
def prerelease
- ENV['MASTODON_VERSION_PRERELEASE'].presence || default_prerelease
+ version_configuration[:prerelease].presence || default_prerelease
end
def build_metadata
- ['glitch', ENV.fetch('MASTODON_VERSION_METADATA', nil)].compact_blank.join('.')
+ version_configuration[:metadata]
end
def to_a
@@ -50,16 +50,16 @@ module Mastodon
end
def repository
- ENV.fetch('GITHUB_REPOSITORY', 'glitch-soc/mastodon')
+ source_configuration[:repository]
end
def source_base_url
- ENV.fetch('SOURCE_BASE_URL', "https://github.com/#{repository}")
+ source_configuration[:base_url] || "https://github.com/#{repository}"
end
# specify git tag or commit hash here
def source_tag
- ENV.fetch('SOURCE_TAG', nil)
+ source_configuration[:tag]
end
def source_url
@@ -77,5 +77,17 @@ module Mastodon
def user_agent
@user_agent ||= "Mastodon/#{Version} (#{HTTP::Request::USER_AGENT}; +http#{Rails.configuration.x.use_https ? 's' : ''}://#{Rails.configuration.x.web_domain}/)"
end
+
+ def version_configuration
+ mastodon_configuration.version
+ end
+
+ def source_configuration
+ mastodon_configuration.source
+ end
+
+ def mastodon_configuration
+ Rails.configuration.x.mastodon
+ end
end
end
diff --git a/lint-staged.config.js b/lint-staged.config.js
index 63f5258a94..baf5d0d454 100644
--- a/lint-staged.config.js
+++ b/lint-staged.config.js
@@ -3,7 +3,7 @@ const config = {
'Gemfile|*.{rb,ruby,ru,rake}': 'bin/rubocop --force-exclusion -a',
'*.{js,jsx,ts,tsx}': 'eslint --fix',
'*.{css,scss}': 'stylelint --fix',
- '*.haml': 'bin/haml-lint -a',
+ '*.haml': 'bin/haml-lint -a --parallel',
'**/*.ts?(x)': () => 'tsc -p tsconfig.json --noEmit',
};
diff --git a/package.json b/package.json
index 7da0cc9908..51ecc8a317 100644
--- a/package.json
+++ b/package.json
@@ -102,7 +102,7 @@
"react-hotkeys": "^1.1.4",
"react-immutable-proptypes": "^2.2.0",
"react-immutable-pure-component": "^2.2.2",
- "react-intl": "^6.4.2",
+ "react-intl": "^7.0.0",
"react-motion": "^0.5.2",
"react-notification": "^6.8.5",
"react-overlays": "^5.2.1",
@@ -201,6 +201,8 @@
"webpack-dev-server": "^3.11.3"
},
"resolutions": {
+ "@types/react": "^18.2.7",
+ "@types/react-dom": "^18.2.4",
"kind-of": "^6.0.3",
"webpack/terser-webpack-plugin": "^4.2.3"
},
diff --git a/spec/controllers/admin/follow_recommendations_controller_spec.rb b/spec/controllers/admin/follow_recommendations_controller_spec.rb
deleted file mode 100644
index 82446cd467..0000000000
--- a/spec/controllers/admin/follow_recommendations_controller_spec.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-# frozen_string_literal: true
-
-require 'rails_helper'
-
-RSpec.describe Admin::FollowRecommendationsController do
- render_views
-
- let(:user) { Fabricate(:admin_user) }
-
- before do
- sign_in user, scope: :user
- end
-
- describe 'GET #show' do
- it 'returns http success' do
- get :show
-
- expect(response).to have_http_status(:success)
- end
- end
-end
diff --git a/spec/controllers/admin/terms_of_service/histories_controller_spec.rb b/spec/controllers/admin/terms_of_service/histories_controller_spec.rb
deleted file mode 100644
index 8c2c3a3de3..0000000000
--- a/spec/controllers/admin/terms_of_service/histories_controller_spec.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-# frozen_string_literal: true
-
-require 'rails_helper'
-
-RSpec.describe Admin::TermsOfService::HistoriesController do
- render_views
-
- let(:user) { Fabricate(:admin_user) }
-
- before do
- sign_in user, scope: :user
- end
-
- describe 'GET #show' do
- it 'returns http success' do
- get :show
-
- expect(response).to have_http_status(:success)
- end
- end
-end
diff --git a/spec/controllers/admin/warning_presets_controller_spec.rb b/spec/controllers/admin/warning_presets_controller_spec.rb
deleted file mode 100644
index d416b9c3cf..0000000000
--- a/spec/controllers/admin/warning_presets_controller_spec.rb
+++ /dev/null
@@ -1,85 +0,0 @@
-# frozen_string_literal: true
-
-require 'rails_helper'
-
-RSpec.describe Admin::WarningPresetsController do
- render_views
-
- let(:user) { Fabricate(:admin_user) }
-
- before do
- sign_in user, scope: :user
- end
-
- describe 'GET #index' do
- it 'returns http success' do
- get :index
-
- expect(response).to have_http_status(:success)
- end
- end
-
- describe 'GET #edit' do
- let(:account_warning_preset) { Fabricate(:account_warning_preset) }
-
- it 'returns http success and renders edit' do
- get :edit, params: { id: account_warning_preset.id }
-
- expect(response).to have_http_status(:success)
- expect(response).to render_template(:edit)
- end
- end
-
- describe 'POST #create' do
- context 'with valid data' do
- it 'creates a new account_warning_preset and redirects' do
- expect do
- post :create, params: { account_warning_preset: { text: 'The account_warning_preset text.' } }
- end.to change(AccountWarningPreset, :count).by(1)
-
- expect(response).to redirect_to(admin_warning_presets_path)
- end
- end
-
- context 'with invalid data' do
- it 'does creates a new account_warning_preset and renders index' do
- expect do
- post :create, params: { account_warning_preset: { text: '' } }
- end.to_not change(AccountWarningPreset, :count)
-
- expect(response).to render_template(:index)
- end
- end
- end
-
- describe 'PUT #update' do
- let(:account_warning_preset) { Fabricate(:account_warning_preset, text: 'Original text') }
-
- context 'with valid data' do
- it 'updates the account_warning_preset and redirects' do
- put :update, params: { id: account_warning_preset.id, account_warning_preset: { text: 'Updated text.' } }
-
- expect(response).to redirect_to(admin_warning_presets_path)
- end
- end
-
- context 'with invalid data' do
- it 'does not update the account_warning_preset and renders index' do
- put :update, params: { id: account_warning_preset.id, account_warning_preset: { text: '' } }
-
- expect(response).to render_template(:edit)
- end
- end
- end
-
- describe 'DELETE #destroy' do
- let!(:account_warning_preset) { Fabricate(:account_warning_preset) }
-
- it 'destroys the account_warning_preset and redirects' do
- delete :destroy, params: { id: account_warning_preset.id }
-
- expect { account_warning_preset.reload }.to raise_error(ActiveRecord::RecordNotFound)
- expect(response).to redirect_to(admin_warning_presets_path)
- end
- end
-end
diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb
index 52d92a2b6c..1678005871 100644
--- a/spec/controllers/application_controller_spec.rb
+++ b/spec/controllers/application_controller_spec.rb
@@ -3,6 +3,8 @@
require 'rails_helper'
RSpec.describe ApplicationController do
+ render_views
+
controller do
def success
head 200
@@ -23,9 +25,22 @@ RSpec.describe ApplicationController do
shared_examples 'respond_with_error' do |code|
it "returns http #{code} for http and renders template" do
- expect(subject).to render_template("errors/#{code}", layout: 'error')
+ subject
- expect(response).to have_http_status(code)
+ expect(response)
+ .to have_http_status(code)
+ expect(response.parsed_body)
+ .to have_css('body[class=error]')
+ expect(response.parsed_body.css('h1').to_s)
+ .to include(error_content(code))
+ end
+
+ def error_content(code)
+ if code == 422
+ I18n.t('errors.422.content')
+ else
+ I18n.t("errors.#{code}")
+ end
end
end
diff --git a/spec/controllers/auth/setup_controller_spec.rb b/spec/controllers/auth/setup_controller_spec.rb
deleted file mode 100644
index 28b07cb4b2..0000000000
--- a/spec/controllers/auth/setup_controller_spec.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-# frozen_string_literal: true
-
-require 'rails_helper'
-
-RSpec.describe Auth::SetupController do
- render_views
-
- describe 'GET #show' do
- context 'with a signed out request' do
- it 'returns http redirect' do
- get :show
- expect(response).to be_redirect
- end
- end
-
- context 'with an unconfirmed signed in user' do
- before { sign_in Fabricate(:user, confirmed_at: nil) }
-
- it 'returns http success' do
- get :show
- expect(response).to have_http_status(200)
- end
- end
- end
-end
diff --git a/spec/controllers/settings/migrations_controller_spec.rb b/spec/controllers/settings/migrations_controller_spec.rb
deleted file mode 100644
index dca4c925fd..0000000000
--- a/spec/controllers/settings/migrations_controller_spec.rb
+++ /dev/null
@@ -1,104 +0,0 @@
-# frozen_string_literal: true
-
-require 'rails_helper'
-
-RSpec.describe Settings::MigrationsController do
- render_views
-
- describe 'GET #show' do
- context 'when user is not sign in' do
- subject { get :show }
-
- it { is_expected.to redirect_to new_user_session_path }
- end
-
- context 'when user is sign in' do
- subject { get :show }
-
- let(:user) { Fabricate(:account, moved_to_account: moved_to_account).user }
-
- before { sign_in user, scope: :user }
-
- context 'when user does not have moved to account' do
- let(:moved_to_account) { nil }
-
- it 'renders show page' do
- expect(subject).to have_http_status 200
- expect(subject).to render_template :show
- end
- end
-
- context 'when user has a moved to account' do
- let(:moved_to_account) { Fabricate(:account) }
-
- it 'renders show page' do
- expect(subject).to have_http_status 200
- expect(subject).to render_template :show
- end
- end
- end
- end
-
- describe 'POST #create' do
- context 'when user is not sign in' do
- subject { post :create }
-
- it { is_expected.to redirect_to new_user_session_path }
- end
-
- context 'when user is signed in' do
- subject { post :create, params: { account_migration: { acct: acct, current_password: '12345678' } } }
-
- let(:user) { Fabricate(:user, password: '12345678') }
-
- before { sign_in user, scope: :user }
-
- context 'when migration account is changed' do
- let(:acct) { Fabricate(:account, also_known_as: [ActivityPub::TagManager.instance.uri_for(user.account)]) }
-
- it 'updates moved to account' do
- expect(subject).to redirect_to settings_migration_path
- expect(user.account.reload.moved_to_account_id).to eq acct.id
- end
- end
-
- context 'when acct is the current account' do
- let(:acct) { user.account }
-
- it 'does not update the moved account', :aggregate_failures do
- subject
-
- expect(user.account.reload.moved_to_account_id).to be_nil
- expect(response).to render_template :show
- end
- end
-
- context 'when target account does not reference the account being moved from' do
- let(:acct) { Fabricate(:account, also_known_as: []) }
-
- it 'does not update the moved account', :aggregate_failures do
- subject
-
- expect(user.account.reload.moved_to_account_id).to be_nil
- expect(response).to render_template :show
- end
- end
-
- context 'when a recent migration already exists' do
- let(:acct) { Fabricate(:account, also_known_as: [ActivityPub::TagManager.instance.uri_for(user.account)]) }
-
- before do
- moved_to = Fabricate(:account, also_known_as: [ActivityPub::TagManager.instance.uri_for(user.account)])
- user.account.migrations.create!(acct: moved_to.acct)
- end
-
- it 'does not update the moved account', :aggregate_failures do
- subject
-
- expect(user.account.reload.moved_to_account_id).to be_nil
- expect(response).to render_template :show
- end
- end
- end
- end
-end
diff --git a/spec/helpers/theme_helper_spec.rb b/spec/helpers/theme_helper_spec.rb
index 83a68f4739..5bcff5274f 100644
--- a/spec/helpers/theme_helper_spec.rb
+++ b/spec/helpers/theme_helper_spec.rb
@@ -79,6 +79,45 @@ RSpec.describe ThemeHelper do
end
end
+ describe '#custom_stylesheet' do
+ let(:custom_css) { 'body {}' }
+ let(:custom_digest) { Digest::SHA256.hexdigest(custom_css) }
+
+ before do
+ Setting.custom_css = custom_css
+ end
+
+ context 'when custom css setting value digest is present' do
+ before { Rails.cache.write(:setting_digest_custom_css, custom_digest) }
+
+ it 'returns value from settings' do
+ expect(custom_stylesheet)
+ .to match("/css/custom-#{custom_digest[...8]}.css")
+ end
+ end
+
+ context 'when custom css setting value digest is expired' do
+ before { Rails.cache.delete(:setting_digest_custom_css) }
+
+ it 'returns value from settings' do
+ expect(custom_stylesheet)
+ .to match("/css/custom-#{custom_digest[...8]}.css")
+ end
+ end
+
+ context 'when custom css setting is not present' do
+ before do
+ Setting.custom_css = nil
+ Rails.cache.delete(:setting_digest_custom_css)
+ end
+
+ it 'returns default value' do
+ expect(custom_stylesheet)
+ .to be_blank
+ end
+ end
+ end
+
private
def html_links
diff --git a/spec/lib/activitypub/activity/create_spec.rb b/spec/lib/activitypub/activity/create_spec.rb
index a98e6896ca..ad4cb9997d 100644
--- a/spec/lib/activitypub/activity/create_spec.rb
+++ b/spec/lib/activitypub/activity/create_spec.rb
@@ -162,12 +162,9 @@ RSpec.describe ActivityPub::Activity::Create do
context 'when object publication date is below ISO8601 range' do
let(:object_json) do
- {
- id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
- type: 'Note',
- content: 'Lorem ipsum',
- published: '-0977-11-03T08:31:22Z',
- }
+ build_object(
+ published: '-0977-11-03T08:31:22Z'
+ )
end
it 'creates status with a valid creation date', :aggregate_failures do
@@ -184,12 +181,9 @@ RSpec.describe ActivityPub::Activity::Create do
context 'when object publication date is above ISO8601 range' do
let(:object_json) do
- {
- id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
- type: 'Note',
- content: 'Lorem ipsum',
- published: '10000-11-03T08:31:22Z',
- }
+ build_object(
+ published: '10000-11-03T08:31:22Z'
+ )
end
it 'creates status with a valid creation date', :aggregate_failures do
@@ -206,13 +200,10 @@ RSpec.describe ActivityPub::Activity::Create do
context 'when object has been edited' do
let(:object_json) do
- {
- id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
- type: 'Note',
- content: 'Lorem ipsum',
+ build_object(
published: '2022-01-22T15:00:00Z',
- updated: '2022-01-22T16:00:00Z',
- }
+ updated: '2022-01-22T16:00:00Z'
+ )
end
it 'creates status with appropriate creation and edition dates', :aggregate_failures do
@@ -232,13 +223,10 @@ RSpec.describe ActivityPub::Activity::Create do
context 'when object has update date equal to creation date' do
let(:object_json) do
- {
- id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
- type: 'Note',
- content: 'Lorem ipsum',
+ build_object(
published: '2022-01-22T15:00:00Z',
- updated: '2022-01-22T15:00:00Z',
- }
+ updated: '2022-01-22T15:00:00Z'
+ )
end
it 'creates status and does not mark it as edited' do
@@ -254,11 +242,9 @@ RSpec.describe ActivityPub::Activity::Create do
context 'with an unknown object type' do
let(:object_json) do
- {
- id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
- type: 'Banana',
- content: 'Lorem ipsum',
- }
+ build_object(
+ type: 'Banana'
+ )
end
it 'does not create a status' do
@@ -267,13 +253,7 @@ RSpec.describe ActivityPub::Activity::Create do
end
context 'with a standalone' do
- let(:object_json) do
- {
- id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
- type: 'Note',
- content: 'Lorem ipsum',
- }
- end
+ let(:object_json) { build_object }
it 'creates status' do
expect { subject.perform }.to change(sender.statuses, :count).by(1)
@@ -296,12 +276,9 @@ RSpec.describe ActivityPub::Activity::Create do
context 'when public with explicit public address' do
let(:object_json) do
- {
- id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
- type: 'Note',
- content: 'Lorem ipsum',
- to: 'https://www.w3.org/ns/activitystreams#Public',
- }
+ build_object(
+ to: 'https://www.w3.org/ns/activitystreams#Public'
+ )
end
it 'creates status' do
@@ -316,12 +293,9 @@ RSpec.describe ActivityPub::Activity::Create do
context 'when public with as:Public' do
let(:object_json) do
- {
- id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
- type: 'Note',
- content: 'Lorem ipsum',
- to: 'as:Public',
- }
+ build_object(
+ to: 'as:Public'
+ )
end
it 'creates status' do
@@ -336,12 +310,9 @@ RSpec.describe ActivityPub::Activity::Create do
context 'when public with Public' do
let(:object_json) do
- {
- id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
- type: 'Note',
- content: 'Lorem ipsum',
- to: 'Public',
- }
+ build_object(
+ to: 'Public'
+ )
end
it 'creates status' do
@@ -356,12 +327,9 @@ RSpec.describe ActivityPub::Activity::Create do
context 'when unlisted with explicit public address' do
let(:object_json) do
- {
- id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
- type: 'Note',
- content: 'Lorem ipsum',
- cc: 'https://www.w3.org/ns/activitystreams#Public',
- }
+ build_object(
+ cc: 'https://www.w3.org/ns/activitystreams#Public'
+ )
end
it 'creates status' do
@@ -376,12 +344,9 @@ RSpec.describe ActivityPub::Activity::Create do
context 'when unlisted with as:Public' do
let(:object_json) do
- {
- id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
- type: 'Note',
- content: 'Lorem ipsum',
- cc: 'as:Public',
- }
+ build_object(
+ cc: 'as:Public'
+ )
end
it 'creates status' do
@@ -396,12 +361,9 @@ RSpec.describe ActivityPub::Activity::Create do
context 'when unlisted with Public' do
let(:object_json) do
- {
- id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
- type: 'Note',
- content: 'Lorem ipsum',
- cc: 'Public',
- }
+ build_object(
+ cc: 'Public'
+ )
end
it 'creates status' do
@@ -416,12 +378,9 @@ RSpec.describe ActivityPub::Activity::Create do
context 'when private' do
let(:object_json) do
- {
- id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
- type: 'Note',
- content: 'Lorem ipsum',
- to: 'http://example.com/followers',
- }
+ build_object(
+ to: 'http://example.com/followers'
+ )
end
it 'creates status' do
@@ -436,16 +395,13 @@ RSpec.describe ActivityPub::Activity::Create do
context 'when private with inlined Collection in audience' do
let(:object_json) do
- {
- id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
- type: 'Note',
- content: 'Lorem ipsum',
+ build_object(
to: {
type: 'OrderedCollection',
id: 'http://example.com/followers',
first: 'http://example.com/followers?page=true',
- },
- }
+ }
+ )
end
it 'creates status' do
@@ -462,12 +418,9 @@ RSpec.describe ActivityPub::Activity::Create do
let(:recipient) { Fabricate(:account) }
let(:object_json) do
- {
- id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
- type: 'Note',
- content: 'Lorem ipsum',
- to: ActivityPub::TagManager.instance.uri_for(recipient),
- }
+ build_object(
+ to: ActivityPub::TagManager.instance.uri_for(recipient)
+ )
end
it 'creates status with a silent mention' do
@@ -512,16 +465,13 @@ RSpec.describe ActivityPub::Activity::Create do
let(:recipient) { Fabricate(:account) }
let(:object_json) do
- {
- id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
- type: 'Note',
- content: 'Lorem ipsum',
+ build_object(
to: ActivityPub::TagManager.instance.uri_for(recipient),
tag: {
type: 'Mention',
href: ActivityPub::TagManager.instance.uri_for(recipient),
- },
- }
+ }
+ )
end
it 'creates status with direct visibility' do
@@ -561,12 +511,9 @@ RSpec.describe ActivityPub::Activity::Create do
let(:original_status) { Fabricate(:status) }
let(:object_json) do
- {
- id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
- type: 'Note',
- content: 'Lorem ipsum',
- inReplyTo: ActivityPub::TagManager.instance.uri_for(original_status),
- }
+ build_object(
+ inReplyTo: ActivityPub::TagManager.instance.uri_for(original_status)
+ )
end
it 'creates status' do
@@ -586,17 +533,14 @@ RSpec.describe ActivityPub::Activity::Create do
let(:recipient) { Fabricate(:account) }
let(:object_json) do
- {
- id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
- type: 'Note',
- content: 'Lorem ipsum',
+ build_object(
tag: [
{
type: 'Mention',
href: ActivityPub::TagManager.instance.uri_for(recipient),
},
- ],
- }
+ ]
+ )
end
it 'creates status' do
@@ -611,16 +555,13 @@ RSpec.describe ActivityPub::Activity::Create do
context 'with mentions missing href' do
let(:object_json) do
- {
- id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
- type: 'Note',
- content: 'Lorem ipsum',
+ build_object(
tag: [
{
type: 'Mention',
},
- ],
- }
+ ]
+ )
end
it 'creates status' do
@@ -633,10 +574,7 @@ RSpec.describe ActivityPub::Activity::Create do
context 'with media attachments' do
let(:object_json) do
- {
- id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
- type: 'Note',
- content: 'Lorem ipsum',
+ build_object(
attachment: [
{
type: 'Document',
@@ -648,8 +586,8 @@ RSpec.describe ActivityPub::Activity::Create do
mediaType: 'image/png',
url: 'http://example.com/emoji.png',
},
- ],
- }
+ ]
+ )
end
it 'creates status with correctly-ordered media attachments' do
@@ -665,19 +603,16 @@ RSpec.describe ActivityPub::Activity::Create do
context 'with media attachments with long description' do
let(:object_json) do
- {
- id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
- type: 'Note',
- content: 'Lorem ipsum',
+ build_object(
attachment: [
{
type: 'Document',
mediaType: 'image/png',
url: 'http://example.com/attachment.png',
- name: '*' * 1500,
+ name: '*' * MediaAttachment::MAX_DESCRIPTION_LENGTH,
},
- ],
- }
+ ]
+ )
end
it 'creates status' do
@@ -686,25 +621,22 @@ RSpec.describe ActivityPub::Activity::Create do
status = sender.statuses.first
expect(status).to_not be_nil
- expect(status.media_attachments.map(&:description)).to include('*' * 1500)
+ expect(status.media_attachments.map(&:description)).to include('*' * MediaAttachment::MAX_DESCRIPTION_LENGTH)
end
end
context 'with media attachments with long description as summary' do
let(:object_json) do
- {
- id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
- type: 'Note',
- content: 'Lorem ipsum',
+ build_object(
attachment: [
{
type: 'Document',
mediaType: 'image/png',
url: 'http://example.com/attachment.png',
- summary: '*' * 1500,
+ summary: '*' * MediaAttachment::MAX_DESCRIPTION_LENGTH,
},
- ],
- }
+ ]
+ )
end
it 'creates status' do
@@ -713,16 +645,13 @@ RSpec.describe ActivityPub::Activity::Create do
status = sender.statuses.first
expect(status).to_not be_nil
- expect(status.media_attachments.map(&:description)).to include('*' * 1500)
+ expect(status.media_attachments.map(&:description)).to include('*' * MediaAttachment::MAX_DESCRIPTION_LENGTH)
end
end
context 'with media attachments with focal points' do
let(:object_json) do
- {
- id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
- type: 'Note',
- content: 'Lorem ipsum',
+ build_object(
attachment: [
{
type: 'Document',
@@ -730,8 +659,8 @@ RSpec.describe ActivityPub::Activity::Create do
url: 'http://example.com/attachment.png',
focalPoint: [0.5, -0.7],
},
- ],
- }
+ ]
+ )
end
it 'creates status' do
@@ -746,17 +675,14 @@ RSpec.describe ActivityPub::Activity::Create do
context 'with media attachments missing url' do
let(:object_json) do
- {
- id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
- type: 'Note',
- content: 'Lorem ipsum',
+ build_object(
attachment: [
{
type: 'Document',
mediaType: 'image/png',
},
- ],
- }
+ ]
+ )
end
it 'creates status' do
@@ -769,18 +695,15 @@ RSpec.describe ActivityPub::Activity::Create do
context 'with hashtags' do
let(:object_json) do
- {
- id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
- type: 'Note',
- content: 'Lorem ipsum',
+ build_object(
tag: [
{
type: 'Hashtag',
href: 'http://example.com/blah',
name: '#test',
},
- ],
- }
+ ]
+ )
end
it 'creates status' do
@@ -795,10 +718,7 @@ RSpec.describe ActivityPub::Activity::Create do
context 'with featured hashtags' do
let(:object_json) do
- {
- id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
- type: 'Note',
- content: 'Lorem ipsum',
+ build_object(
to: 'https://www.w3.org/ns/activitystreams#Public',
tag: [
{
@@ -806,8 +726,8 @@ RSpec.describe ActivityPub::Activity::Create do
href: 'http://example.com/blah',
name: '#test',
},
- ],
- }
+ ]
+ )
end
before do
@@ -829,17 +749,14 @@ RSpec.describe ActivityPub::Activity::Create do
context 'with hashtags missing name' do
let(:object_json) do
- {
- id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
- type: 'Note',
- content: 'Lorem ipsum',
+ build_object(
tag: [
{
type: 'Hashtag',
href: 'http://example.com/blah',
},
- ],
- }
+ ]
+ )
end
it 'creates status' do
@@ -852,18 +769,15 @@ RSpec.describe ActivityPub::Activity::Create do
context 'with hashtags invalid name' do
let(:object_json) do
- {
- id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
- type: 'Note',
- content: 'Lorem ipsum',
+ build_object(
tag: [
{
type: 'Hashtag',
href: 'http://example.com/blah',
name: 'foo, #eh !',
},
- ],
- }
+ ]
+ )
end
it 'creates status' do
@@ -876,9 +790,7 @@ RSpec.describe ActivityPub::Activity::Create do
context 'with emojis' do
let(:object_json) do
- {
- id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
- type: 'Note',
+ build_object(
content: 'Lorem ipsum :tinking:',
tag: [
{
@@ -888,8 +800,8 @@ RSpec.describe ActivityPub::Activity::Create do
},
name: 'tinking',
},
- ],
- }
+ ]
+ )
end
it 'creates status' do
@@ -904,9 +816,7 @@ RSpec.describe ActivityPub::Activity::Create do
context 'with emojis served with invalid content-type' do
let(:object_json) do
- {
- id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
- type: 'Note',
+ build_object(
content: 'Lorem ipsum :tinkong:',
tag: [
{
@@ -916,8 +826,8 @@ RSpec.describe ActivityPub::Activity::Create do
},
name: 'tinkong',
},
- ],
- }
+ ]
+ )
end
it 'creates status' do
@@ -932,9 +842,7 @@ RSpec.describe ActivityPub::Activity::Create do
context 'with emojis missing name' do
let(:object_json) do
- {
- id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
- type: 'Note',
+ build_object(
content: 'Lorem ipsum :tinking:',
tag: [
{
@@ -943,8 +851,8 @@ RSpec.describe ActivityPub::Activity::Create do
url: 'http://example.com/emoji.png',
},
},
- ],
- }
+ ]
+ )
end
it 'creates status' do
@@ -957,17 +865,15 @@ RSpec.describe ActivityPub::Activity::Create do
context 'with emojis missing icon' do
let(:object_json) do
- {
- id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
- type: 'Note',
+ build_object(
content: 'Lorem ipsum :tinking:',
tag: [
{
type: 'Emoji',
name: 'tinking',
},
- ],
- }
+ ]
+ )
end
it 'creates status' do
@@ -980,8 +886,7 @@ RSpec.describe ActivityPub::Activity::Create do
context 'with poll' do
let(:object_json) do
- {
- id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
+ build_object(
type: 'Question',
content: 'Which color was the submarine?',
oneOf: [
@@ -999,8 +904,8 @@ RSpec.describe ActivityPub::Activity::Create do
totalItems: 3,
},
},
- ],
- }
+ ]
+ )
end
it 'creates status with a poll' do
@@ -1023,12 +928,10 @@ RSpec.describe ActivityPub::Activity::Create do
let!(:local_status) { Fabricate(:status, poll: poll) }
let(:object_json) do
- {
- id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
- type: 'Note',
+ build_object(
name: 'Yellow',
- inReplyTo: ActivityPub::TagManager.instance.uri_for(local_status),
- }
+ inReplyTo: ActivityPub::TagManager.instance.uri_for(local_status)
+ ).except(:content)
end
it 'adds a vote to the poll with correct uri' do
@@ -1050,12 +953,10 @@ RSpec.describe ActivityPub::Activity::Create do
let!(:local_status) { Fabricate(:status, poll: poll) }
let(:object_json) do
- {
- id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
- type: 'Note',
+ build_object(
name: 'Yellow',
- inReplyTo: ActivityPub::TagManager.instance.uri_for(local_status),
- }
+ inReplyTo: ActivityPub::TagManager.instance.uri_for(local_status)
+ ).except(:content)
end
it 'does not add a vote to the poll' do
@@ -1067,10 +968,7 @@ RSpec.describe ActivityPub::Activity::Create do
context 'with counts' do
let(:object_json) do
- {
- id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
- type: 'Note',
- content: 'Lorem ipsum',
+ build_object(
likes: {
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar', '/likes'].join,
type: 'Collection',
@@ -1080,8 +978,8 @@ RSpec.describe ActivityPub::Activity::Create do
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar', '/shares'].join,
type: 'Collection',
totalItems: 100,
- },
- }
+ }
+ )
end
it 'uses the counts from the created object' do
@@ -1110,12 +1008,9 @@ RSpec.describe ActivityPub::Activity::Create do
end
let(:object_json) do
- {
- id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
- type: 'Note',
- content: 'Lorem ipsum',
- to: 'https://www.w3.org/ns/activitystreams#Public',
- }
+ build_object(
+ to: 'https://www.w3.org/ns/activitystreams#Public'
+ )
end
before do
@@ -1145,13 +1040,7 @@ RSpec.describe ActivityPub::Activity::Create do
subject.perform
end
- let(:object_json) do
- {
- id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
- type: 'Note',
- content: 'Lorem ipsum',
- }
- end
+ let(:object_json) { build_object }
it 'creates status' do
status = sender.statuses.first
@@ -1166,12 +1055,9 @@ RSpec.describe ActivityPub::Activity::Create do
let!(:local_status) { Fabricate(:status) }
let(:object_json) do
- {
- id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
- type: 'Note',
- content: 'Lorem ipsum',
- inReplyTo: ActivityPub::TagManager.instance.uri_for(local_status),
- }
+ build_object(
+ inReplyTo: ActivityPub::TagManager.instance.uri_for(local_status)
+ )
end
before do
@@ -1190,13 +1076,11 @@ RSpec.describe ActivityPub::Activity::Create do
subject { described_class.new(json, sender, delivery: true) }
let!(:local_account) { Fabricate(:account) }
+
let(:object_json) do
- {
- id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
- type: 'Note',
- content: 'Lorem ipsum',
- to: ActivityPub::TagManager.instance.uri_for(local_account),
- }
+ build_object(
+ to: ActivityPub::TagManager.instance.uri_for(local_account)
+ )
end
before do
@@ -1216,12 +1100,9 @@ RSpec.describe ActivityPub::Activity::Create do
let!(:local_account) { Fabricate(:account) }
let(:object_json) do
- {
- id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
- type: 'Note',
- content: 'Lorem ipsum',
- cc: ActivityPub::TagManager.instance.uri_for(local_account),
- }
+ build_object(
+ cc: ActivityPub::TagManager.instance.uri_for(local_account)
+ )
end
before do
@@ -1243,17 +1124,19 @@ RSpec.describe ActivityPub::Activity::Create do
subject.perform
end
- let(:object_json) do
- {
- id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
- type: 'Note',
- content: 'Lorem ipsum',
- }
- end
+ let(:object_json) { build_object }
it 'does not create anything' do
expect(sender.statuses.count).to eq 0
end
end
+
+ def build_object(options = {})
+ {
+ id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
+ type: 'Note',
+ content: 'Lorem ipsum',
+ }.merge(options)
+ end
end
end
diff --git a/spec/lib/activitypub/tag_manager_spec.rb b/spec/lib/activitypub/tag_manager_spec.rb
index 05d2609b0d..4dcfc69824 100644
--- a/spec/lib/activitypub/tag_manager_spec.rb
+++ b/spec/lib/activitypub/tag_manager_spec.rb
@@ -7,17 +7,50 @@ RSpec.describe ActivityPub::TagManager do
subject { described_class.instance }
+ let(:domain) { "#{Rails.configuration.x.use_https ? 'https' : 'http'}://#{Rails.configuration.x.web_domain}" }
+
+ describe '#public_collection?' do
+ it 'returns true for the special public collection and common shorthands' do
+ expect(subject.public_collection?('https://www.w3.org/ns/activitystreams#Public')).to be true
+ expect(subject.public_collection?('as:Public')).to be true
+ expect(subject.public_collection?('Public')).to be true
+ end
+
+ it 'returns false for other URIs' do
+ expect(subject.public_collection?('https://example.com/foo/bar')).to be false
+ end
+ end
+
describe '#url_for' do
- it 'returns a string' do
+ it 'returns a string starting with web domain' do
account = Fabricate(:account)
- expect(subject.url_for(account)).to be_a String
+ expect(subject.url_for(account)).to be_a(String)
+ .and start_with(domain)
end
end
describe '#uri_for' do
- it 'returns a string' do
+ it 'returns a string starting with web domain' do
account = Fabricate(:account)
- expect(subject.uri_for(account)).to be_a String
+ expect(subject.uri_for(account)).to be_a(String)
+ .and start_with(domain)
+ end
+ end
+
+ describe '#activity_uri_for' do
+ context 'when given an account' do
+ it 'raises an exception' do
+ account = Fabricate(:account)
+ expect { subject.activity_uri_for(account) }.to raise_error(ArgumentError)
+ end
+ end
+
+ context 'when given a local activity' do
+ it 'returns a string starting with web domain' do
+ status = Fabricate(:status)
+ expect(subject.uri_for(status)).to be_a(String)
+ .and start_with(domain)
+ end
end
end
diff --git a/spec/lib/delivery_failure_tracker_spec.rb b/spec/lib/delivery_failure_tracker_spec.rb
index 40c8adc4c8..34912c8133 100644
--- a/spec/lib/delivery_failure_tracker_spec.rb
+++ b/spec/lib/delivery_failure_tracker_spec.rb
@@ -42,8 +42,8 @@ RSpec.describe DeliveryFailureTracker do
Fabricate(:unavailable_domain, domain: 'foo.bar')
end
- it 'removes URLs that are unavailable' do
- results = described_class.without_unavailable(['http://example.com/good/inbox', 'http://foo.bar/unavailable/inbox'])
+ it 'removes URLs that are bogus or unavailable' do
+ results = described_class.without_unavailable(['http://example.com/good/inbox', 'http://foo.bar/unavailable/inbox', '{foo:'])
expect(results).to include('http://example.com/good/inbox')
expect(results).to_not include('http://foo.bar/unavailable/inbox')
diff --git a/spec/lib/mastodon/cli/maintenance_spec.rb b/spec/lib/mastodon/cli/maintenance_spec.rb
index 6a15677f43..3e8eb9c360 100644
--- a/spec/lib/mastodon/cli/maintenance_spec.rb
+++ b/spec/lib/mastodon/cli/maintenance_spec.rb
@@ -89,10 +89,8 @@ RSpec.describe Mastodon::CLI::Maintenance do
def prepare_duplicate_data
ActiveRecord::Base.connection.remove_index :accounts, name: :index_accounts_on_username_and_domain_lower
- _remote_account = Fabricate(:account, username: duplicate_account_username, domain: duplicate_account_domain)
- _remote_account_dupe = Fabricate.build(:account, username: duplicate_account_username, domain: duplicate_account_domain).save(validate: false)
- _local_account = Fabricate(:account, username: duplicate_account_username, domain: nil)
- _local_account_dupe = Fabricate.build(:account, username: duplicate_account_username, domain: nil).save(validate: false)
+ duplicate_record(:account, username: duplicate_account_username, domain: duplicate_account_domain)
+ duplicate_record(:account, username: duplicate_account_username, domain: nil)
end
def choose_local_account_to_keep
@@ -127,8 +125,7 @@ RSpec.describe Mastodon::CLI::Maintenance do
def prepare_duplicate_data
ActiveRecord::Base.connection.remove_index :users, :email
- Fabricate(:user, email: duplicate_email)
- Fabricate.build(:user, email: duplicate_email).save(validate: false)
+ duplicate_record(:user, email: duplicate_email)
end
end
@@ -156,8 +153,7 @@ RSpec.describe Mastodon::CLI::Maintenance do
def prepare_duplicate_data
ActiveRecord::Base.connection.remove_index :users, :confirmation_token
- Fabricate(:user, confirmation_token: duplicate_confirmation_token)
- Fabricate.build(:user, confirmation_token: duplicate_confirmation_token).save(validate: false)
+ duplicate_record(:user, confirmation_token: duplicate_confirmation_token)
end
end
@@ -185,8 +181,7 @@ RSpec.describe Mastodon::CLI::Maintenance do
def prepare_duplicate_data
ActiveRecord::Base.connection.remove_index :users, :reset_password_token
- Fabricate(:user, reset_password_token: duplicate_reset_password_token)
- Fabricate.build(:user, reset_password_token: duplicate_reset_password_token).save(validate: false)
+ duplicate_record(:user, reset_password_token: duplicate_reset_password_token)
end
end
@@ -214,8 +209,7 @@ RSpec.describe Mastodon::CLI::Maintenance do
def prepare_duplicate_data
ActiveRecord::Base.connection.remove_index :account_domain_blocks, [:account_id, :domain]
- Fabricate(:account_domain_block, account: account, domain: duplicate_domain)
- Fabricate.build(:account_domain_block, account: account, domain: duplicate_domain).save(validate: false)
+ duplicate_record(:account_domain_block, account: account, domain: duplicate_domain)
end
end
@@ -244,8 +238,7 @@ RSpec.describe Mastodon::CLI::Maintenance do
def prepare_duplicate_data
ActiveRecord::Base.connection.remove_index :announcement_reactions, [:account_id, :announcement_id, :name]
- Fabricate(:announcement_reaction, account: account, announcement: announcement, name: name)
- Fabricate.build(:announcement_reaction, account: account, announcement: announcement, name: name).save(validate: false)
+ duplicate_record(:announcement_reaction, account: account, announcement: announcement, name: name)
end
end
@@ -272,8 +265,7 @@ RSpec.describe Mastodon::CLI::Maintenance do
def prepare_duplicate_data
ActiveRecord::Base.connection.remove_index :conversations, :uri
- Fabricate(:conversation, uri: uri)
- Fabricate.build(:conversation, uri: uri).save(validate: false)
+ duplicate_record(:conversation, uri: uri)
end
end
@@ -301,8 +293,7 @@ RSpec.describe Mastodon::CLI::Maintenance do
def prepare_duplicate_data
ActiveRecord::Base.connection.remove_index :custom_emojis, [:shortcode, :domain]
- Fabricate(:custom_emoji, shortcode: duplicate_shortcode, domain: duplicate_domain)
- Fabricate.build(:custom_emoji, shortcode: duplicate_shortcode, domain: duplicate_domain).save(validate: false)
+ duplicate_record(:custom_emoji, shortcode: duplicate_shortcode, domain: duplicate_domain)
end
end
@@ -329,8 +320,7 @@ RSpec.describe Mastodon::CLI::Maintenance do
def prepare_duplicate_data
ActiveRecord::Base.connection.remove_index :custom_emoji_categories, :name
- Fabricate(:custom_emoji_category, name: duplicate_name)
- Fabricate.build(:custom_emoji_category, name: duplicate_name).save(validate: false)
+ duplicate_record(:custom_emoji_category, name: duplicate_name)
end
end
@@ -357,8 +347,7 @@ RSpec.describe Mastodon::CLI::Maintenance do
def prepare_duplicate_data
ActiveRecord::Base.connection.remove_index :domain_allows, :domain
- Fabricate(:domain_allow, domain: domain)
- Fabricate.build(:domain_allow, domain: domain).save(validate: false)
+ duplicate_record(:domain_allow, domain: domain)
end
end
@@ -385,8 +374,7 @@ RSpec.describe Mastodon::CLI::Maintenance do
def prepare_duplicate_data
ActiveRecord::Base.connection.remove_index :domain_blocks, :domain
- Fabricate(:domain_block, domain: domain)
- Fabricate.build(:domain_block, domain: domain).save(validate: false)
+ duplicate_record(:domain_block, domain: domain)
end
end
@@ -413,8 +401,7 @@ RSpec.describe Mastodon::CLI::Maintenance do
def prepare_duplicate_data
ActiveRecord::Base.connection.remove_index :email_domain_blocks, :domain
- Fabricate(:email_domain_block, domain: domain)
- Fabricate.build(:email_domain_block, domain: domain).save(validate: false)
+ duplicate_record(:email_domain_block, domain: domain)
end
end
@@ -441,8 +428,7 @@ RSpec.describe Mastodon::CLI::Maintenance do
def prepare_duplicate_data
ActiveRecord::Base.connection.remove_index :media_attachments, :shortcode
- Fabricate(:media_attachment, shortcode: shortcode)
- Fabricate.build(:media_attachment, shortcode: shortcode).save(validate: false)
+ duplicate_record(:media_attachment, shortcode: shortcode)
end
end
@@ -469,8 +455,7 @@ RSpec.describe Mastodon::CLI::Maintenance do
def prepare_duplicate_data
ActiveRecord::Base.connection.remove_index :preview_cards, :url
- Fabricate(:preview_card, url: url)
- Fabricate.build(:preview_card, url: url).save(validate: false)
+ duplicate_record(:preview_card, url: url)
end
end
@@ -530,8 +515,7 @@ RSpec.describe Mastodon::CLI::Maintenance do
def prepare_duplicate_data
ActiveRecord::Base.connection.remove_index :tags, name: 'index_tags_on_name_lower_btree'
- Fabricate(:tag, name: name)
- Fabricate.build(:tag, name: name).save(validate: false)
+ duplicate_record(:tag, name: name)
end
end
@@ -558,8 +542,7 @@ RSpec.describe Mastodon::CLI::Maintenance do
def prepare_duplicate_data
ActiveRecord::Base.connection.remove_index :webauthn_credentials, :external_id
- Fabricate(:webauthn_credential, external_id: external_id)
- Fabricate.build(:webauthn_credential, external_id: external_id).save(validate: false)
+ duplicate_record(:webauthn_credential, external_id: external_id)
end
end
@@ -586,11 +569,15 @@ RSpec.describe Mastodon::CLI::Maintenance do
def prepare_duplicate_data
ActiveRecord::Base.connection.remove_index :webhooks, :url
- Fabricate(:webhook, url: url)
- Fabricate.build(:webhook, url: url).save(validate: false)
+ duplicate_record(:webhook, url: url)
end
end
+ def duplicate_record(fabricator, options = {})
+ Fabricate(fabricator, options)
+ Fabricate.build(fabricator, options).save(validate: false)
+ end
+
def agree_to_backup_warning
allow(cli.shell)
.to receive(:yes?)
diff --git a/spec/lib/request_spec.rb b/spec/lib/request_spec.rb
index c600a48ee2..f17cf637b9 100644
--- a/spec/lib/request_spec.rb
+++ b/spec/lib/request_spec.rb
@@ -4,7 +4,9 @@ require 'rails_helper'
require 'securerandom'
RSpec.describe Request do
- subject { described_class.new(:get, 'http://example.com') }
+ subject { described_class.new(:get, 'http://example.com', **options) }
+
+ let(:options) { {} }
describe '#headers' do
it 'returns user agent' do
@@ -39,8 +41,8 @@ RSpec.describe Request do
end
describe '#perform' do
- context 'with valid host' do
- before { stub_request(:get, 'http://example.com') }
+ context 'with valid host and non-persistent connection' do
+ before { stub_request(:get, 'http://example.com').to_return(body: 'lorem ipsum') }
it 'executes a HTTP request' do
expect { |block| subject.perform(&block) }.to yield_control
@@ -71,9 +73,9 @@ RSpec.describe Request do
expect(subject.send(:http_client)).to have_received(:close)
end
- it 'returns response which implements body_with_limit' do
+ it 'yields response' do
subject.perform do |response|
- expect(response).to respond_to :body_with_limit
+ expect(response.body_with_limit).to eq 'lorem ipsum'
end
end
end
@@ -95,6 +97,43 @@ RSpec.describe Request do
expect { subject.perform }.to raise_error Mastodon::ValidationError
end
end
+
+ context 'with persistent connection' do
+ before { stub_request(:get, 'http://example.com').to_return(body: SecureRandom.random_bytes(2.megabytes)) }
+
+ let(:http_client) { described_class.http_client.persistent('http://example.com') }
+ let(:options) { { http_client: http_client } }
+
+ it 'leaves connection open after completely consumed response' do
+ allow(http_client).to receive(:close)
+
+ subject.perform { |response| response.truncated_body(3.megabytes) }
+
+ expect(http_client).to_not have_received(:close)
+ end
+
+ it 'leaves connection open after nearly consumed response' do
+ allow(http_client).to receive(:close)
+
+ subject.perform { |response| response.truncated_body(1.8.megabytes) }
+
+ expect(http_client).to_not have_received(:close)
+ end
+
+ it 'closes connection after unconsumed response' do
+ allow(http_client).to receive(:close)
+
+ subject.perform
+
+ expect(http_client).to have_received(:close)
+ end
+
+ it 'yields response' do
+ subject.perform do |response|
+ expect(response.body_with_limit(2.megabytes).size).to eq 2.megabytes
+ end
+ end
+ end
end
describe "response's body_with_limit method" do
diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb
index 77341b7786..a0187b6211 100644
--- a/spec/models/account_spec.rb
+++ b/spec/models/account_spec.rb
@@ -3,6 +3,7 @@
require 'rails_helper'
RSpec.describe Account do
+ include_examples 'Account::Search'
include_examples 'Reviewable'
context 'with an account record' do
@@ -48,14 +49,44 @@ RSpec.describe Account do
end
describe '#local?' do
- it 'returns true when the account is local' do
- account = Fabricate(:account, domain: nil)
- expect(account.local?).to be true
+ context 'when the domain is null' do
+ subject { Fabricate.build :account, domain: nil }
+
+ it { is_expected.to be_local }
end
- it 'returns false when the account is on a different domain' do
- account = Fabricate(:account, domain: 'foreign.tld')
- expect(account.local?).to be false
+ context 'when the domain is present' do
+ subject { Fabricate.build :account, domain: 'host.example' }
+
+ it { is_expected.to_not be_local }
+ end
+ end
+
+ describe '#remote?' do
+ context 'when the domain is null' do
+ subject { Fabricate.build :account, domain: nil }
+
+ it { is_expected.to_not be_remote }
+ end
+
+ context 'when the domain is present' do
+ subject { Fabricate.build :account, domain: 'host.example' }
+
+ it { is_expected.to be_remote }
+ end
+ end
+
+ describe '#actor_type_application?' do
+ context 'when the actor is not of type application' do
+ subject { Fabricate.build :account, actor_type: 'Person' }
+
+ it { is_expected.to_not be_actor_type_application }
+ end
+
+ context 'when the actor is of type application' do
+ subject { Fabricate.build :account, actor_type: 'Application' }
+
+ it { is_expected.to be_actor_type_application }
end
end
@@ -324,271 +355,6 @@ RSpec.describe Account do
end
end
- describe '.search_for' do
- before do
- _missing = Fabricate(
- :account,
- display_name: 'Missing',
- username: 'missing',
- domain: 'missing.com'
- )
- end
-
- it 'does not return suspended users' do
- Fabricate(
- :account,
- display_name: 'Display Name',
- username: 'username',
- domain: 'example.com',
- suspended: true
- )
-
- results = described_class.search_for('username')
- expect(results).to eq []
- end
-
- it 'does not return unapproved users' do
- match = Fabricate(
- :account,
- display_name: 'Display Name',
- username: 'username'
- )
-
- match.user.update(approved: false)
-
- results = described_class.search_for('username')
- expect(results).to eq []
- end
-
- it 'does not return unconfirmed users' do
- match = Fabricate(
- :account,
- display_name: 'Display Name',
- username: 'username'
- )
-
- match.user.update(confirmed_at: nil)
-
- results = described_class.search_for('username')
- expect(results).to eq []
- end
-
- it 'accepts ?, \, : and space as delimiter' do
- match = Fabricate(
- :account,
- display_name: 'A & l & i & c & e',
- username: 'username',
- domain: 'example.com'
- )
-
- results = described_class.search_for('A?l\i:c e')
- expect(results).to eq [match]
- end
-
- it 'finds accounts with matching display_name' do
- match = Fabricate(
- :account,
- display_name: 'Display Name',
- username: 'username',
- domain: 'example.com'
- )
-
- results = described_class.search_for('display')
- expect(results).to eq [match]
- end
-
- it 'finds accounts with matching username' do
- match = Fabricate(
- :account,
- display_name: 'Display Name',
- username: 'username',
- domain: 'example.com'
- )
-
- results = described_class.search_for('username')
- expect(results).to eq [match]
- end
-
- it 'finds accounts with matching domain' do
- match = Fabricate(
- :account,
- display_name: 'Display Name',
- username: 'username',
- domain: 'example.com'
- )
-
- results = described_class.search_for('example')
- expect(results).to eq [match]
- end
-
- it 'limits via constant by default' do
- stub_const('Account::Search::DEFAULT_LIMIT', 1)
- 2.times.each { Fabricate(:account, display_name: 'Display Name') }
- results = described_class.search_for('display')
- expect(results.size).to eq 1
- end
-
- it 'accepts arbitrary limits' do
- 2.times.each { Fabricate(:account, display_name: 'Display Name') }
- results = described_class.search_for('display', limit: 1)
- expect(results.size).to eq 1
- end
-
- it 'ranks multiple matches higher' do
- matches = [
- { username: 'username', display_name: 'username' },
- { display_name: 'Display Name', username: 'username', domain: 'example.com' },
- ].map(&method(:Fabricate).curry(2).call(:account))
-
- results = described_class.search_for('username')
- expect(results).to eq matches
- end
- end
-
- describe '.advanced_search_for' do
- let(:account) { Fabricate(:account) }
-
- context 'when limiting search to followed accounts' do
- it 'accepts ?, \, : and space as delimiter' do
- match = Fabricate(
- :account,
- display_name: 'A & l & i & c & e',
- username: 'username',
- domain: 'example.com'
- )
- account.follow!(match)
-
- results = described_class.advanced_search_for('A?l\i:c e', account, limit: 10, following: true)
- expect(results).to eq [match]
- end
-
- it 'does not return non-followed accounts' do
- Fabricate(
- :account,
- display_name: 'A & l & i & c & e',
- username: 'username',
- domain: 'example.com'
- )
-
- results = described_class.advanced_search_for('A?l\i:c e', account, limit: 10, following: true)
- expect(results).to eq []
- end
-
- it 'does not return suspended users' do
- Fabricate(
- :account,
- display_name: 'Display Name',
- username: 'username',
- domain: 'example.com',
- suspended: true
- )
-
- results = described_class.advanced_search_for('username', account, limit: 10, following: true)
- expect(results).to eq []
- end
-
- it 'does not return unapproved users' do
- match = Fabricate(
- :account,
- display_name: 'Display Name',
- username: 'username'
- )
-
- match.user.update(approved: false)
-
- results = described_class.advanced_search_for('username', account, limit: 10, following: true)
- expect(results).to eq []
- end
-
- it 'does not return unconfirmed users' do
- match = Fabricate(
- :account,
- display_name: 'Display Name',
- username: 'username'
- )
-
- match.user.update(confirmed_at: nil)
-
- results = described_class.advanced_search_for('username', account, limit: 10, following: true)
- expect(results).to eq []
- end
- end
-
- it 'does not return suspended users' do
- Fabricate(
- :account,
- display_name: 'Display Name',
- username: 'username',
- domain: 'example.com',
- suspended: true
- )
-
- results = described_class.advanced_search_for('username', account)
- expect(results).to eq []
- end
-
- it 'does not return unapproved users' do
- match = Fabricate(
- :account,
- display_name: 'Display Name',
- username: 'username'
- )
-
- match.user.update(approved: false)
-
- results = described_class.advanced_search_for('username', account)
- expect(results).to eq []
- end
-
- it 'does not return unconfirmed users' do
- match = Fabricate(
- :account,
- display_name: 'Display Name',
- username: 'username'
- )
-
- match.user.update(confirmed_at: nil)
-
- results = described_class.advanced_search_for('username', account)
- expect(results).to eq []
- end
-
- it 'accepts ?, \, : and space as delimiter' do
- match = Fabricate(
- :account,
- display_name: 'A & l & i & c & e',
- username: 'username',
- domain: 'example.com'
- )
-
- results = described_class.advanced_search_for('A?l\i:c e', account)
- expect(results).to eq [match]
- end
-
- it 'limits result count by default value' do
- stub_const('Account::Search::DEFAULT_LIMIT', 1)
- 2.times { Fabricate(:account, display_name: 'Display Name') }
- results = described_class.advanced_search_for('display', account)
- expect(results.size).to eq 1
- end
-
- it 'accepts arbitrary limits' do
- 2.times { Fabricate(:account, display_name: 'Display Name') }
- results = described_class.advanced_search_for('display', account, limit: 1)
- expect(results.size).to eq 1
- end
-
- it 'ranks followed accounts higher' do
- match = Fabricate(:account, username: 'Matching')
- followed_match = Fabricate(:account, username: 'Matcher')
- Fabricate(:follow, account: account, target_account: followed_match)
-
- results = described_class.advanced_search_for('match', account)
- expect(results).to eq [followed_match, match]
- expect(results.first.rank).to be > results.last.rank
- end
- end
-
describe '#statuses_count' do
subject { Fabricate(:account) }
@@ -787,6 +553,8 @@ RSpec.describe Account do
describe 'Validations' do
it { is_expected.to validate_presence_of(:username) }
+ it { is_expected.to_not allow_value('').for(:domain) }
+
context 'when account is local' do
subject { Fabricate.build :account, domain: nil }
@@ -822,6 +590,14 @@ RSpec.describe Account do
it { is_expected.to validate_length_of(:display_name).is_at_most(described_class::DISPLAY_NAME_LENGTH_LIMIT) }
it { is_expected.to_not allow_values(account_note_over_limit).for(:note) }
+
+ it { is_expected.to allow_value(fields_empty_name_value).for(:fields) }
+ it { is_expected.to_not allow_values(fields_over_limit, fields_empty_name).for(:fields) }
+
+ it { is_expected.to validate_absence_of(:followers_url).on(:create) }
+ it { is_expected.to validate_absence_of(:inbox_url).on(:create) }
+ it { is_expected.to validate_absence_of(:shared_inbox_url).on(:create) }
+ it { is_expected.to validate_absence_of(:uri).on(:create) }
end
context 'when account is remote' do
@@ -854,6 +630,18 @@ RSpec.describe Account do
def account_note_over_limit
'a' * described_class::NOTE_LENGTH_LIMIT * 2
end
+
+ def fields_empty_name_value
+ Array.new(4) { { 'name' => '', 'value' => '' } }
+ end
+
+ def fields_over_limit
+ Array.new(described_class::DEFAULT_FIELDS_SIZE + 1) { { 'name' => 'Name', 'value' => 'Value', 'verified_at' => '01/01/1970' } }
+ end
+
+ def fields_empty_name
+ [{ 'name' => '', 'value' => 'Value', 'verified_at' => '01/01/1970' }]
+ end
end
describe 'scopes' do
diff --git a/spec/models/account_summary_spec.rb b/spec/models/account_summary_spec.rb
new file mode 100644
index 0000000000..cede8cda55
--- /dev/null
+++ b/spec/models/account_summary_spec.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe AccountSummary do
+ describe 'Scopes' do
+ describe '.localized' do
+ let(:first) { Fabricate :account }
+ let(:last) { Fabricate :account }
+
+ before do
+ Fabricate :status, account: first, language: 'en'
+ Fabricate :status, account: last, language: 'es'
+ described_class.refresh
+ end
+
+ it 'returns records in order of language' do
+ expect(described_class.localized('en'))
+ .to contain_exactly(
+ have_attributes(account_id: first.id, language: 'en'),
+ have_attributes(account_id: last.id, language: 'es')
+ )
+ end
+ end
+ end
+end
diff --git a/spec/models/account_warning_spec.rb b/spec/models/account_warning_spec.rb
index 37866ce3da..9fe2b331eb 100644
--- a/spec/models/account_warning_spec.rb
+++ b/spec/models/account_warning_spec.rb
@@ -8,4 +8,18 @@ RSpec.describe AccountWarning do
it { is_expected.to normalize(:text).from(nil).to('') }
end
end
+
+ describe '#appeal_eligible?' do
+ context 'when created too long ago' do
+ subject { Fabricate.build :account_warning, created_at: (described_class::APPEAL_WINDOW * 2).ago }
+
+ it { is_expected.to_not be_appeal_eligible }
+ end
+
+ context 'when created recently' do
+ subject { Fabricate.build :account_warning, created_at: (described_class::APPEAL_WINDOW - 2.days).ago }
+
+ it { is_expected.to be_appeal_eligible }
+ end
+ end
end
diff --git a/spec/models/appeal_spec.rb b/spec/models/appeal_spec.rb
index e974ff9254..d624e14949 100644
--- a/spec/models/appeal_spec.rb
+++ b/spec/models/appeal_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe Appeal do
it { is_expected.to validate_length_of(:text).is_at_most(described_class::TEXT_LENGTH_LIMIT) }
context 'with a strike created too long ago' do
- let(:strike) { Fabricate.build :account_warning, created_at: 100.days.ago }
+ let(:strike) { Fabricate.build :account_warning, created_at: (AccountWarning::APPEAL_WINDOW * 2).ago }
it { is_expected.to_not allow_values(strike).for(:strike).against(:base).on(:create) }
end
diff --git a/spec/models/doorkeeper/application_spec.rb b/spec/models/doorkeeper/application_spec.rb
new file mode 100644
index 0000000000..e026d90caa
--- /dev/null
+++ b/spec/models/doorkeeper/application_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe Doorkeeper::Application do
+ describe 'Associations' do
+ it { is_expected.to have_many(:created_users).class_name('User').inverse_of(:created_by_application).with_foreign_key(:created_by_application_id) }
+ end
+
+ describe 'Validations' do
+ it { is_expected.to validate_length_of(:name).is_at_most(described_class::APP_NAME_LIMIT) }
+ it { is_expected.to validate_length_of(:redirect_uri).is_at_most(described_class::APP_REDIRECT_URI_LIMIT) }
+ it { is_expected.to validate_length_of(:website).is_at_most(described_class::APP_WEBSITE_LIMIT) }
+ end
+end
diff --git a/spec/models/form/admin_settings_spec.rb b/spec/models/form/admin_settings_spec.rb
index 73106f2b69..899d56703a 100644
--- a/spec/models/form/admin_settings_spec.rb
+++ b/spec/models/form/admin_settings_spec.rb
@@ -17,4 +17,40 @@ RSpec.describe Form::AdminSettings do
end
end
end
+
+ describe '#save' do
+ describe 'updating digest values' do
+ context 'when updating custom css to real value' do
+ subject { described_class.new(custom_css: css) }
+
+ let(:css) { 'body { color: red; }' }
+ let(:digested) { Digest::SHA256.hexdigest(css) }
+
+ it 'changes relevant digest value' do
+ expect { subject.save }
+ .to(change { Rails.cache.read(:setting_digest_custom_css) }.to(digested))
+ end
+ end
+
+ context 'when updating custom css to empty value' do
+ subject { described_class.new(custom_css: '') }
+
+ before { Rails.cache.write(:setting_digest_custom_css, 'previous-value') }
+
+ it 'changes relevant digest value' do
+ expect { subject.save }
+ .to(change { Rails.cache.read(:setting_digest_custom_css) }.to(be_blank))
+ end
+ end
+
+ context 'when updating other fields' do
+ subject { described_class.new(site_contact_email: 'test@example.host') }
+
+ it 'does not update digests' do
+ expect { subject.save }
+ .to(not_change { Rails.cache.read(:setting_digest_custom_css) })
+ end
+ end
+ end
+ end
end
diff --git a/spec/models/invite_spec.rb b/spec/models/invite_spec.rb
index e85885a8d8..6363f77a64 100644
--- a/spec/models/invite_spec.rb
+++ b/spec/models/invite_spec.rb
@@ -5,6 +5,29 @@ require 'rails_helper'
RSpec.describe Invite do
include_examples 'Expireable'
+ describe 'Associations' do
+ it { is_expected.to belong_to(:user).inverse_of(:invites) }
+ it { is_expected.to have_many(:users).inverse_of(:invite) }
+ end
+
+ describe 'Validations' do
+ it { is_expected.to validate_length_of(:comment).is_at_most(described_class::COMMENT_SIZE_LIMIT) }
+ end
+
+ describe 'Scopes' do
+ describe '.available' do
+ let!(:no_expires) { Fabricate :invite, expires_at: nil }
+ let!(:past_expires) { Fabricate :invite, expires_at: 2.days.ago }
+ let!(:future_expires) { Fabricate :invite, expires_at: 2.days.from_now }
+
+ it 'returns future and non-epiring records' do
+ expect(described_class.available)
+ .to include(no_expires, future_expires)
+ .and not_include(past_expires)
+ end
+ end
+ end
+
describe '#valid_for_use?' do
it 'returns true when there are no limitations' do
invite = Fabricate(:invite, max_uses: nil, expires_at: nil)
@@ -37,4 +60,26 @@ RSpec.describe Invite do
expect(invite.valid_for_use?).to be false
end
end
+
+ describe 'Callbacks' do
+ describe 'Setting the invite code' do
+ context 'when creating a new record' do
+ subject { Fabricate.build :invite }
+
+ it 'sets a code value' do
+ expect { subject.save }
+ .to change(subject, :code).from(be_blank).to(be_present)
+ end
+ end
+
+ context 'when updating a record' do
+ subject { Fabricate :invite }
+
+ it 'does not change the code value' do
+ expect { subject.update(max_uses: 123_456) }
+ .to not_change(subject, :code)
+ end
+ end
+ end
+ end
end
diff --git a/spec/models/ip_block_spec.rb b/spec/models/ip_block_spec.rb
index 93ee72423b..856d55be9d 100644
--- a/spec/models/ip_block_spec.rb
+++ b/spec/models/ip_block_spec.rb
@@ -12,6 +12,8 @@ RSpec.describe IpBlock do
it { is_expected.to validate_presence_of(:severity) }
it { is_expected.to validate_uniqueness_of(:ip) }
+
+ it { is_expected.to allow_values(:sign_up_requires_approval, :sign_up_block, :no_access).for(:severity) }
end
describe '#to_log_human_identifier' do
diff --git a/spec/models/software_update_spec.rb b/spec/models/software_update_spec.rb
index 43e9cd058f..8c698e51b3 100644
--- a/spec/models/software_update_spec.rb
+++ b/spec/models/software_update_spec.rb
@@ -3,6 +3,29 @@
require 'rails_helper'
RSpec.describe SoftwareUpdate do
+ describe 'Scopes' do
+ describe '.urgent' do
+ let!(:urgent_update) { Fabricate :software_update, urgent: true }
+ let!(:non_urgent_update) { Fabricate :software_update, urgent: false }
+
+ it 'returns records that are urgent' do
+ expect(described_class.urgent)
+ .to include(urgent_update)
+ .and not_include(non_urgent_update)
+ end
+ end
+ end
+
+ describe '.by_version' do
+ let!(:latest_update) { Fabricate :software_update, version: '4.0.0' }
+ let!(:older_update) { Fabricate :software_update, version: '3.0.0' }
+
+ it 'returns record in gem version order' do
+ expect(described_class.by_version)
+ .to eq([older_update, latest_update])
+ end
+ end
+
describe '#pending?' do
subject { described_class.new(version: update_version) }
diff --git a/spec/models/status_spec.rb b/spec/models/status_spec.rb
index cd0d32b56b..35d5c14372 100644
--- a/spec/models/status_spec.rb
+++ b/spec/models/status_spec.rb
@@ -9,6 +9,8 @@ RSpec.describe Status do
let(:bob) { Fabricate(:account, username: 'bob') }
let(:other) { Fabricate(:status, account: bob, text: 'Skulls for the skull god! The enemy\'s gates are sideways!') }
+ include_examples 'Status::Visibility'
+
describe '#local?' do
it 'returns true when no remote URI is set' do
expect(subject.local?).to be true
@@ -84,36 +86,6 @@ RSpec.describe Status do
end
end
- describe '#hidden?' do
- context 'when private_visibility?' do
- it 'returns true' do
- subject.visibility = :private
- expect(subject.hidden?).to be true
- end
- end
-
- context 'when direct_visibility?' do
- it 'returns true' do
- subject.visibility = :direct
- expect(subject.hidden?).to be true
- end
- end
-
- context 'when public_visibility?' do
- it 'returns false' do
- subject.visibility = :public
- expect(subject.hidden?).to be false
- end
- end
-
- context 'when unlisted_visibility?' do
- it 'returns false' do
- subject.visibility = :unlisted
- expect(subject.hidden?).to be false
- end
- end
- end
-
describe '#content' do
it 'returns the text of the status if it is not a reblog' do
expect(subject.content).to eql subject.text
diff --git a/spec/models/trends/links_spec.rb b/spec/models/trends/links_spec.rb
new file mode 100644
index 0000000000..81a4270c38
--- /dev/null
+++ b/spec/models/trends/links_spec.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe Trends::Links do
+ describe 'Trends::Links::Query' do
+ subject { described_class.new.query }
+
+ describe '#records' do
+ context 'with scored cards' do
+ let!(:higher_score) { Fabricate :preview_card_trend, score: 10, language: 'en' }
+ let!(:lower_score) { Fabricate :preview_card_trend, score: 1, language: 'es' }
+
+ it 'returns higher score first' do
+ expect(subject.records)
+ .to eq([higher_score.preview_card, lower_score.preview_card])
+ end
+
+ context 'with preferred locale' do
+ before { subject.in_locale!('es') }
+
+ it 'returns in language order' do
+ expect(subject.records)
+ .to eq([lower_score.preview_card, higher_score.preview_card])
+ end
+ end
+
+ context 'when account has chosen languages' do
+ let!(:lang_match_higher_score) { Fabricate :preview_card_trend, score: 10, language: 'is' }
+ let!(:lang_match_lower_score) { Fabricate :preview_card_trend, score: 1, language: 'da' }
+ let(:user) { Fabricate :user, chosen_languages: %w(da is) }
+ let(:account) { Fabricate :account, user: user }
+
+ before { subject.filtered_for!(account) }
+
+ it 'returns results' do
+ expect(subject.records)
+ .to eq([lang_match_higher_score.preview_card, lang_match_lower_score.preview_card, higher_score.preview_card, lower_score.preview_card])
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/models/trends/statuses_spec.rb b/spec/models/trends/statuses_spec.rb
index 7c30b5b997..3983901042 100644
--- a/spec/models/trends/statuses_spec.rb
+++ b/spec/models/trends/statuses_spec.rb
@@ -45,6 +45,45 @@ RSpec.describe Trends::Statuses do
end
end
+ describe 'Trends::Statuses::Query methods' do
+ subject { described_class.new.query }
+
+ describe '#records' do
+ context 'with scored cards' do
+ let!(:higher_score) { Fabricate :status_trend, score: 10, language: 'en' }
+ let!(:lower_score) { Fabricate :status_trend, score: 1, language: 'es' }
+
+ it 'returns higher score first' do
+ expect(subject.records)
+ .to eq([higher_score.status, lower_score.status])
+ end
+
+ context 'with preferred locale' do
+ before { subject.in_locale!('es') }
+
+ it 'returns in language order' do
+ expect(subject.records)
+ .to eq([lower_score.status, higher_score.status])
+ end
+ end
+
+ context 'when account has chosen languages' do
+ let!(:lang_match_higher_score) { Fabricate :status_trend, score: 10, language: 'is' }
+ let!(:lang_match_lower_score) { Fabricate :status_trend, score: 1, language: 'da' }
+ let(:user) { Fabricate :user, chosen_languages: %w(da is) }
+ let(:account) { Fabricate :account, user: user }
+
+ before { subject.filtered_for!(account) }
+
+ it 'returns results' do
+ expect(subject.records)
+ .to eq([lang_match_higher_score.status, lang_match_lower_score.status, higher_score.status, lower_score.status])
+ end
+ end
+ end
+ end
+ end
+
describe '#add' do
let(:status) { Fabricate(:status) }
diff --git a/spec/models/trends/tags_spec.rb b/spec/models/trends/tags_spec.rb
index 936b441d92..dacae602ac 100644
--- a/spec/models/trends/tags_spec.rb
+++ b/spec/models/trends/tags_spec.rb
@@ -29,6 +29,45 @@ RSpec.describe Trends::Tags do
end
end
+ describe 'Trends::Tags::Query' do
+ subject { described_class.new.query }
+
+ describe '#records' do
+ context 'with scored cards' do
+ let!(:higher_score) { Fabricate :tag_trend, score: 10, language: 'en' }
+ let!(:lower_score) { Fabricate :tag_trend, score: 1, language: 'es' }
+
+ it 'returns higher score first' do
+ expect(subject.records)
+ .to eq([higher_score.tag, lower_score.tag])
+ end
+
+ context 'with preferred locale' do
+ before { subject.in_locale!('es') }
+
+ it 'returns in language order' do
+ expect(subject.records)
+ .to eq([lower_score.tag, higher_score.tag])
+ end
+ end
+
+ context 'when account has chosen languages' do
+ let!(:lang_match_higher_score) { Fabricate :tag_trend, score: 10, language: 'is' }
+ let!(:lang_match_lower_score) { Fabricate :tag_trend, score: 1, language: 'da' }
+ let(:user) { Fabricate :user, chosen_languages: %w(da is) }
+ let(:account) { Fabricate :account, user: user }
+
+ before { subject.filtered_for!(account) }
+
+ it 'returns results' do
+ expect(subject.records)
+ .to eq([lang_match_higher_score.tag, lang_match_lower_score.tag, higher_score.tag, lower_score.tag])
+ end
+ end
+ end
+ end
+ end
+
describe '#refresh' do
let!(:today) { at_time }
let!(:yesterday) { today - 1.day }
diff --git a/spec/policies/account_warning_policy_spec.rb b/spec/policies/account_warning_policy_spec.rb
index 75142e2071..2603794886 100644
--- a/spec/policies/account_warning_policy_spec.rb
+++ b/spec/policies/account_warning_policy_spec.rb
@@ -31,11 +31,11 @@ RSpec.describe AccountWarningPolicy do
context 'when account is target' do
context 'when record is appealable' do
- it { is_expected.to permit(account, AccountWarning.new(target_account_id: account.id, created_at: Appeal::MAX_STRIKE_AGE.ago + 1.hour)) }
+ it { is_expected.to permit(account, AccountWarning.new(target_account_id: account.id, created_at: AccountWarning::APPEAL_WINDOW.ago + 1.hour)) }
end
context 'when record is not appealable' do
- it { is_expected.to_not permit(account, AccountWarning.new(target_account_id: account.id, created_at: Appeal::MAX_STRIKE_AGE.ago - 1.hour)) }
+ it { is_expected.to_not permit(account, AccountWarning.new(target_account_id: account.id, created_at: AccountWarning::APPEAL_WINDOW.ago - 1.hour)) }
end
end
end
diff --git a/spec/policies/backup_policy_spec.rb b/spec/policies/backup_policy_spec.rb
index 031021d91d..28995d195b 100644
--- a/spec/policies/backup_policy_spec.rb
+++ b/spec/policies/backup_policy_spec.rb
@@ -23,22 +23,30 @@ RSpec.describe BackupPolicy do
context 'when backups are too old' do
it 'permits' do
- travel(-8.days) do
+ travel(-before_time) do
Fabricate(:backup, user: john.user)
end
expect(subject).to permit(john, Backup)
end
+
+ def before_time
+ described_class::MIN_AGE + 2.days
+ end
end
context 'when backups are newer' do
it 'denies' do
- travel(-3.days) do
+ travel(-within_time) do
Fabricate(:backup, user: john.user)
end
expect(subject).to_not permit(john, Backup)
end
+
+ def within_time
+ described_class::MIN_AGE - 2.days
+ end
end
end
end
diff --git a/spec/presenters/instance_presenter_spec.rb b/spec/presenters/instance_presenter_spec.rb
index 9ac8f08b16..484a7a43b2 100644
--- a/spec/presenters/instance_presenter_spec.rb
+++ b/spec/presenters/instance_presenter_spec.rb
@@ -68,6 +68,7 @@ RSpec.describe InstancePresenter do
context 'with the GITHUB_REPOSITORY env variable set' do
around do |example|
ClimateControl.modify GITHUB_REPOSITORY: 'other/repo' do
+ reload_configuration
example.run
end
end
@@ -80,6 +81,7 @@ RSpec.describe InstancePresenter do
context 'without the GITHUB_REPOSITORY env variable set' do
around do |example|
ClimateControl.modify GITHUB_REPOSITORY: nil do
+ reload_configuration
example.run
end
end
@@ -88,6 +90,10 @@ RSpec.describe InstancePresenter do
expect(instance_presenter.source_url).to eq('https://github.com/glitch-soc/mastodon')
end
end
+
+ def reload_configuration
+ Rails.configuration.x.mastodon = Rails.application.config_for(:mastodon)
+ end
end
describe '#thumbnail' do
diff --git a/spec/requests/accounts_spec.rb b/spec/requests/accounts_spec.rb
index afd9ac80e2..72913ebf22 100644
--- a/spec/requests/accounts_spec.rb
+++ b/spec/requests/accounts_spec.rb
@@ -53,8 +53,9 @@ RSpec.describe 'Accounts show response' do
it 'returns a standard HTML response', :aggregate_failures do
expect(response)
.to have_http_status(200)
- .and render_template(:show)
.and have_http_link_header(ActivityPub::TagManager.instance.uri_for(account)).for(rel: 'alternate')
+ expect(response.parsed_body.at('title').content)
+ .to include(account.username)
end
end
diff --git a/spec/requests/api/v1/accounts/credentials_spec.rb b/spec/requests/api/v1/accounts/credentials_spec.rb
index 966d1f598e..0badc17e1b 100644
--- a/spec/requests/api/v1/accounts/credentials_spec.rb
+++ b/spec/requests/api/v1/accounts/credentials_spec.rb
@@ -85,7 +85,7 @@ RSpec.describe 'credentials API' do
end
describe 'with invalid data' do
- let(:params) { { note: "This is too long. #{'a' * Account::NOTE_LENGTH_LIMIT}" } }
+ let(:params) { { note: 'a' * 2 * Account::NOTE_LENGTH_LIMIT } }
it 'returns http unprocessable entity' do
subject
diff --git a/spec/requests/api/v1/accounts/notes_spec.rb b/spec/requests/api/v1/accounts/notes_spec.rb
index 1677ec07e3..e616df1e6f 100644
--- a/spec/requests/api/v1/accounts/notes_spec.rb
+++ b/spec/requests/api/v1/accounts/notes_spec.rb
@@ -29,7 +29,7 @@ RSpec.describe 'Accounts Notes API' do
end
context 'when account note exceeds allowed length', :aggregate_failures do
- let(:comment) { 'a' * 2_001 }
+ let(:comment) { 'a' * AccountNote::COMMENT_SIZE_LIMIT * 2 }
it 'does not create account note' do
subject
diff --git a/spec/requests/api/v1/admin/domain_blocks_spec.rb b/spec/requests/api/v1/admin/domain_blocks_spec.rb
index 029de72fd7..0b01d04f9a 100644
--- a/spec/requests/api/v1/admin/domain_blocks_spec.rb
+++ b/spec/requests/api/v1/admin/domain_blocks_spec.rb
@@ -217,6 +217,19 @@ RSpec.describe 'Domain Blocks' do
.to start_with('application/json')
end
end
+
+ context 'when severity is invalid' do
+ let(:params) { { domain: 'bar.com', severity: :bar } }
+
+ it 'returns http unprocessable entity' do
+ subject
+
+ expect(response).to have_http_status(422)
+ expect(response.content_type)
+ .to start_with('application/json')
+ expect(response.parsed_body[:error]).to eq('Validation failed: Severity is not included in the list')
+ end
+ end
end
describe 'PUT /api/v1/admin/domain_blocks/:id' do
diff --git a/spec/requests/api/v1/admin/ip_blocks_spec.rb b/spec/requests/api/v1/admin/ip_blocks_spec.rb
index aa3db33915..59ef8d2966 100644
--- a/spec/requests/api/v1/admin/ip_blocks_spec.rb
+++ b/spec/requests/api/v1/admin/ip_blocks_spec.rb
@@ -187,6 +187,16 @@ RSpec.describe 'IP Blocks' do
.to start_with('application/json')
end
end
+
+ context 'when the given severity is invalid' do
+ let(:params) { { ip: '151.0.32.55', severity: 'invalid' } }
+
+ it 'returns http unprocessable entity' do
+ subject
+
+ expect(response).to have_http_status(422)
+ end
+ end
end
describe 'PUT /api/v1/admin/ip_blocks/:id' do
diff --git a/spec/requests/api/v1/apps_spec.rb b/spec/requests/api/v1/apps_spec.rb
index 4e9147ba32..3120ab9c64 100644
--- a/spec/requests/api/v1/apps_spec.rb
+++ b/spec/requests/api/v1/apps_spec.rb
@@ -122,7 +122,7 @@ RSpec.describe 'Apps' do
end
context 'with a too-long name' do
- let(:client_name) { 'hoge' * 20 }
+ let(:client_name) { 'a' * Doorkeeper::Application::APP_NAME_LIMIT * 2 }
it 'returns http unprocessable entity' do
subject
@@ -134,7 +134,7 @@ RSpec.describe 'Apps' do
end
context 'with a too-long website' do
- let(:website) { "https://foo.bar/#{'hoge' * 2_000}" }
+ let(:website) { "https://foo.bar/#{'a' * Doorkeeper::Application::APP_WEBSITE_LIMIT * 2}" }
it 'returns http unprocessable entity' do
subject
@@ -146,7 +146,7 @@ RSpec.describe 'Apps' do
end
context 'with a too-long redirect_uri' do
- let(:redirect_uris) { "https://app.example/#{'hoge' * 2_000}" }
+ let(:redirect_uris) { "https://app.example/#{'a' * Doorkeeper::Application::APP_REDIRECT_URI_LIMIT * 2}" }
it 'returns http unprocessable entity' do
subject
diff --git a/spec/requests/api/v1/instances/languages_spec.rb b/spec/requests/api/v1/instances/languages_spec.rb
index 3d188d23c0..0b325bbb6d 100644
--- a/spec/requests/api/v1/instances/languages_spec.rb
+++ b/spec/requests/api/v1/instances/languages_spec.rb
@@ -9,10 +9,21 @@ RSpec.describe 'Languages' do
end
it 'returns http success and includes supported languages' do
- expect(response).to have_http_status(200)
+ expect(response)
+ .to have_http_status(200)
expect(response.content_type)
.to start_with('application/json')
- expect(response.parsed_body.pluck(:code)).to match_array LanguagesHelper::SUPPORTED_LOCALES.keys.map(&:to_s)
+ expect(response.parsed_body)
+ .to match_array(supported_locale_expectations)
+ end
+
+ def supported_locale_expectations
+ LanguagesHelper::SUPPORTED_LOCALES.map do |key, values|
+ include(
+ code: key.to_s,
+ name: values.first
+ )
+ end
end
end
end
diff --git a/spec/requests/api/v1/polls_spec.rb b/spec/requests/api/v1/polls_spec.rb
index fd38297931..c93231e1ee 100644
--- a/spec/requests/api/v1/polls_spec.rb
+++ b/spec/requests/api/v1/polls_spec.rb
@@ -36,6 +36,31 @@ RSpec.describe 'Polls' do
end
end
+ context 'when poll is remote and needs refresh' do
+ let(:poll) { Fabricate(:poll, last_fetched_at: nil, account: remote_account, status: status) }
+ let(:remote_account) { Fabricate :account, domain: 'host.example' }
+ let(:service) { instance_double(ActivityPub::FetchRemotePollService, call: nil) }
+ let(:status) { Fabricate(:status, visibility: 'public', account: remote_account) }
+
+ before { allow(ActivityPub::FetchRemotePollService).to receive(:new).and_return(service) }
+
+ it 'returns poll data and calls fetch remote service' do
+ subject
+
+ expect(response)
+ .to have_http_status(200)
+ expect(response.content_type)
+ .to start_with('application/json')
+ expect(response.parsed_body).to match(
+ a_hash_including(
+ id: poll.id.to_s
+ )
+ )
+ expect(service)
+ .to have_received(:call).with(poll, user.account)
+ end
+ end
+
context 'when parent status is private' do
let(:visibility) { 'private' }
diff --git a/spec/requests/api/v2/media_spec.rb b/spec/requests/api/v2/media_spec.rb
index 807e427d3f..18ebb9cdda 100644
--- a/spec/requests/api/v2/media_spec.rb
+++ b/spec/requests/api/v2/media_spec.rb
@@ -33,7 +33,7 @@ RSpec.describe 'Media API', :attachment_processing do
let(:params) do
{
file: fixture_file_upload('attachment-jpg.123456_abcd', 'image/jpeg'),
- description: 'aa' * MediaAttachment::MAX_DESCRIPTION_LENGTH,
+ description: 'a' * MediaAttachment::MAX_DESCRIPTION_LENGTH * 2,
}
end
diff --git a/spec/requests/api/v2/notifications_spec.rb b/spec/requests/api/v2/notifications_spec.rb
index ffa0a71c77..aa4a861557 100644
--- a/spec/requests/api/v2/notifications_spec.rb
+++ b/spec/requests/api/v2/notifications_spec.rb
@@ -143,6 +143,55 @@ RSpec.describe 'Notifications' do
end
end
+ context 'when there are numerous notifications for the same final group' do
+ before do
+ user.account.notifications.destroy_all
+ 5.times.each { FavouriteService.new.call(Fabricate(:account), user.account.statuses.first) }
+ end
+
+ context 'with no options' do
+ it 'returns a notification group covering all notifications' do
+ subject
+
+ notification_ids = user.account.notifications.reload.pluck(:id)
+
+ expect(response).to have_http_status(200)
+ expect(response.content_type)
+ .to start_with('application/json')
+ expect(response.parsed_body[:notification_groups]).to contain_exactly(
+ a_hash_including(
+ type: 'favourite',
+ sample_account_ids: have_attributes(size: 5),
+ page_min_id: notification_ids.first.to_s,
+ page_max_id: notification_ids.last.to_s
+ )
+ )
+ end
+ end
+
+ context 'with min_id param' do
+ let(:params) { { min_id: user.account.notifications.reload.first.id - 1 } }
+
+ it 'returns a notification group covering all notifications' do
+ subject
+
+ notification_ids = user.account.notifications.reload.pluck(:id)
+
+ expect(response).to have_http_status(200)
+ expect(response.content_type)
+ .to start_with('application/json')
+ expect(response.parsed_body[:notification_groups]).to contain_exactly(
+ a_hash_including(
+ type: 'favourite',
+ sample_account_ids: have_attributes(size: 5),
+ page_min_id: notification_ids.first.to_s,
+ page_max_id: notification_ids.last.to_s
+ )
+ )
+ end
+ end
+ end
+
context 'with no options' do
it 'returns expected notification types', :aggregate_failures do
subject
diff --git a/spec/controllers/auth/challenges_controller_spec.rb b/spec/requests/auth/challenges_spec.rb
similarity index 64%
rename from spec/controllers/auth/challenges_controller_spec.rb
rename to spec/requests/auth/challenges_spec.rb
index 3c9d2a5964..628bfe499b 100644
--- a/spec/controllers/auth/challenges_controller_spec.rb
+++ b/spec/requests/auth/challenges_spec.rb
@@ -2,9 +2,7 @@
require 'rails_helper'
-RSpec.describe Auth::ChallengesController do
- render_views
-
+RSpec.describe 'Auth Challenges' do
let(:password) { 'foobar12345' }
let(:user) { Fabricate(:user, password: password) }
@@ -14,9 +12,9 @@ RSpec.describe Auth::ChallengesController do
let(:return_to) { edit_user_registration_path }
context 'with correct password' do
- before { post :create, params: { form_challenge: { return_to: return_to, current_password: password } } }
-
it 'redirects back and sets challenge passed at in session' do
+ post '/auth/challenge', params: { form_challenge: { return_to: return_to, current_password: password } }
+
expect(response)
.to redirect_to(return_to)
expect(session[:challenge_passed_at])
@@ -25,13 +23,12 @@ RSpec.describe Auth::ChallengesController do
end
context 'with incorrect password' do
- before { post :create, params: { form_challenge: { return_to: return_to, current_password: 'hhfggjjd562' } } }
-
it 'renders challenge, displays error, does not set session' do
- expect(response)
- .to render_template('auth/challenges/new')
+ post '/auth/challenge', params: { form_challenge: { return_to: return_to, current_password: 'hhfggjjd562' } }
+
expect(response.body)
- .to include 'Invalid password'
+ .to include(I18n.t('challenge.prompt'))
+ .and include('Invalid password')
expect(session[:challenge_passed_at])
.to be_nil
end
diff --git a/spec/requests/auth/setup_spec.rb b/spec/requests/auth/setup_spec.rb
new file mode 100644
index 0000000000..fa3c196805
--- /dev/null
+++ b/spec/requests/auth/setup_spec.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe 'Auth Setup' do
+ describe 'GET /auth/setup' do
+ context 'with a signed out request' do
+ it 'redirects to root' do
+ get '/auth/setup'
+
+ expect(response)
+ .to redirect_to(new_user_session_url)
+ end
+ end
+
+ context 'with a confirmed signed in user' do
+ before { sign_in Fabricate(:user, confirmed_at: 2.days.ago) }
+
+ it 'redirects to root' do
+ get '/auth/setup'
+
+ expect(response)
+ .to redirect_to(root_url)
+ end
+ end
+ end
+end
diff --git a/spec/requests/cache_spec.rb b/spec/requests/cache_spec.rb
index 2a52e4dea9..8ca2817263 100644
--- a/spec/requests/cache_spec.rb
+++ b/spec/requests/cache_spec.rb
@@ -10,6 +10,7 @@ module TestEndpoints
/.well-known/nodeinfo
/nodeinfo/2.0
/manifest
+ /css/custom-1a2s3d4f.css
/custom.css
/actor
/api/v1/instance/extended_description
diff --git a/spec/requests/custom_css_spec.rb b/spec/requests/custom_css_spec.rb
index d97da00187..66ff5c4b13 100644
--- a/spec/requests/custom_css_spec.rb
+++ b/spec/requests/custom_css_spec.rb
@@ -5,10 +5,10 @@ require 'rails_helper'
RSpec.describe 'Custom CSS' do
include RoutingHelper
- describe 'GET /custom.css' do
+ describe 'GET /css/:id.css' do
context 'without any CSS or User Roles' do
it 'returns empty stylesheet' do
- get '/custom.css'
+ get '/css/custom-123.css'
expect(response)
.to have_http_status(200)
@@ -27,7 +27,7 @@ RSpec.describe 'Custom CSS' do
end
it 'returns stylesheet from settings' do
- get '/custom.css'
+ get '/css/custom-456.css'
expect(response)
.to have_http_status(200)
@@ -45,34 +45,5 @@ RSpec.describe 'Custom CSS' do
CSS
end
end
-
- context 'with highlighted colored UserRole records' do
- before do
- _highlighted_colored = Fabricate :user_role, highlighted: true, color: '#336699', id: '123_123_123'
- _highlighted_no_color = Fabricate :user_role, highlighted: true, color: ''
- _no_highlight_with_color = Fabricate :user_role, highlighted: false, color: ''
- end
-
- it 'returns stylesheet from settings' do
- get '/custom.css'
-
- expect(response)
- .to have_http_status(200)
- .and have_cacheable_headers
- .and have_attributes(
- content_type: match('text/css')
- )
- expect(response.body.strip)
- .to eq(expected_css)
- end
-
- def expected_css
- <<~CSS.strip
- .user-role-123123123 {
- --user-role-accent: #336699;
- }
- CSS
- end
- end
end
end
diff --git a/spec/requests/remote_interaction_helper_spec.rb b/spec/requests/remote_interaction_helper_spec.rb
index 942f70b9a4..b89060b5b2 100644
--- a/spec/requests/remote_interaction_helper_spec.rb
+++ b/spec/requests/remote_interaction_helper_spec.rb
@@ -9,7 +9,6 @@ RSpec.describe 'Remote Interaction Helper' do
expect(response)
.to have_http_status(200)
- .and render_template(:index, layout: 'helper_frame')
.and have_attributes(
headers: include(
'X-Frame-Options' => 'SAMEORIGIN',
@@ -17,6 +16,8 @@ RSpec.describe 'Remote Interaction Helper' do
'Content-Security-Policy' => expected_csp_headers
)
)
+ expect(response.body)
+ .to match(/remote_interaction_helper/)
end
end
diff --git a/spec/requests/settings/migrations_spec.rb b/spec/requests/settings/migrations_spec.rb
new file mode 100644
index 0000000000..4103d6b320
--- /dev/null
+++ b/spec/requests/settings/migrations_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe 'Settings Migrations' do
+ describe 'GET #show' do
+ context 'when user is not signed in' do
+ subject { get '/settings/migration' }
+
+ it { is_expected.to redirect_to new_user_session_path }
+ end
+ end
+
+ describe 'POST #create' do
+ context 'when user is not signed in' do
+ subject { post '/settings/migration' }
+
+ it { is_expected.to redirect_to new_user_session_path }
+ end
+ end
+end
diff --git a/spec/routing/custom_css_routing_spec.rb b/spec/routing/custom_css_routing_spec.rb
new file mode 100644
index 0000000000..26139b4744
--- /dev/null
+++ b/spec/routing/custom_css_routing_spec.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe 'Custom CSS routes' do
+ describe 'the legacy route' do
+ it 'routes to correct place' do
+ expect(get('/custom.css'))
+ .to route_to('custom_css#show')
+ end
+ end
+
+ describe 'the custom digest route' do
+ it 'routes to correct place' do
+ expect(get('/css/custom-1a2s3d4f.css'))
+ .to route_to('custom_css#show', id: 'custom-1a2s3d4f', format: 'css')
+ end
+ end
+end
diff --git a/spec/serializers/rest/preview_card_serializer_spec.rb b/spec/serializers/rest/preview_card_serializer_spec.rb
index 6dbc337865..41ba305b7c 100644
--- a/spec/serializers/rest/preview_card_serializer_spec.rb
+++ b/spec/serializers/rest/preview_card_serializer_spec.rb
@@ -21,7 +21,24 @@ RSpec.describe REST::PreviewCardSerializer do
end
end
- context 'when preview card has author data' do
+ context 'when preview card has fediverse author data' do
+ let(:preview_card) { Fabricate.build :preview_card, author_account: Fabricate(:account) }
+
+ it 'includes populated authors array' do
+ expect(subject.deep_symbolize_keys)
+ .to include(
+ authors: be_an(Array).and(
+ contain_exactly(
+ include(
+ account: be_present
+ )
+ )
+ )
+ )
+ end
+ end
+
+ context 'when preview card has non-fediverse author data' do
let(:preview_card) { Fabricate.build :preview_card, author_name: 'Name', author_url: 'https://host.example/123' }
it 'includes populated authors array' do
diff --git a/spec/serializers/rest/scheduled_status_serializer_spec.rb b/spec/serializers/rest/scheduled_status_serializer_spec.rb
index 08ad8a6f8a..2cf0098654 100644
--- a/spec/serializers/rest/scheduled_status_serializer_spec.rb
+++ b/spec/serializers/rest/scheduled_status_serializer_spec.rb
@@ -17,7 +17,7 @@ RSpec.describe REST::ScheduledStatusSerializer do
expect(subject.deep_symbolize_keys)
.to include(
scheduled_at: be_a(String).and(match_api_datetime_format),
- params: not_include(:application_id)
+ params: include(:application_id)
)
end
end
diff --git a/spec/services/import_service_spec.rb b/spec/services/import_service_spec.rb
index 0a99c5e748..2e1358c635 100644
--- a/spec/services/import_service_spec.rb
+++ b/spec/services/import_service_spec.rb
@@ -204,7 +204,7 @@ RSpec.describe ImportService, :inline_jobs do
subject { described_class.new }
let(:csv) { attachment_fixture('bookmark-imports.txt') }
- let(:local_account) { Fabricate(:account, username: 'foo', domain: '') }
+ let(:local_account) { Fabricate(:account, username: 'foo', domain: nil) }
let!(:remote_status) { Fabricate(:status, uri: 'https://example.com/statuses/1312') }
let!(:direct_status) { Fabricate(:status, uri: 'https://example.com/statuses/direct', visibility: :direct) }
diff --git a/spec/support/examples/models/concerns/account/search.rb b/spec/support/examples/models/concerns/account/search.rb
new file mode 100644
index 0000000000..9d9b499732
--- /dev/null
+++ b/spec/support/examples/models/concerns/account/search.rb
@@ -0,0 +1,268 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'Account::Search' do
+ describe '.search_for' do
+ before do
+ _missing = Fabricate(
+ :account,
+ display_name: 'Missing',
+ username: 'missing',
+ domain: 'missing.com'
+ )
+ end
+
+ it 'does not return suspended users' do
+ Fabricate(
+ :account,
+ display_name: 'Display Name',
+ username: 'username',
+ domain: 'example.com',
+ suspended: true
+ )
+
+ results = described_class.search_for('username')
+ expect(results).to eq []
+ end
+
+ it 'does not return unapproved users' do
+ match = Fabricate(
+ :account,
+ display_name: 'Display Name',
+ username: 'username'
+ )
+
+ match.user.update(approved: false)
+
+ results = described_class.search_for('username')
+ expect(results).to eq []
+ end
+
+ it 'does not return unconfirmed users' do
+ match = Fabricate(
+ :account,
+ display_name: 'Display Name',
+ username: 'username'
+ )
+
+ match.user.update(confirmed_at: nil)
+
+ results = described_class.search_for('username')
+ expect(results).to eq []
+ end
+
+ it 'accepts ?, \, : and space as delimiter' do
+ match = Fabricate(
+ :account,
+ display_name: 'A & l & i & c & e',
+ username: 'username',
+ domain: 'example.com'
+ )
+
+ results = described_class.search_for('A?l\i:c e')
+ expect(results).to eq [match]
+ end
+
+ it 'finds accounts with matching display_name' do
+ match = Fabricate(
+ :account,
+ display_name: 'Display Name',
+ username: 'username',
+ domain: 'example.com'
+ )
+
+ results = described_class.search_for('display')
+ expect(results).to eq [match]
+ end
+
+ it 'finds accounts with matching username' do
+ match = Fabricate(
+ :account,
+ display_name: 'Display Name',
+ username: 'username',
+ domain: 'example.com'
+ )
+
+ results = described_class.search_for('username')
+ expect(results).to eq [match]
+ end
+
+ it 'finds accounts with matching domain' do
+ match = Fabricate(
+ :account,
+ display_name: 'Display Name',
+ username: 'username',
+ domain: 'example.com'
+ )
+
+ results = described_class.search_for('example')
+ expect(results).to eq [match]
+ end
+
+ it 'limits via constant by default' do
+ stub_const('Account::Search::DEFAULT_LIMIT', 1)
+ 2.times.each { Fabricate(:account, display_name: 'Display Name') }
+ results = described_class.search_for('display')
+ expect(results.size).to eq 1
+ end
+
+ it 'accepts arbitrary limits' do
+ 2.times.each { Fabricate(:account, display_name: 'Display Name') }
+ results = described_class.search_for('display', limit: 1)
+ expect(results.size).to eq 1
+ end
+
+ it 'ranks multiple matches higher' do
+ matches = [
+ { username: 'username', display_name: 'username' },
+ { display_name: 'Display Name', username: 'username', domain: 'example.com' },
+ ].map(&method(:Fabricate).curry(2).call(:account))
+
+ results = described_class.search_for('username')
+ expect(results).to eq matches
+ end
+ end
+
+ describe '.advanced_search_for' do
+ let(:account) { Fabricate(:account) }
+
+ context 'when limiting search to followed accounts' do
+ it 'accepts ?, \, : and space as delimiter' do
+ match = Fabricate(
+ :account,
+ display_name: 'A & l & i & c & e',
+ username: 'username',
+ domain: 'example.com'
+ )
+ account.follow!(match)
+
+ results = described_class.advanced_search_for('A?l\i:c e', account, limit: 10, following: true)
+ expect(results).to eq [match]
+ end
+
+ it 'does not return non-followed accounts' do
+ Fabricate(
+ :account,
+ display_name: 'A & l & i & c & e',
+ username: 'username',
+ domain: 'example.com'
+ )
+
+ results = described_class.advanced_search_for('A?l\i:c e', account, limit: 10, following: true)
+ expect(results).to eq []
+ end
+
+ it 'does not return suspended users' do
+ Fabricate(
+ :account,
+ display_name: 'Display Name',
+ username: 'username',
+ domain: 'example.com',
+ suspended: true
+ )
+
+ results = described_class.advanced_search_for('username', account, limit: 10, following: true)
+ expect(results).to eq []
+ end
+
+ it 'does not return unapproved users' do
+ match = Fabricate(
+ :account,
+ display_name: 'Display Name',
+ username: 'username'
+ )
+
+ match.user.update(approved: false)
+
+ results = described_class.advanced_search_for('username', account, limit: 10, following: true)
+ expect(results).to eq []
+ end
+
+ it 'does not return unconfirmed users' do
+ match = Fabricate(
+ :account,
+ display_name: 'Display Name',
+ username: 'username'
+ )
+
+ match.user.update(confirmed_at: nil)
+
+ results = described_class.advanced_search_for('username', account, limit: 10, following: true)
+ expect(results).to eq []
+ end
+ end
+
+ it 'does not return suspended users' do
+ Fabricate(
+ :account,
+ display_name: 'Display Name',
+ username: 'username',
+ domain: 'example.com',
+ suspended: true
+ )
+
+ results = described_class.advanced_search_for('username', account)
+ expect(results).to eq []
+ end
+
+ it 'does not return unapproved users' do
+ match = Fabricate(
+ :account,
+ display_name: 'Display Name',
+ username: 'username'
+ )
+
+ match.user.update(approved: false)
+
+ results = described_class.advanced_search_for('username', account)
+ expect(results).to eq []
+ end
+
+ it 'does not return unconfirmed users' do
+ match = Fabricate(
+ :account,
+ display_name: 'Display Name',
+ username: 'username'
+ )
+
+ match.user.update(confirmed_at: nil)
+
+ results = described_class.advanced_search_for('username', account)
+ expect(results).to eq []
+ end
+
+ it 'accepts ?, \, : and space as delimiter' do
+ match = Fabricate(
+ :account,
+ display_name: 'A & l & i & c & e',
+ username: 'username',
+ domain: 'example.com'
+ )
+
+ results = described_class.advanced_search_for('A?l\i:c e', account)
+ expect(results).to eq [match]
+ end
+
+ it 'limits result count by default value' do
+ stub_const('Account::Search::DEFAULT_LIMIT', 1)
+ 2.times { Fabricate(:account, display_name: 'Display Name') }
+ results = described_class.advanced_search_for('display', account)
+ expect(results.size).to eq 1
+ end
+
+ it 'accepts arbitrary limits' do
+ 2.times { Fabricate(:account, display_name: 'Display Name') }
+ results = described_class.advanced_search_for('display', account, limit: 1)
+ expect(results.size).to eq 1
+ end
+
+ it 'ranks followed accounts higher' do
+ match = Fabricate(:account, username: 'Matching')
+ followed_match = Fabricate(:account, username: 'Matcher')
+ Fabricate(:follow, account: account, target_account: followed_match)
+
+ results = described_class.advanced_search_for('match', account)
+ expect(results).to eq [followed_match, match]
+ expect(results.first.rank).to be > results.last.rank
+ end
+ end
+end
diff --git a/spec/support/examples/models/concerns/status/visibility.rb b/spec/support/examples/models/concerns/status/visibility.rb
new file mode 100644
index 0000000000..dd9e0bddf0
--- /dev/null
+++ b/spec/support/examples/models/concerns/status/visibility.rb
@@ -0,0 +1,184 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.shared_examples 'Status::Visibility' do
+ describe 'Validations' do
+ context 'when status is a reblog' do
+ subject { Fabricate.build :status, reblog: Fabricate(:status) }
+
+ it { is_expected.to allow_values('public', 'unlisted', 'private').for(:visibility) }
+ it { is_expected.to_not allow_values('direct', 'limited').for(:visibility) }
+ end
+
+ context 'when status is not reblog' do
+ subject { Fabricate.build :status, reblog_of_id: nil }
+
+ it { is_expected.to allow_values('public', 'unlisted', 'private', 'direct', 'limited').for(:visibility) }
+ end
+ end
+
+ describe 'Scopes' do
+ let!(:direct_status) { Fabricate :status, visibility: :direct }
+ let!(:limited_status) { Fabricate :status, visibility: :limited }
+ let!(:private_status) { Fabricate :status, visibility: :private }
+ let!(:public_status) { Fabricate :status, visibility: :public }
+ let!(:unlisted_status) { Fabricate :status, visibility: :unlisted }
+
+ describe '.list_eligible_visibility' do
+ it 'returns appropriate records' do
+ expect(Status.list_eligible_visibility)
+ .to include(
+ private_status,
+ public_status,
+ unlisted_status
+ )
+ .and not_include(direct_status)
+ .and not_include(limited_status)
+ end
+ end
+
+ describe '.distributable_visibility' do
+ it 'returns appropriate records' do
+ expect(Status.distributable_visibility)
+ .to include(
+ public_status,
+ unlisted_status
+ )
+ .and not_include(private_status)
+ .and not_include(direct_status)
+ .and not_include(limited_status)
+ end
+ end
+
+ describe '.not_direct_visibility' do
+ it 'returns appropriate records' do
+ expect(Status.not_direct_visibility)
+ .to include(
+ limited_status,
+ private_status,
+ public_status,
+ unlisted_status
+ )
+ .and not_include(direct_status)
+ end
+ end
+ end
+
+ describe 'Callbacks' do
+ describe 'Setting visibility in before validation' do
+ subject { Fabricate.build :status, visibility: nil }
+
+ context 'when explicit value is set' do
+ before { subject.visibility = :public }
+
+ it 'does not change' do
+ expect { subject.valid? }
+ .to_not change(subject, :visibility)
+ end
+ end
+
+ context 'when status is a reblog' do
+ before { subject.reblog = Fabricate(:status, visibility: :public) }
+
+ it 'changes to match the reblog' do
+ expect { subject.valid? }
+ .to change(subject, :visibility).to('public')
+ end
+ end
+
+ context 'when account is locked' do
+ before { subject.account = Fabricate.build(:account, locked: true) }
+
+ it 'changes to private' do
+ expect { subject.valid? }
+ .to change(subject, :visibility).to('private')
+ end
+ end
+
+ context 'when account is not locked' do
+ before { subject.account = Fabricate.build(:account, locked: false) }
+
+ it 'changes to public' do
+ expect { subject.valid? }
+ .to change(subject, :visibility).to('public')
+ end
+ end
+ end
+ end
+
+ describe '.selectable_visibilities' do
+ it 'returns options available for default privacy selection' do
+ expect(Status.selectable_visibilities)
+ .to match(%w(public unlisted private))
+ end
+ end
+
+ describe '#hidden?' do
+ subject { Status.new }
+
+ context 'when visibility is private' do
+ before { subject.visibility = :private }
+
+ it { is_expected.to be_hidden }
+ end
+
+ context 'when visibility is direct' do
+ before { subject.visibility = :direct }
+
+ it { is_expected.to be_hidden }
+ end
+
+ context 'when visibility is limited' do
+ before { subject.visibility = :limited }
+
+ it { is_expected.to be_hidden }
+ end
+
+ context 'when visibility is public' do
+ before { subject.visibility = :public }
+
+ it { is_expected.to_not be_hidden }
+ end
+
+ context 'when visibility is unlisted' do
+ before { subject.visibility = :unlisted }
+
+ it { is_expected.to_not be_hidden }
+ end
+ end
+
+ describe '#distributable?' do
+ subject { Status.new }
+
+ context 'when visibility is public' do
+ before { subject.visibility = :public }
+
+ it { is_expected.to be_distributable }
+ end
+
+ context 'when visibility is unlisted' do
+ before { subject.visibility = :unlisted }
+
+ it { is_expected.to be_distributable }
+ end
+
+ context 'when visibility is private' do
+ before { subject.visibility = :private }
+
+ it { is_expected.to_not be_distributable }
+ end
+
+ context 'when visibility is direct' do
+ before { subject.visibility = :direct }
+
+ it { is_expected.to_not be_distributable }
+ end
+
+ context 'when visibility is limited' do
+ before { subject.visibility = :limited }
+
+ it { is_expected.to_not be_distributable }
+ end
+ end
+end
diff --git a/spec/system/admin/email_domain_blocks_spec.rb b/spec/system/admin/email_domain_blocks_spec.rb
index acf5027eda..807cfb3768 100644
--- a/spec/system/admin/email_domain_blocks_spec.rb
+++ b/spec/system/admin/email_domain_blocks_spec.rb
@@ -5,9 +5,7 @@ require 'rails_helper'
RSpec.describe 'Admin::EmailDomainBlocks' do
let(:current_user) { Fabricate(:admin_user) }
- before do
- sign_in current_user
- end
+ before { sign_in current_user }
describe 'Performing batch updates' do
before do
@@ -22,6 +20,27 @@ RSpec.describe 'Admin::EmailDomainBlocks' do
end
end
+ context 'with a selected block' do
+ let!(:email_domain_block) { Fabricate :email_domain_block }
+
+ it 'deletes the block' do
+ visit admin_email_domain_blocks_path
+
+ check_item
+
+ expect { click_on button_for_delete }
+ .to change(EmailDomainBlock, :count).by(-1)
+ expect { email_domain_block.reload }
+ .to raise_error(ActiveRecord::RecordNotFound)
+ end
+ end
+
+ def check_item
+ within '.batch-table__row' do
+ find('input[type=checkbox]').check
+ end
+ end
+
def button_for_delete
I18n.t('admin.email_domain_blocks.delete')
end
diff --git a/spec/system/admin/follow_recommendations_spec.rb b/spec/system/admin/follow_recommendations_spec.rb
new file mode 100644
index 0000000000..141a0f8152
--- /dev/null
+++ b/spec/system/admin/follow_recommendations_spec.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe 'Admin Follow Recommendations' do
+ let(:user) { Fabricate(:admin_user) }
+
+ before { sign_in(user) }
+
+ describe 'Viewing follow recommendations details' do
+ it 'shows a list of accounts' do
+ visit admin_follow_recommendations_path
+
+ expect(page)
+ .to have_content(I18n.t('admin.follow_recommendations.title'))
+ end
+ end
+end
diff --git a/spec/system/admin/ip_blocks_spec.rb b/spec/system/admin/ip_blocks_spec.rb
index 8e8c8031c8..3bed506b68 100644
--- a/spec/system/admin/ip_blocks_spec.rb
+++ b/spec/system/admin/ip_blocks_spec.rb
@@ -48,6 +48,27 @@ RSpec.describe 'Admin::IpBlocks' do
end
end
+ context 'with a selected block' do
+ let!(:ip_block) { Fabricate :ip_block }
+
+ it 'deletes the block' do
+ visit admin_ip_blocks_path
+
+ check_item
+
+ expect { click_on button_for_delete }
+ .to change(IpBlock, :count).by(-1)
+ expect { ip_block.reload }
+ .to raise_error(ActiveRecord::RecordNotFound)
+ end
+ end
+
+ def check_item
+ within '.batch-table__row' do
+ find('input[type=checkbox]').check
+ end
+ end
+
def button_for_delete
I18n.t('admin.ip_blocks.delete')
end
diff --git a/spec/system/admin/terms_of_service/histories_spec.rb b/spec/system/admin/terms_of_service/histories_spec.rb
new file mode 100644
index 0000000000..aa59550d09
--- /dev/null
+++ b/spec/system/admin/terms_of_service/histories_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe 'Admin Terms of Service Histories' do
+ let(:current_user) { Fabricate(:admin_user) }
+
+ before { sign_in(current_user) }
+
+ describe 'Viewing TOS histories' do
+ before { Fabricate :terms_of_service, changelog: 'The changelog notes from v1 are here' }
+
+ it 'shows previous terms versions' do
+ visit admin_terms_of_service_history_path
+
+ expect(page)
+ .to have_content(I18n.t('admin.terms_of_service.history'))
+ .and have_content(/changelog notes from v1/)
+ end
+ end
+end
diff --git a/spec/system/admin/trends/links/preview_card_providers_spec.rb b/spec/system/admin/trends/links/preview_card_providers_spec.rb
index 0a5b5a7581..159a5b720a 100644
--- a/spec/system/admin/trends/links/preview_card_providers_spec.rb
+++ b/spec/system/admin/trends/links/preview_card_providers_spec.rb
@@ -5,20 +5,49 @@ require 'rails_helper'
RSpec.describe 'Admin::Trends::Links::PreviewCardProviders' do
let(:current_user) { Fabricate(:admin_user) }
- before do
- sign_in current_user
- end
+ before { sign_in current_user }
describe 'Performing batch updates' do
- before do
- visit admin_trends_links_preview_card_providers_path
- end
-
context 'without selecting any records' do
it 'displays a notice about selection' do
+ visit admin_trends_links_preview_card_providers_path
+
click_on button_for_allow
- expect(page).to have_content(selection_error_text)
+ expect(page)
+ .to have_content(selection_error_text)
+ end
+ end
+
+ context 'with providers that are not trendable' do
+ let!(:provider) { Fabricate :preview_card_provider, trendable: false }
+
+ it 'allows the providers' do
+ visit admin_trends_links_preview_card_providers_path
+
+ check_item
+
+ expect { click_on button_for_allow }
+ .to change { provider.reload.trendable? }.from(false).to(true)
+ end
+ end
+
+ context 'with providers that are trendable' do
+ let!(:provider) { Fabricate :preview_card_provider, trendable: true }
+
+ it 'disallows the providers' do
+ visit admin_trends_links_preview_card_providers_path
+
+ check_item
+
+ expect { click_on button_for_disallow }
+ .to change { provider.reload.trendable? }.from(true).to(false)
+ end
+ end
+
+ def check_item
+ within '.batch-table__row' do
+ find('input[type=checkbox]').check
end
end
@@ -26,6 +55,10 @@ RSpec.describe 'Admin::Trends::Links::PreviewCardProviders' do
I18n.t('admin.trends.allow')
end
+ def button_for_disallow
+ I18n.t('admin.trends.disallow')
+ end
+
def selection_error_text
I18n.t('admin.trends.links.publishers.no_publisher_selected')
end
diff --git a/spec/system/admin/trends/links_spec.rb b/spec/system/admin/trends/links_spec.rb
index 15138f42d1..879bbe8ad9 100644
--- a/spec/system/admin/trends/links_spec.rb
+++ b/spec/system/admin/trends/links_spec.rb
@@ -5,20 +5,77 @@ require 'rails_helper'
RSpec.describe 'Admin::Trends::Links' do
let(:current_user) { Fabricate(:admin_user) }
- before do
- sign_in current_user
- end
+ before { sign_in current_user }
describe 'Performing batch updates' do
- before do
- visit admin_trends_links_path
- end
-
context 'without selecting any records' do
it 'displays a notice about selection' do
+ visit admin_trends_links_path
+
click_on button_for_allow
- expect(page).to have_content(selection_error_text)
+ expect(page)
+ .to have_content(selection_error_text)
+ end
+ end
+
+ context 'with links that are not trendable' do
+ let!(:preview_card_trend) { Fabricate :preview_card_trend, preview_card: Fabricate(:preview_card, trendable: false) }
+
+ it 'allows the links' do
+ visit admin_trends_links_path
+
+ check_item
+
+ expect { click_on button_for_allow }
+ .to change { preview_card_trend.preview_card.reload.trendable? }.from(false).to(true)
+ end
+ end
+
+ context 'with links whose providers are not trendable' do
+ let(:preview_card_provider) { Fabricate :preview_card_provider, trendable: false }
+ let!(:preview_card_trend) { Fabricate :preview_card_trend, preview_card: Fabricate(:preview_card, url: "https://#{preview_card_provider.domain}/page") }
+
+ it 'allows the providers of the links' do
+ visit admin_trends_links_path
+
+ check_item
+
+ expect { click_on button_for_allow_providers }
+ .to change { preview_card_trend.preview_card.provider.reload.trendable? }.from(false).to(true)
+ end
+ end
+
+ context 'with links that are trendable' do
+ let!(:preview_card_trend) { Fabricate :preview_card_trend, preview_card: Fabricate(:preview_card, trendable: true) }
+
+ it 'disallows the links' do
+ visit admin_trends_links_path
+
+ check_item
+
+ expect { click_on button_for_disallow }
+ .to change { preview_card_trend.preview_card.reload.trendable? }.from(true).to(false)
+ end
+ end
+
+ context 'with links whose providers are trendable' do
+ let(:preview_card_provider) { Fabricate :preview_card_provider, trendable: true }
+ let!(:preview_card_trend) { Fabricate :preview_card_trend, preview_card: Fabricate(:preview_card, url: "https://#{preview_card_provider.domain}/page") }
+
+ it 'disallows the links' do
+ visit admin_trends_links_path
+
+ check_item
+
+ expect { click_on button_for_disallow_providers }
+ .to change { preview_card_trend.preview_card.provider.reload.trendable? }.from(true).to(false)
+ end
+ end
+
+ def check_item
+ within '.batch-table__row' do
+ find('input[type=checkbox]').check
end
end
@@ -26,6 +83,18 @@ RSpec.describe 'Admin::Trends::Links' do
I18n.t('admin.trends.links.allow')
end
+ def button_for_allow_providers
+ I18n.t('admin.trends.links.allow_provider')
+ end
+
+ def button_for_disallow
+ I18n.t('admin.trends.links.disallow')
+ end
+
+ def button_for_disallow_providers
+ I18n.t('admin.trends.links.disallow_provider')
+ end
+
def selection_error_text
I18n.t('admin.trends.links.no_link_selected')
end
diff --git a/spec/system/admin/trends/statuses_spec.rb b/spec/system/admin/trends/statuses_spec.rb
index 45c048afb0..be081df989 100644
--- a/spec/system/admin/trends/statuses_spec.rb
+++ b/spec/system/admin/trends/statuses_spec.rb
@@ -5,20 +5,75 @@ require 'rails_helper'
RSpec.describe 'Admin::Trends::Statuses' do
let(:current_user) { Fabricate(:admin_user) }
- before do
- sign_in current_user
- end
+ before { sign_in current_user }
describe 'Performing batch updates' do
- before do
- visit admin_trends_statuses_path
- end
-
context 'without selecting any records' do
it 'displays a notice about selection' do
+ visit admin_trends_statuses_path
+
click_on button_for_allow
- expect(page).to have_content(selection_error_text)
+ expect(page)
+ .to have_content(selection_error_text)
+ end
+ end
+
+ context 'with statuses that are not trendable' do
+ let!(:status_trend) { Fabricate :status_trend, status: Fabricate(:status, trendable: false) }
+
+ it 'allows the statuses' do
+ visit admin_trends_statuses_path
+
+ check_item
+
+ expect { click_on button_for_allow }
+ .to change { status_trend.status.reload.trendable? }.from(false).to(true)
+ end
+ end
+
+ context 'with statuses whose accounts are not trendable' do
+ let!(:status_trend) { Fabricate :status_trend, status: Fabricate(:status, account: Fabricate(:account, trendable: false)) }
+
+ it 'allows the accounts of the statuses' do
+ visit admin_trends_statuses_path
+
+ check_item
+
+ expect { click_on button_for_allow_accounts }
+ .to change { status_trend.status.account.reload.trendable? }.from(false).to(true)
+ end
+ end
+
+ context 'with statuses that are trendable' do
+ let!(:status_trend) { Fabricate :status_trend, status: Fabricate(:status, trendable: true) }
+
+ it 'disallows the statuses' do
+ visit admin_trends_statuses_path
+
+ check_item
+
+ expect { click_on button_for_disallow }
+ .to change { status_trend.status.reload.trendable? }.from(true).to(false)
+ end
+ end
+
+ context 'with statuses whose accounts are trendable' do
+ let!(:status_trend) { Fabricate :status_trend, status: Fabricate(:status, account: Fabricate(:account, trendable: true)) }
+
+ it 'disallows the statuses' do
+ visit admin_trends_statuses_path
+
+ check_item
+
+ expect { click_on button_for_disallow_accounts }
+ .to change { status_trend.status.reload.trendable? }.from(true).to(false)
+ end
+ end
+
+ def check_item
+ within '.batch-table__row' do
+ find('input[type=checkbox]').check
end
end
@@ -26,6 +81,18 @@ RSpec.describe 'Admin::Trends::Statuses' do
I18n.t('admin.trends.statuses.allow')
end
+ def button_for_allow_accounts
+ I18n.t('admin.trends.statuses.allow_account')
+ end
+
+ def button_for_disallow
+ I18n.t('admin.trends.statuses.disallow')
+ end
+
+ def button_for_disallow_accounts
+ I18n.t('admin.trends.statuses.disallow_account')
+ end
+
def selection_error_text
I18n.t('admin.trends.statuses.no_status_selected')
end
diff --git a/spec/system/admin/trends/tags_spec.rb b/spec/system/admin/trends/tags_spec.rb
index 30b0850b93..a71d9ba8ca 100644
--- a/spec/system/admin/trends/tags_spec.rb
+++ b/spec/system/admin/trends/tags_spec.rb
@@ -5,27 +5,59 @@ require 'rails_helper'
RSpec.describe 'Admin::Trends::Tags' do
let(:current_user) { Fabricate(:admin_user) }
- before do
- sign_in current_user
- end
+ before { sign_in current_user }
describe 'Performing batch updates' do
- before do
- visit admin_trends_tags_path
- end
-
context 'without selecting any records' do
it 'displays a notice about selection' do
+ visit admin_trends_tags_path
+
click_on button_for_allow
expect(page).to have_content(selection_error_text)
end
end
+ context 'with tags that are not trendable' do
+ let!(:tag_trend) { Fabricate :tag_trend, tag: Fabricate(:tag, trendable: false) }
+
+ it 'allows the tags' do
+ visit admin_trends_tags_path
+
+ check_item
+
+ expect { click_on button_for_allow }
+ .to change { tag_trend.tag.reload.trendable? }.from(false).to(true)
+ end
+ end
+
+ context 'with tags that are trendable' do
+ let!(:tag_trend) { Fabricate :tag_trend, tag: Fabricate(:tag, trendable: true) }
+
+ it 'disallows the tags' do
+ visit admin_trends_tags_path
+
+ check_item
+
+ expect { click_on button_for_disallow }
+ .to change { tag_trend.tag.reload.trendable? }.from(true).to(false)
+ end
+ end
+
+ def check_item
+ within '.batch-table__row' do
+ find('input[type=checkbox]').check
+ end
+ end
+
def button_for_allow
I18n.t('admin.trends.allow')
end
+ def button_for_disallow
+ I18n.t('admin.trends.disallow')
+ end
+
def selection_error_text
I18n.t('admin.trends.tags.no_tag_selected')
end
diff --git a/spec/system/admin/warning_presets_spec.rb b/spec/system/admin/warning_presets_spec.rb
new file mode 100644
index 0000000000..f1ab690981
--- /dev/null
+++ b/spec/system/admin/warning_presets_spec.rb
@@ -0,0 +1,81 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe 'Admin Warning Presets' do
+ describe 'Managing warning presets' do
+ before { sign_in Fabricate(:admin_user) }
+
+ describe 'Viewing warning presets' do
+ let!(:account_warning_preset) { Fabricate :account_warning_preset, text: 'This is a preset' }
+
+ it 'lists existing records' do
+ visit admin_warning_presets_path
+
+ expect(page)
+ .to have_content(I18n.t('admin.warning_presets.title'))
+ .and have_content(account_warning_preset.text)
+ end
+ end
+
+ describe 'Creating a new account warning preset' do
+ it 'creates new record with valid attributes' do
+ visit admin_warning_presets_path
+
+ # Invalid submission
+ fill_in 'account_warning_preset_text', with: ''
+ expect { submit_form }
+ .to_not change(AccountWarningPreset, :count)
+ expect(page)
+ .to have_content(/error below/)
+
+ # Valid submission
+ fill_in 'account_warning_preset_text', with: 'You cant do that here'
+ expect { submit_form }
+ .to change(AccountWarningPreset, :count).by(1)
+ expect(page)
+ .to have_content(I18n.t('admin.warning_presets.title'))
+ end
+
+ def submit_form
+ click_on I18n.t('admin.warning_presets.add_new')
+ end
+ end
+
+ describe 'Editing an existing account warning preset' do
+ let!(:account_warning_preset) { Fabricate :account_warning_preset, text: 'Preset text' }
+
+ it 'updates with valid attributes' do
+ visit admin_warning_presets_path
+
+ # Invalid submission
+ click_on account_warning_preset.text
+ fill_in 'account_warning_preset_text', with: ''
+ expect { submit_form }
+ .to_not change(account_warning_preset.reload, :updated_at)
+
+ # Valid update
+ fill_in 'account_warning_preset_text', with: 'Updated text'
+ expect { submit_form }
+ .to(change { account_warning_preset.reload.text })
+ end
+
+ def submit_form
+ click_on I18n.t('generic.save_changes')
+ end
+ end
+
+ describe 'Destroy an account warning preset' do
+ let!(:account_warning_preset) { Fabricate :account_warning_preset }
+
+ it 'removes the record' do
+ visit admin_warning_presets_path
+
+ expect { click_on I18n.t('admin.warning_presets.delete') }
+ .to change(AccountWarningPreset, :count).by(-1)
+ expect { account_warning_preset.reload }
+ .to raise_error(ActiveRecord::RecordNotFound)
+ end
+ end
+ end
+end
diff --git a/spec/system/auth/setup_spec.rb b/spec/system/auth/setup_spec.rb
new file mode 100644
index 0000000000..154f8cd5fa
--- /dev/null
+++ b/spec/system/auth/setup_spec.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe 'Auth Setup' do
+ context 'with an unconfirmed signed in user' do
+ let(:user) { Fabricate(:user, confirmed_at: nil) }
+
+ before { sign_in(user) }
+
+ it 'can update email address' do
+ visit auth_setup_path
+
+ expect(page)
+ .to have_content(I18n.t('auth.setup.title'))
+
+ find('summary.lead').click
+ fill_in 'user_email', with: 'new-email@example.host'
+
+ expect { submit_form }
+ .to(change { user.reload.unconfirmed_email })
+ expect(page)
+ .to have_content(I18n.t('auth.setup.new_confirmation_instructions_sent'))
+ end
+
+ def submit_form
+ find('[name=button]').click
+ end
+ end
+end
diff --git a/spec/system/settings/migrations_spec.rb b/spec/system/settings/migrations_spec.rb
new file mode 100644
index 0000000000..fecde36f35
--- /dev/null
+++ b/spec/system/settings/migrations_spec.rb
@@ -0,0 +1,99 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe 'Settings Migrations' do
+ describe 'Viewing settings migrations' do
+ let(:user) { Fabricate(:account, moved_to_account: moved_to_account).user }
+
+ before { sign_in(user) }
+
+ context 'when user does not have moved to account' do
+ let(:moved_to_account) { nil }
+
+ it 'renders show page' do
+ visit settings_migration_path
+
+ expect(page)
+ .to have_content(I18n.t('settings.migrate'))
+ end
+ end
+
+ context 'when user has a moved to account' do
+ let(:moved_to_account) { Fabricate(:account) }
+
+ it 'renders show page and account details' do
+ visit settings_migration_path
+
+ expect(page)
+ .to have_content(I18n.t('settings.migrate'))
+ .and have_content(moved_to_account.pretty_acct)
+ end
+ end
+ end
+
+ describe 'Creating migrations' do
+ let(:user) { Fabricate(:user, password: '12345678') }
+
+ before { sign_in(user) }
+
+ context 'when migration account is changed' do
+ let(:acct) { Fabricate(:account, also_known_as: [ActivityPub::TagManager.instance.uri_for(user.account)]) }
+
+ it 'updates moved to account' do
+ visit settings_migration_path
+
+ expect { fill_in_and_submit }
+ .to(change { user.account.reload.moved_to_account_id }.to(acct.id))
+ expect(page)
+ .to have_content(I18n.t('settings.migrate'))
+ end
+ end
+
+ context 'when acct is the current account' do
+ let(:acct) { user.account }
+
+ it 'does not update the moved account', :aggregate_failures do
+ visit settings_migration_path
+
+ expect { fill_in_and_submit }
+ .to_not(change { user.account.reload.moved_to_account_id }.from(nil))
+ expect(page)
+ .to have_content(I18n.t('settings.migrate'))
+ end
+ end
+
+ context 'when target account does not reference the account being moved from' do
+ let(:acct) { Fabricate(:account, also_known_as: []) }
+
+ it 'does not update the moved account', :aggregate_failures do
+ visit settings_migration_path
+
+ expect { fill_in_and_submit }
+ .to_not(change { user.account.reload.moved_to_account_id }.from(nil))
+ expect(page)
+ .to have_content(I18n.t('settings.migrate'))
+ end
+ end
+
+ context 'when a recent migration already exists' do
+ let(:acct) { Fabricate(:account, also_known_as: [ActivityPub::TagManager.instance.uri_for(user.account)]) }
+ let(:moved_to) { Fabricate(:account, also_known_as: [ActivityPub::TagManager.instance.uri_for(user.account)]) }
+
+ before { user.account.migrations.create!(acct: moved_to.acct) }
+
+ it 'can not update the moved account', :aggregate_failures do
+ visit settings_migration_path
+
+ expect(find_by_id('account_migration_acct'))
+ .to be_disabled
+ end
+ end
+
+ def fill_in_and_submit
+ fill_in 'account_migration_acct', with: acct.username
+ fill_in 'account_migration_current_password', with: '12345678'
+ click_on I18n.t('migrations.proceed_with_move')
+ end
+ end
+end
diff --git a/spec/workers/web/push_notification_worker_spec.rb b/spec/workers/web/push_notification_worker_spec.rb
index 4993d467b3..6ee8ae53f8 100644
--- a/spec/workers/web/push_notification_worker_spec.rb
+++ b/spec/workers/web/push_notification_worker_spec.rb
@@ -5,21 +5,36 @@ require 'rails_helper'
RSpec.describe Web::PushNotificationWorker do
subject { described_class.new }
- let(:p256dh) { 'BN4GvZtEZiZuqFxSKVZfSfluwKBD7UxHNBmWkfiZfCtgDE8Bwh-_MtLXbBxTBAWH9r7IPKL0lhdcaqtL1dfxU5E=' }
- let(:auth) { 'Q2BoAjC09xH3ywDLNJr-dA==' }
let(:endpoint) { 'https://updates.push.services.mozilla.com/push/v1/subscription-id' }
let(:user) { Fabricate(:user) }
let(:notification) { Fabricate(:notification) }
- let(:subscription) { Fabricate(:web_push_subscription, user_id: user.id, key_p256dh: p256dh, key_auth: auth, endpoint: endpoint, data: { alerts: { notification.type => true } }) }
let(:vapid_public_key) { 'BB37UCyc8LLX4PNQSe-04vSFvpUWGrENubUaslVFM_l5TxcGVMY0C3RXPeUJAQHKYlcOM2P4vTYmkoo0VZGZTM4=' }
let(:vapid_private_key) { 'OPrw1Sum3gRoL4-DXfSCC266r-qfFSRZrnj8MgIhRHg=' }
let(:vapid_key) { Webpush::VapidKey.from_keys(vapid_public_key, vapid_private_key) }
let(:contact_email) { 'sender@example.com' }
- let(:ciphertext) { "+\xB8\xDBT}\x13\xB6\xDD.\xF9\xB0\xA7\xC8\xD2\x80\xFD\x99#\xF7\xAC\x83\xA4\xDB,\x1F\xB5\xB9w\x85>\xF7\xADr" }
- let(:salt) { "X\x97\x953\xE4X\xF8_w\xE7T\x95\xC51q\xFE" }
- let(:server_public_key) { "\x04\b-RK9w\xDD$\x16lFz\xF9=\xB4~\xC6\x12k\xF3\xF40t\xA9\xC1\fR\xC3\x81\x80\xAC\f\x7F\xE4\xCC\x8E\xC2\x88 n\x8BB\xF1\x9C\x14\a\xFA\x8D\xC9\x80\xA1\xDDyU\\&c\x01\x88#\x118Ua" }
- let(:shared_secret) { "\t\xA7&\x85\t\xC5m\b\xA8\xA7\xF8B{1\xADk\xE1y'm\xEDE\xEC\xDD\xEDj\xB3$s\xA9\xDA\xF0" }
- let(:payload) { { ciphertext: ciphertext, salt: salt, server_public_key: server_public_key, shared_secret: shared_secret } }
+
+ # Legacy values
+ let(:p256dh) { 'BN4GvZtEZiZuqFxSKVZfSfluwKBD7UxHNBmWkfiZfCtgDE8Bwh-_MtLXbBxTBAWH9r7IPKL0lhdcaqtL1dfxU5E=' }
+ let(:auth) { 'Q2BoAjC09xH3ywDLNJr-dA==' }
+ let(:legacy_subscription) { Fabricate(:web_push_subscription, user_id: user.id, key_p256dh: p256dh, key_auth: auth, endpoint: endpoint, data: { alerts: { notification.type => true } }) }
+ let(:legacy_payload) do
+ {
+ ciphertext: "+\xB8\xDBT}\x13\xB6\xDD.\xF9\xB0\xA7\xC8\xD2\x80\xFD\x99#\xF7\xAC\x83\xA4\xDB,\x1F\xB5\xB9w\x85>\xF7\xADr",
+ salt: "X\x97\x953\xE4X\xF8_w\xE7T\x95\xC51q\xFE",
+ server_public_key: "\x04\b-RK9w\xDD$\x16lFz\xF9=\xB4~\xC6\x12k\xF3\xF40t\xA9\xC1\fR\xC3\x81\x80\xAC\f\x7F\xE4\xCC\x8E\xC2\x88 n\x8BB\xF1\x9C\x14\a\xFA\x8D\xC9\x80\xA1\xDDyU\\&c\x01\x88#\x118Ua",
+ shared_secret: "\t\xA7&\x85\t\xC5m\b\xA8\xA7\xF8B{1\xADk\xE1y'm\xEDE\xEC\xDD\xEDj\xB3$s\xA9\xDA\xF0",
+ }
+ end
+
+ # Standard values, from RFC8291
+ let(:std_p256dh) { 'BCVxsr7N_eNgVRqvHtD0zTZsEc6-VV-JvLexhqUzORcxaOzi6-AYWXvTBHm4bjyPjs7Vd8pZGH6SRpkNtoIAiw4' }
+ let(:std_auth) { 'BTBZMqHH6r4Tts7J_aSIgg' }
+ let(:std_as_public) { 'BP4z9KsN6nGRTbVYI_c7VJSPQTBtkgcy27mlmlMoZIIgDll6e3vCYLocInmYWAmS6TlzAC8wEqKK6PBru3jl7A8' }
+ let(:std_as_private) { 'yfWPiYE-n46HLnH0KqZOF1fJJU3MYrct3AELtAQ-oRw' }
+ let(:std_salt) { 'DGv6ra1nlYgDCS1FRnbzlw' }
+ let(:std_subscription) { Fabricate(:web_push_subscription, user_id: user.id, key_p256dh: std_p256dh, key_auth: std_auth, endpoint: endpoint, standard: true, data: { alerts: { notification.type => true } }) }
+ let(:std_input) { 'When I grow up, I want to be a watermelon' }
+ let(:std_ciphertext) { 'DGv6ra1nlYgDCS1FRnbzlwAAEABBBP4z9KsN6nGRTbVYI_c7VJSPQTBtkgcy27mlmlMoZIIgDll6e3vCYLocInmYWAmS6TlzAC8wEqKK6PBru3jl7A_yl95bQpu6cVPTpK4Mqgkf1CXztLVBSt2Ks3oZwbuwXPXLWyouBWLVWGNWQexSgSxsj_Qulcy4a-fN' }
describe 'perform' do
around do |example|
@@ -35,20 +50,40 @@ RSpec.describe Web::PushNotificationWorker do
before do
Setting.site_contact_email = contact_email
- allow(Webpush::Encryption).to receive(:encrypt).and_return(payload)
allow(JWT).to receive(:encode).and_return('jwt.encoded.payload')
stub_request(:post, endpoint).to_return(status: 201, body: '')
end
- it 'calls the relevant service with the correct headers' do
- subject.perform(subscription.id, notification.id)
+ it 'Legacy push calls the relevant service with the legacy headers' do
+ allow(Webpush::Legacy::Encryption).to receive(:encrypt).and_return(legacy_payload)
- expect(web_push_endpoint_request)
+ subject.perform(legacy_subscription.id, notification.id)
+
+ expect(legacy_web_push_endpoint_request)
.to have_been_made
end
- def web_push_endpoint_request
+ # We allow subject stub to encrypt the same input than the RFC8291 example
+ # rubocop:disable RSpec/SubjectStub
+ it 'Standard push calls the relevant service with the standard headers' do
+ # Mock server keys to match RFC example
+ allow(OpenSSL::PKey::EC).to receive(:generate).and_return(std_as_keys)
+ # Mock the random salt to match RFC example
+ rand = Random.new
+ allow(Random).to receive(:new).and_return(rand)
+ allow(rand).to receive(:bytes).and_return(Webpush.decode64(std_salt))
+ # Mock input to match RFC example
+ allow(subject).to receive(:push_notification_json).and_return(std_input)
+
+ subject.perform(std_subscription.id, notification.id)
+
+ expect(standard_web_push_endpoint_request)
+ .to have_been_made
+ end
+ # rubocop:enable RSpec/SubjectStub
+
+ def legacy_web_push_endpoint_request
a_request(
:post,
endpoint
@@ -66,5 +101,28 @@ RSpec.describe Web::PushNotificationWorker do
body: "+\xB8\xDBT}\u0013\xB6\xDD.\xF9\xB0\xA7\xC8Ҁ\xFD\x99#\xF7\xAC\x83\xA4\xDB,\u001F\xB5\xB9w\x85>\xF7\xADr"
)
end
+
+ def standard_web_push_endpoint_request
+ a_request(
+ :post,
+ endpoint
+ ).with(
+ headers: {
+ 'Content-Encoding' => 'aes128gcm',
+ 'Content-Type' => 'application/octet-stream',
+ 'Ttl' => '172800',
+ 'Urgency' => 'normal',
+ 'Authorization' => "vapid t=jwt.encoded.payload,k=#{vapid_public_key.delete('=')}",
+ 'Unsubscribe-URL' => %r{/api/web/push_subscriptions/},
+ },
+ body: Webpush.decode64(std_ciphertext)
+ )
+ end
+
+ def std_as_keys
+ # VapidKey contains a method to retrieve EC keypair from
+ # B64 raw keys, the keypair is stored in curve field
+ Webpush::VapidKey.from_keys(std_as_public, std_as_private).curve
+ end
end
end
diff --git a/streaming/package.json b/streaming/package.json
index 2419ffd273..b98608edd5 100644
--- a/streaming/package.json
+++ b/streaming/package.json
@@ -21,7 +21,7 @@
"dotenv": "^16.0.3",
"express": "^4.18.2",
"ioredis": "^5.3.2",
- "jsdom": "^25.0.0",
+ "jsdom": "^26.0.0",
"pg": "^8.5.0",
"pg-connection-string": "^2.6.0",
"pino": "^9.0.0",
diff --git a/yarn.lock b/yarn.lock
index 13adec91f8..8c07b30e32 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -42,6 +42,19 @@ __metadata:
languageName: node
linkType: hard
+"@asamuzakjp/css-color@npm:^2.8.2":
+ version: 2.8.2
+ resolution: "@asamuzakjp/css-color@npm:2.8.2"
+ dependencies:
+ "@csstools/css-calc": "npm:^2.1.1"
+ "@csstools/css-color-parser": "npm:^3.0.7"
+ "@csstools/css-parser-algorithms": "npm:^3.0.4"
+ "@csstools/css-tokenizer": "npm:^3.0.3"
+ lru-cache: "npm:^11.0.2"
+ checksum: 10c0/352b91ca7741876e459cd3cb350a969e842da1e532577157d38365a6da89b7d6e6944249489366ee61b8a225ede1b521e7ab305b70ad4c688b01404061eecca8
+ languageName: node
+ linkType: hard
+
"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.25.9, @babel/code-frame@npm:^7.26.0":
version: 7.26.0
resolution: "@babel/code-frame@npm:7.26.0"
@@ -225,10 +238,10 @@ __metadata:
languageName: node
linkType: hard
-"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.22.5, @babel/helper-plugin-utils@npm:^7.25.0, @babel/helper-plugin-utils@npm:^7.25.9, @babel/helper-plugin-utils@npm:^7.8.0":
- version: 7.25.9
- resolution: "@babel/helper-plugin-utils@npm:7.25.9"
- checksum: 10c0/483066a1ba36ff16c0116cd24f93de05de746a603a777cd695ac7a1b034928a65a4ecb35f255761ca56626435d7abdb73219eba196f9aa83b6c3c3169325599d
+"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.22.5, @babel/helper-plugin-utils@npm:^7.25.0, @babel/helper-plugin-utils@npm:^7.25.9, @babel/helper-plugin-utils@npm:^7.26.5, @babel/helper-plugin-utils@npm:^7.8.0":
+ version: 7.26.5
+ resolution: "@babel/helper-plugin-utils@npm:7.26.5"
+ checksum: 10c0/cdaba71d4b891aa6a8dfbe5bac2f94effb13e5fa4c2c487667fdbaa04eae059b78b28d85a885071f45f7205aeb56d16759e1bed9c118b94b16e4720ef1ab0f65
languageName: node
linkType: hard
@@ -922,13 +935,13 @@ __metadata:
linkType: hard
"@babel/plugin-transform-nullish-coalescing-operator@npm:^7.22.3, @babel/plugin-transform-nullish-coalescing-operator@npm:^7.25.9":
- version: 7.25.9
- resolution: "@babel/plugin-transform-nullish-coalescing-operator@npm:7.25.9"
+ version: 7.26.6
+ resolution: "@babel/plugin-transform-nullish-coalescing-operator@npm:7.26.6"
dependencies:
- "@babel/helper-plugin-utils": "npm:^7.25.9"
+ "@babel/helper-plugin-utils": "npm:^7.26.5"
peerDependencies:
"@babel/core": ^7.0.0-0
- checksum: 10c0/eb623db5be078a1c974afe7c7797b0309ba2ea9e9237c0b6831ade0f56d8248bb4ab3432ab34495ff8c877ec2fe412ff779d1e9b3c2b8139da18e1753d950bc3
+ checksum: 10c0/574d6db7cbc5c092db5d1dece8ce26195e642b9c40dbfeaf3082058a78ad7959c1c333471cdd45f38b784ec488850548075d527b178c5010ee9bff7aa527cc7a
languageName: node
linkType: hard
@@ -2267,14 +2280,15 @@ __metadata:
languageName: node
linkType: hard
-"@formatjs/ecma402-abstract@npm:2.2.3":
- version: 2.2.3
- resolution: "@formatjs/ecma402-abstract@npm:2.2.3"
+"@formatjs/ecma402-abstract@npm:2.3.2":
+ version: 2.3.2
+ resolution: "@formatjs/ecma402-abstract@npm:2.3.2"
dependencies:
- "@formatjs/fast-memoize": "npm:2.2.3"
- "@formatjs/intl-localematcher": "npm:0.5.7"
+ "@formatjs/fast-memoize": "npm:2.2.6"
+ "@formatjs/intl-localematcher": "npm:0.5.10"
+ decimal.js: "npm:10"
tslib: "npm:2"
- checksum: 10c0/611d12bf320fc5c5b85cb2b57e3dcebe8490a51c6a0459c857c7a3560656cd2bdba5b117e9dd7cf174f5aa120c11eaad7a65a6783637b816caa59a1bc5c727f6
+ checksum: 10c0/364e9e7de974fed976e0e8142a0f888ee0af4a11a61899115e5761ed933e7c1f16379b7b54a01524fd3c5d58bf08b71308237ea969cd54889eaf7bb2d30ec776
languageName: node
linkType: hard
@@ -2287,12 +2301,12 @@ __metadata:
languageName: node
linkType: hard
-"@formatjs/fast-memoize@npm:2.2.3":
- version: 2.2.3
- resolution: "@formatjs/fast-memoize@npm:2.2.3"
+"@formatjs/fast-memoize@npm:2.2.6":
+ version: 2.2.6
+ resolution: "@formatjs/fast-memoize@npm:2.2.6"
dependencies:
tslib: "npm:2"
- checksum: 10c0/f1004c3b280de7e362bd37c5f48ff34c2ba1d6271d4a7b695fed561d1201a3379397824d8bffbf15fecee344d1e70398393bbb04297f242692310a305f12e75b
+ checksum: 10c0/dccdc21105af673e58ec7b04eb17cd6fde1fb1a7e7a446273ca43f7ab97c26d5c0fcc2b9e80d5b54bf9b80354f9e1e681273c0ed26633ec72f0adc2d116dfd7f
languageName: node
linkType: hard
@@ -2307,14 +2321,24 @@ __metadata:
languageName: node
linkType: hard
-"@formatjs/icu-messageformat-parser@npm:2.9.3":
- version: 2.9.3
- resolution: "@formatjs/icu-messageformat-parser@npm:2.9.3"
+"@formatjs/icu-messageformat-parser@npm:2.9.8":
+ version: 2.9.8
+ resolution: "@formatjs/icu-messageformat-parser@npm:2.9.8"
dependencies:
- "@formatjs/ecma402-abstract": "npm:2.2.3"
- "@formatjs/icu-skeleton-parser": "npm:1.8.7"
+ "@formatjs/ecma402-abstract": "npm:2.3.2"
+ "@formatjs/icu-skeleton-parser": "npm:1.8.12"
tslib: "npm:2"
- checksum: 10c0/519b59f7b4cf90681315c5382f7fcd105eb1974486f0d62d9227b6d0498895114ccc818792c208baae1ef79571d93b0edb9914c676e5ab76924dddb7fd6c28a0
+ checksum: 10c0/df97c7f24fbeb8ef49ae1371f9498ad90f231f88211bf1effb7b2e8ac3531bec67c5d9147ddcb1add0ba697e8d089729add44a9a9c5015e0e8d61e7a43f062d9
+ languageName: node
+ linkType: hard
+
+"@formatjs/icu-skeleton-parser@npm:1.8.12":
+ version: 1.8.12
+ resolution: "@formatjs/icu-skeleton-parser@npm:1.8.12"
+ dependencies:
+ "@formatjs/ecma402-abstract": "npm:2.3.2"
+ tslib: "npm:2"
+ checksum: 10c0/03e743aa09acb2137e37d03b98578fcbbc949d056b8c151763778e885d04d621e69c82f7656547f0532351d2a987bffac0a8c4c3d81186f47a28047ba64385e2
languageName: node
linkType: hard
@@ -2328,35 +2352,12 @@ __metadata:
languageName: node
linkType: hard
-"@formatjs/icu-skeleton-parser@npm:1.8.7":
- version: 1.8.7
- resolution: "@formatjs/icu-skeleton-parser@npm:1.8.7"
+"@formatjs/intl-localematcher@npm:0.5.10":
+ version: 0.5.10
+ resolution: "@formatjs/intl-localematcher@npm:0.5.10"
dependencies:
- "@formatjs/ecma402-abstract": "npm:2.2.3"
tslib: "npm:2"
- checksum: 10c0/e29eb4151580f2d324e6591509dc4543e2326266fc209a08580c94d502acab14acc3560d98b3aaf9ffbd5ff8e2683601ff08c65b32886f22da015c31ca35c5d0
- languageName: node
- linkType: hard
-
-"@formatjs/intl-displaynames@npm:6.8.3":
- version: 6.8.3
- resolution: "@formatjs/intl-displaynames@npm:6.8.3"
- dependencies:
- "@formatjs/ecma402-abstract": "npm:2.2.3"
- "@formatjs/intl-localematcher": "npm:0.5.7"
- tslib: "npm:2"
- checksum: 10c0/54d3ecaabc45dc8494be4cd9633bbf5eb0bc40c5f62ed5e5073aa5b214c4a9a9620d61d1c8a6f0074618474fd9c2ccc42904605078d2f6341c242bf43627bb3a
- languageName: node
- linkType: hard
-
-"@formatjs/intl-listformat@npm:7.7.3":
- version: 7.7.3
- resolution: "@formatjs/intl-listformat@npm:7.7.3"
- dependencies:
- "@formatjs/ecma402-abstract": "npm:2.2.3"
- "@formatjs/intl-localematcher": "npm:0.5.7"
- tslib: "npm:2"
- checksum: 10c0/2683513e86cc7885528f23237079e3ab9e3a8bc7111aa10d4c685dcbe368ef07039573115df240112fb20f2ec0b70c8ea189ea3b79cbfed7e3dc46024a4667ff
+ checksum: 10c0/362ec83aca9382165be575f1cefa477478339e6fead8ca8866185ce6e58427ea1487a811b12c73d1bcfa99fd4db0c24543b35c823451839f585576bfccb8c9cc
languageName: node
linkType: hard
@@ -2369,43 +2370,33 @@ __metadata:
languageName: node
linkType: hard
-"@formatjs/intl-localematcher@npm:0.5.7":
- version: 0.5.7
- resolution: "@formatjs/intl-localematcher@npm:0.5.7"
- dependencies:
- tslib: "npm:2"
- checksum: 10c0/1ae374ca146a0d7457794926eed808c99971628e594f704a42ae2540b1f38928b26cbf942a7bbcc2796cc9fe8d9d7a603ac422bd9b89b714d2f91b506da40792
- languageName: node
- linkType: hard
-
"@formatjs/intl-pluralrules@npm:^5.2.2":
- version: 5.3.3
- resolution: "@formatjs/intl-pluralrules@npm:5.3.3"
+ version: 5.4.2
+ resolution: "@formatjs/intl-pluralrules@npm:5.4.2"
dependencies:
- "@formatjs/ecma402-abstract": "npm:2.2.3"
- "@formatjs/intl-localematcher": "npm:0.5.7"
+ "@formatjs/ecma402-abstract": "npm:2.3.2"
+ "@formatjs/intl-localematcher": "npm:0.5.10"
+ decimal.js: "npm:10"
tslib: "npm:2"
- checksum: 10c0/003d33af6f5d902517f230b7038e39b8336da3a84500fe5f4278fa5cac0c9a1b746de484f1c2bb2315026fd771491eafdc0aa59a809bb25189b63093efb8400d
+ checksum: 10c0/0ecb9da19084d7a15e636362c206c7ee14297ad365e3e63ea53c82c8442d02cd4dcf8a0da65af9b0f835b32f0e8c3d43407d6ee0a0d7974c3044c92574b49686
languageName: node
linkType: hard
-"@formatjs/intl@npm:2.10.13":
- version: 2.10.13
- resolution: "@formatjs/intl@npm:2.10.13"
+"@formatjs/intl@npm:3.1.0":
+ version: 3.1.0
+ resolution: "@formatjs/intl@npm:3.1.0"
dependencies:
- "@formatjs/ecma402-abstract": "npm:2.2.3"
- "@formatjs/fast-memoize": "npm:2.2.3"
- "@formatjs/icu-messageformat-parser": "npm:2.9.3"
- "@formatjs/intl-displaynames": "npm:6.8.3"
- "@formatjs/intl-listformat": "npm:7.7.3"
- intl-messageformat: "npm:10.7.5"
+ "@formatjs/ecma402-abstract": "npm:2.3.2"
+ "@formatjs/fast-memoize": "npm:2.2.6"
+ "@formatjs/icu-messageformat-parser": "npm:2.9.8"
+ intl-messageformat: "npm:10.7.11"
tslib: "npm:2"
peerDependencies:
- typescript: ^4.7 || 5
+ typescript: 5
peerDependenciesMeta:
typescript:
optional: true
- checksum: 10c0/14edcc45addc181c7a45845bc34cec5c73bb5544dcf8ed455eca916bbd3a5023c7204a8e8e289060545b85006933c6f297c33c4f04a8a4cb3890aa7baae5bfbf
+ checksum: 10c0/a073768fffc51696eb7bd25fe1f0afdda1a0e38db3e2dd9b2fc3138ea799f00ef522f3d3083626ad3acbf913593254cfd728a6c6b08ef4f167dd132626a7e9fc
languageName: node
linkType: hard
@@ -2429,11 +2420,11 @@ __metadata:
languageName: node
linkType: hard
-"@formatjs/ts-transformer@npm:3.13.22":
- version: 3.13.22
- resolution: "@formatjs/ts-transformer@npm:3.13.22"
+"@formatjs/ts-transformer@npm:3.13.27":
+ version: 3.13.27
+ resolution: "@formatjs/ts-transformer@npm:3.13.27"
dependencies:
- "@formatjs/icu-messageformat-parser": "npm:2.9.3"
+ "@formatjs/icu-messageformat-parser": "npm:2.9.8"
"@types/json-stable-stringify": "npm:1"
"@types/node": "npm:14 || 16 || 17 || 18 || 20 || 22"
chalk: "npm:4"
@@ -2445,7 +2436,7 @@ __metadata:
peerDependenciesMeta:
ts-jest:
optional: true
- checksum: 10c0/42503292248bcae728181fdf68e79eac4169c18064953beb9245097d2c58e5434ae7a7978e6ce2829dfd6eb4b4155f78fecef70ac6820626a338f231c66f60cb
+ checksum: 10c0/ab26ce081835ce2a1384172a6e24db42d48bc9f8b9b941c646fba2f01ce8370123480623d1fbc1e75f6ec90a232f8ff67b5243c5e9ad18311dc03d7061a4892b
languageName: node
linkType: hard
@@ -2942,7 +2933,7 @@ __metadata:
react-hotkeys: "npm:^1.1.4"
react-immutable-proptypes: "npm:^2.2.0"
react-immutable-pure-component: "npm:^2.2.2"
- react-intl: "npm:^6.4.2"
+ react-intl: "npm:^7.0.0"
react-motion: "npm:^0.5.2"
react-notification: "npm:^6.8.5"
react-overlays: "npm:^5.2.1"
@@ -3012,7 +3003,7 @@ __metadata:
eslint-define-config: "npm:^2.0.0"
express: "npm:^4.18.2"
ioredis: "npm:^5.3.2"
- jsdom: "npm:^25.0.0"
+ jsdom: "npm:^26.0.0"
pg: "npm:^8.5.0"
pg-connection-string: "npm:^2.6.0"
pino: "npm:^9.0.0"
@@ -3294,22 +3285,22 @@ __metadata:
linkType: hard
"@reduxjs/toolkit@npm:^2.0.1":
- version: 2.3.0
- resolution: "@reduxjs/toolkit@npm:2.3.0"
+ version: 2.5.0
+ resolution: "@reduxjs/toolkit@npm:2.5.0"
dependencies:
immer: "npm:^10.0.3"
redux: "npm:^5.0.1"
redux-thunk: "npm:^3.1.0"
reselect: "npm:^5.1.0"
peerDependencies:
- react: ^16.9.0 || ^17.0.0 || ^18
+ react: ^16.9.0 || ^17.0.0 || ^18 || ^19
react-redux: ^7.2.1 || ^8.1.3 || ^9.0.0
peerDependenciesMeta:
react:
optional: true
react-redux:
optional: true
- checksum: 10c0/414e90b706331385a2122fc79e33f90c59a9caf9a59419f1bfd7f5e594bc8e4987902fd1bccbc53eb96d22c65ec2981ff5581f3d2df3ecd381a630f391edfc3e
+ checksum: 10c0/81748a5a6d2f52a14769b6ed25aea1e77cda81b1db6599c7c3a1d1605696c65c4469f55146c2b2a7a2f8ebafa5ecd4996aa8deecb37aebb5307217ec2fe384ac
languageName: node
linkType: hard
@@ -3833,12 +3824,12 @@ __metadata:
linkType: hard
"@types/hoist-non-react-statics@npm:3, @types/hoist-non-react-statics@npm:^3.3.1":
- version: 3.3.5
- resolution: "@types/hoist-non-react-statics@npm:3.3.5"
+ version: 3.3.6
+ resolution: "@types/hoist-non-react-statics@npm:3.3.6"
dependencies:
"@types/react": "npm:*"
hoist-non-react-statics: "npm:^3.3.0"
- checksum: 10c0/2a3b64bf3d9817d7830afa60ee314493c475fb09570a64e7737084cd482d2177ebdddf888ce837350bac51741278b077683facc9541f052d4bbe8487b4e3e618
+ checksum: 10c0/149a4c217d81f21f8a1e152160a59d5b99b6a9aa6d354385d5f5bc02760cbf1e170a8442ba92eb653befff44b0c5bc2234bb77ce33e0d11a65f779e8bab5c321
languageName: node
linkType: hard
@@ -4018,9 +4009,9 @@ __metadata:
linkType: hard
"@types/prop-types@npm:*, @types/prop-types@npm:^15.7.5":
- version: 15.7.13
- resolution: "@types/prop-types@npm:15.7.13"
- checksum: 10c0/1b20fc67281902c6743379960247bc161f3f0406ffc0df8e7058745a85ea1538612109db0406290512947f9632fe9e10e7337bf0ce6338a91d6c948df16a7c61
+ version: 15.7.14
+ resolution: "@types/prop-types@npm:15.7.14"
+ checksum: 10c0/1ec775160bfab90b67a782d735952158c7e702ca4502968aa82565bd8e452c2de8601c8dfe349733073c31179116cf7340710160d3836aa8a1ef76d1532893b1
languageName: node
linkType: hard
@@ -4060,11 +4051,11 @@ __metadata:
linkType: hard
"@types/react-dom@npm:^18.2.4":
- version: 18.3.1
- resolution: "@types/react-dom@npm:18.3.1"
- dependencies:
- "@types/react": "npm:*"
- checksum: 10c0/8b416551c60bb6bd8ec10e198c957910cfb271bc3922463040b0d57cf4739cdcd24b13224f8d68f10318926e1ec3cd69af0af79f0291b599a992f8c80d47f1eb
+ version: 18.3.5
+ resolution: "@types/react-dom@npm:18.3.5"
+ peerDependencies:
+ "@types/react": ^18.0.0
+ checksum: 10c0/b163d35a6b32a79f5782574a7aeb12a31a647e248792bf437e6d596e2676961c394c5e3c6e91d1ce44ae90441dbaf93158efb4f051c0d61e2612f1cb04ce4faa
languageName: node
linkType: hard
@@ -4127,20 +4118,20 @@ __metadata:
linkType: hard
"@types/react-swipeable-views@npm:^0.13.1":
- version: 0.13.5
- resolution: "@types/react-swipeable-views@npm:0.13.5"
+ version: 0.13.6
+ resolution: "@types/react-swipeable-views@npm:0.13.6"
dependencies:
"@types/react": "npm:*"
- checksum: 10c0/d1dcc78d862f37d30a43d79d915fdb388e05dce0b2ac07462ca4f1b00e0eef37cb41d75997f5685dec79bcce1ffee0dfbc744f20d5266dd3090658def5b4e193
+ checksum: 10c0/a26879146748417234bb7f44c5a71e6bab2b76c0b34c72f0493c18403487a5d77021510e8665bd8bd22786904fbbd90d6db55c8dd2bf983c32421139de851c94
languageName: node
linkType: hard
"@types/react-test-renderer@npm:^18.0.0":
- version: 18.3.0
- resolution: "@types/react-test-renderer@npm:18.3.0"
+ version: 18.3.1
+ resolution: "@types/react-test-renderer@npm:18.3.1"
dependencies:
- "@types/react": "npm:*"
- checksum: 10c0/3c9748be52e8e659e7adf91dea6939486463264e6f633bf21c4cb116de18af7bef0595568a1e588160420b2f65289473075dda1cb417c2875df8cf7a09f5d913
+ "@types/react": "npm:^18"
+ checksum: 10c0/9fc8467ff1a3f14be6cc3498a75fc788d2c92c0fffa7bf21269ed5d9d82db9195bf2178ddc42ea16a0836995c1b77601c6be8abb27bd1864668c418c6d0e5a3b
languageName: node
linkType: hard
@@ -4162,13 +4153,13 @@ __metadata:
languageName: node
linkType: hard
-"@types/react@npm:*, @types/react@npm:16 || 17 || 18, @types/react@npm:>=16.9.11, @types/react@npm:^18.2.7":
- version: 18.3.12
- resolution: "@types/react@npm:18.3.12"
+"@types/react@npm:^18.2.7":
+ version: 18.3.18
+ resolution: "@types/react@npm:18.3.18"
dependencies:
"@types/prop-types": "npm:*"
csstype: "npm:^3.0.2"
- checksum: 10c0/8bae8d9a41619804561574792e29112b413044eb0d53746dde2b9720c1f9a59f71c895bbd7987cd8ce9500b00786e53bc032dced38cddf42910458e145675290
+ checksum: 10c0/8fb2b00672072135d0858dc9db07873ea107cc238b6228aaa2a9afd1ef7a64a7074078250db38afbeb19064be8ea6af5eac32d404efdd5f45e093cc4829d87f8
languageName: node
linkType: hard
@@ -4743,12 +4734,10 @@ __metadata:
languageName: node
linkType: hard
-"agent-base@npm:^7.0.2, agent-base@npm:^7.1.0":
- version: 7.1.0
- resolution: "agent-base@npm:7.1.0"
- dependencies:
- debug: "npm:^4.3.4"
- checksum: 10c0/fc974ab57ffdd8421a2bc339644d312a9cca320c20c3393c9d8b1fd91731b9bbabdb985df5fc860f5b79d81c3e350daa3fcb31c5c07c0bb385aafc817df004ce
+"agent-base@npm:^7.0.2, agent-base@npm:^7.1.0, agent-base@npm:^7.1.2":
+ version: 7.1.3
+ resolution: "agent-base@npm:7.1.3"
+ checksum: 10c0/6192b580c5b1d8fb399b9c62bf8343d76654c2dd62afcb9a52b2cf44a8b6ace1e3b704d3fe3547d91555c857d3df02603341ff2cb961b9cfe2b12f9f3c38ee11
languageName: node
linkType: hard
@@ -5351,21 +5340,21 @@ __metadata:
linkType: hard
"babel-plugin-formatjs@npm:^10.5.1":
- version: 10.5.24
- resolution: "babel-plugin-formatjs@npm:10.5.24"
+ version: 10.5.30
+ resolution: "babel-plugin-formatjs@npm:10.5.30"
dependencies:
"@babel/core": "npm:^7.25.0"
"@babel/helper-plugin-utils": "npm:^7.25.0"
"@babel/plugin-syntax-jsx": "npm:^7.25.0"
"@babel/traverse": "npm:^7.25.0"
"@babel/types": "npm:^7.25.0"
- "@formatjs/icu-messageformat-parser": "npm:2.9.3"
- "@formatjs/ts-transformer": "npm:3.13.22"
+ "@formatjs/icu-messageformat-parser": "npm:2.9.8"
+ "@formatjs/ts-transformer": "npm:3.13.27"
"@types/babel__core": "npm:^7.20.5"
"@types/babel__helper-plugin-utils": "npm:^7.10.3"
"@types/babel__traverse": "npm:^7.20.6"
tslib: "npm:2"
- checksum: 10c0/a99c92e62edb30e0b6a5bf1115811eb18336b851109f8c096b5a9aa2be3eb72299eea3a8cb706e03705eb79edfa5a62d69bb52d80a375c1d5a1c963e3d00c40e
+ checksum: 10c0/cf9f17b71da9e95a402e0722cfbf4a549c984a8bb536f6b46dc187fa4b69dd2e248e6a825ea95d5ddfa2a9b09b3eb399cefa2dce1dcf52fd6e661adb99a9d3fa
languageName: node
linkType: hard
@@ -6544,9 +6533,9 @@ __metadata:
linkType: hard
"core-js@npm:^3.30.2":
- version: 3.39.0
- resolution: "core-js@npm:3.39.0"
- checksum: 10c0/f7602069b6afb2e3298eec612a5c1e0c3e6a458930fbfc7a4c5f9ac03426507f49ce395eecdd2d9bae9024f820e44582b67ffe16f2272395af26964f174eeb6b
+ version: 3.40.0
+ resolution: "core-js@npm:3.40.0"
+ checksum: 10c0/db7946ada881e845d8b157061945b1187618fa45cf162f392a151e8a497962aed2da688c982eaa1d444c864be97a70f8be4d73385294b515d224dd164d19f1d4
languageName: node
linkType: hard
@@ -7003,12 +6992,13 @@ __metadata:
languageName: node
linkType: hard
-"cssstyle@npm:^4.1.0":
- version: 4.1.0
- resolution: "cssstyle@npm:4.1.0"
+"cssstyle@npm:^4.2.1":
+ version: 4.2.1
+ resolution: "cssstyle@npm:4.2.1"
dependencies:
- rrweb-cssom: "npm:^0.7.1"
- checksum: 10c0/05c6597e5d3e0ec6b15221f2c0ce9a0443a46cc50a6089a3ba9ee1ac27f83ff86a445a8f95435137dadd859f091fc61b6d342abaf396d3c910471b5b33cfcbfa
+ "@asamuzakjp/css-color": "npm:^2.8.2"
+ rrweb-cssom: "npm:^0.8.0"
+ checksum: 10c0/02ba8c47c0caaab57acadacb3eb6c0f5f009000f55d61f6563670e07d389b26edefeed497e6c1847fcd2e6bbe0b6974c2d4291f97fa0c6ec6add13a7fa926d84
languageName: node
linkType: hard
@@ -7131,7 +7121,7 @@ __metadata:
languageName: node
linkType: hard
-"decimal.js@npm:^10.4.2, decimal.js@npm:^10.4.3":
+"decimal.js@npm:10, decimal.js@npm:^10.4.2, decimal.js@npm:^10.4.3":
version: 10.4.3
resolution: "decimal.js@npm:10.4.3"
checksum: 10c0/6d60206689ff0911f0ce968d40f163304a6c1bc739927758e6efc7921cfa630130388966f16bf6ef6b838cb33679fbe8e7a78a2f3c478afce841fd55ac8fb8ee
@@ -7719,7 +7709,7 @@ __metadata:
languageName: node
linkType: hard
-"entities@npm:^4.2.0, entities@npm:^4.4.0":
+"entities@npm:^4.2.0, entities@npm:^4.5.0":
version: 4.5.0
resolution: "entities@npm:4.5.0"
checksum: 10c0/5b039739f7621f5d1ad996715e53d964035f75ad3b9a4d38c6b3804bb226e282ffeae2443624d8fdd9c47d8e926ae9ac009c54671243f0c3294c26af7cc85250
@@ -8890,14 +8880,14 @@ __metadata:
languageName: node
linkType: hard
-"form-data@npm:^4.0.0":
- version: 4.0.0
- resolution: "form-data@npm:4.0.0"
+"form-data@npm:^4.0.0, form-data@npm:^4.0.1":
+ version: 4.0.1
+ resolution: "form-data@npm:4.0.1"
dependencies:
asynckit: "npm:^0.4.0"
combined-stream: "npm:^1.0.8"
mime-types: "npm:^2.1.12"
- checksum: 10c0/cb6f3ac49180be03ff07ba3ff125f9eba2ff0b277fb33c7fc47569fc5e616882c5b1c69b9904c4c4187e97dd0419dd03b134174756f296dec62041e6527e2c6e
+ checksum: 10c0/bb102d570be8592c23f4ea72d7df9daa50c7792eb0cf1c5d7e506c1706e7426a4e4ae48a35b109e91c85f1c0ec63774a21ae252b66f4eb981cb8efef7d0463c8
languageName: node
linkType: hard
@@ -9677,13 +9667,13 @@ __metadata:
languageName: node
linkType: hard
-"https-proxy-agent@npm:^7.0.1, https-proxy-agent@npm:^7.0.5":
- version: 7.0.5
- resolution: "https-proxy-agent@npm:7.0.5"
+"https-proxy-agent@npm:^7.0.1, https-proxy-agent@npm:^7.0.6":
+ version: 7.0.6
+ resolution: "https-proxy-agent@npm:7.0.6"
dependencies:
- agent-base: "npm:^7.0.2"
+ agent-base: "npm:^7.1.2"
debug: "npm:4"
- checksum: 10c0/2490e3acec397abeb88807db52cac59102d5ed758feee6df6112ab3ccd8325e8a1ce8bce6f4b66e5470eca102d31e425ace904242e4fa28dbe0c59c4bafa7b2c
+ checksum: 10c0/f729219bc735edb621fa30e6e84e60ee5d00802b8247aac0d7b79b0bd6d4b3294737a337b93b86a0bd9e68099d031858a39260c976dc14cdbba238ba1f8779ac
languageName: node
linkType: hard
@@ -9942,15 +9932,15 @@ __metadata:
languageName: node
linkType: hard
-"intl-messageformat@npm:10.7.5, intl-messageformat@npm:^10.3.5":
- version: 10.7.5
- resolution: "intl-messageformat@npm:10.7.5"
+"intl-messageformat@npm:10.7.11, intl-messageformat@npm:^10.3.5":
+ version: 10.7.11
+ resolution: "intl-messageformat@npm:10.7.11"
dependencies:
- "@formatjs/ecma402-abstract": "npm:2.2.3"
- "@formatjs/fast-memoize": "npm:2.2.3"
- "@formatjs/icu-messageformat-parser": "npm:2.9.3"
+ "@formatjs/ecma402-abstract": "npm:2.3.2"
+ "@formatjs/fast-memoize": "npm:2.2.6"
+ "@formatjs/icu-messageformat-parser": "npm:2.9.8"
tslib: "npm:2"
- checksum: 10c0/1aa173a8c16ace50520af3de7d3f0ce9bafda90d47b6d674eb88cc47c12c3460d4d53c97ca412fae1141b00abb8ff2ba313250997a040837f01a25379165c949
+ checksum: 10c0/7ccd972277cc6798038af876c830203084db6552becfa99c3706541fd67838552013f57f8ed0ed3aed03d4fba436591a83a25f913365d66ad04ee9332eee7b73
languageName: node
linkType: hard
@@ -11276,21 +11266,21 @@ __metadata:
languageName: node
linkType: hard
-"jsdom@npm:^25.0.0":
- version: 25.0.1
- resolution: "jsdom@npm:25.0.1"
+"jsdom@npm:^26.0.0":
+ version: 26.0.0
+ resolution: "jsdom@npm:26.0.0"
dependencies:
- cssstyle: "npm:^4.1.0"
+ cssstyle: "npm:^4.2.1"
data-urls: "npm:^5.0.0"
decimal.js: "npm:^10.4.3"
- form-data: "npm:^4.0.0"
+ form-data: "npm:^4.0.1"
html-encoding-sniffer: "npm:^4.0.0"
http-proxy-agent: "npm:^7.0.2"
- https-proxy-agent: "npm:^7.0.5"
+ https-proxy-agent: "npm:^7.0.6"
is-potential-custom-element-name: "npm:^1.0.1"
- nwsapi: "npm:^2.2.12"
- parse5: "npm:^7.1.2"
- rrweb-cssom: "npm:^0.7.1"
+ nwsapi: "npm:^2.2.16"
+ parse5: "npm:^7.2.1"
+ rrweb-cssom: "npm:^0.8.0"
saxes: "npm:^6.0.0"
symbol-tree: "npm:^3.2.4"
tough-cookie: "npm:^5.0.0"
@@ -11298,15 +11288,15 @@ __metadata:
webidl-conversions: "npm:^7.0.0"
whatwg-encoding: "npm:^3.1.1"
whatwg-mimetype: "npm:^4.0.0"
- whatwg-url: "npm:^14.0.0"
+ whatwg-url: "npm:^14.1.0"
ws: "npm:^8.18.0"
xml-name-validator: "npm:^5.0.0"
peerDependencies:
- canvas: ^2.11.2
+ canvas: ^3.0.0
peerDependenciesMeta:
canvas:
optional: true
- checksum: 10c0/6bda32a6dfe4e37a30568bf51136bdb3ba9c0b72aadd6356280404275a34c9e097c8c25b5eb3c742e602623741e172da977ff456684befd77c9042ed9bf8c2b4
+ checksum: 10c0/e48725ba4027edcfc9bca5799eaec72c6561ecffe3675a8ff87fe9c3541ca4ff9f82b4eff5b3d9c527302da0d859b2f60e9364347a5d42b77f5c76c436c569dc
languageName: node
linkType: hard
@@ -11773,6 +11763,13 @@ __metadata:
languageName: node
linkType: hard
+"lru-cache@npm:^11.0.2":
+ version: 11.0.2
+ resolution: "lru-cache@npm:11.0.2"
+ checksum: 10c0/c993b8e06ead0b24b969c1dbb5b301716aed66e320e9014a80012f5febe280b438f28ff50046b2c55ff404e889351ccb332ff91f8dd175a21f5eae80e3fb155f
+ languageName: node
+ linkType: hard
+
"lru-cache@npm:^5.1.1":
version: 5.1.1
resolution: "lru-cache@npm:5.1.1"
@@ -12359,12 +12356,12 @@ __metadata:
languageName: node
linkType: hard
-"nanoid@npm:^3.3.7":
- version: 3.3.7
- resolution: "nanoid@npm:3.3.7"
+"nanoid@npm:^3.3.8":
+ version: 3.3.8
+ resolution: "nanoid@npm:3.3.8"
bin:
nanoid: bin/nanoid.cjs
- checksum: 10c0/e3fb661aa083454f40500473bb69eedb85dc160e763150b9a2c567c7e9ff560ce028a9f833123b618a6ea742e311138b591910e795614a629029e86e180660f3
+ checksum: 10c0/4b1bb29f6cfebf3be3bc4ad1f1296fb0a10a3043a79f34fbffe75d1621b4318319211cd420549459018ea3592f0d2f159247a6f874911d6d26eaaadda2478120
languageName: node
linkType: hard
@@ -12600,10 +12597,10 @@ __metadata:
languageName: node
linkType: hard
-"nwsapi@npm:^2.2.12, nwsapi@npm:^2.2.2":
- version: 2.2.12
- resolution: "nwsapi@npm:2.2.12"
- checksum: 10c0/95e9623d63df111405503df8c5d800e26f71675d319e2c9c70cddfa31e5ace1d3f8b6d98d354544fc156a1506d920ec291e303fab761e4f99296868e199a466e
+"nwsapi@npm:^2.2.16, nwsapi@npm:^2.2.2":
+ version: 2.2.16
+ resolution: "nwsapi@npm:2.2.16"
+ checksum: 10c0/0aa0637f4d51043d0183d994e08336bae996b03b42984381bf09ebdf3ff4909c018eda6b2a8aba0a08f3ea8303db8a0dad0608b38dc0bff15fd87017286ae21a
languageName: node
linkType: hard
@@ -13000,12 +12997,12 @@ __metadata:
languageName: node
linkType: hard
-"parse5@npm:^7.0.0, parse5@npm:^7.1.1, parse5@npm:^7.1.2":
- version: 7.1.2
- resolution: "parse5@npm:7.1.2"
+"parse5@npm:^7.0.0, parse5@npm:^7.1.1, parse5@npm:^7.2.1":
+ version: 7.2.1
+ resolution: "parse5@npm:7.2.1"
dependencies:
- entities: "npm:^4.4.0"
- checksum: 10c0/297d7af8224f4b5cb7f6617ecdae98eeaed7f8cbd78956c42785e230505d5a4f07cef352af10d3006fa5c1544b76b57784d3a22d861ae071bbc460c649482bf4
+ entities: "npm:^4.5.0"
+ checksum: 10c0/829d37a0c709215a887e410a7118d754f8e1afd7edb529db95bc7bbf8045fb0266a7b67801331d8e8d9d073ea75793624ec27ce9ff3b96862c3b9008f4d68e80
languageName: node
linkType: hard
@@ -13333,14 +13330,14 @@ __metadata:
linkType: hard
"pino-http@npm:^10.0.0":
- version: 10.3.0
- resolution: "pino-http@npm:10.3.0"
+ version: 10.4.0
+ resolution: "pino-http@npm:10.4.0"
dependencies:
get-caller-file: "npm:^2.0.5"
pino: "npm:^9.0.0"
pino-std-serializers: "npm:^7.0.0"
process-warning: "npm:^4.0.0"
- checksum: 10c0/da95d93e1176c02201f9b9bb0af53ad737105c5772acbb077dcad0f52ebce2438e0e9fc8216cd96396d1305d0ecf1f1d23142c7a50110a701ea093b2ee999ea7
+ checksum: 10c0/64144e2c94e939070f56ad82dfb012b6a98d21582e0660cf821e7cee64d4e06f7724aa40bc5bf9cd1254d58ab7cbd972dec287b7989eba647d384f6edd8d95fd
languageName: node
linkType: hard
@@ -14236,13 +14233,13 @@ __metadata:
linkType: hard
"postcss@npm:^8.2.15, postcss@npm:^8.4.24, postcss@npm:^8.4.49":
- version: 8.4.49
- resolution: "postcss@npm:8.4.49"
+ version: 8.5.1
+ resolution: "postcss@npm:8.5.1"
dependencies:
- nanoid: "npm:^3.3.7"
+ nanoid: "npm:^3.3.8"
picocolors: "npm:^1.1.1"
source-map-js: "npm:^1.2.1"
- checksum: 10c0/f1b3f17aaf36d136f59ec373459f18129908235e65dbdc3aee5eef8eba0756106f52de5ec4682e29a2eab53eb25170e7e871b3e4b52a8f1de3d344a514306be3
+ checksum: 10c0/c4d90c59c98e8a0c102b77d3f4cac190f883b42d63dc60e2f3ed840f16197c0c8e25a4327d2e9a847b45a985612317dc0534178feeebd0a1cf3eb0eecf75cae4
languageName: node
linkType: hard
@@ -14685,27 +14682,25 @@ __metadata:
languageName: node
linkType: hard
-"react-intl@npm:^6.4.2":
- version: 6.8.6
- resolution: "react-intl@npm:6.8.6"
+"react-intl@npm:^7.0.0":
+ version: 7.1.0
+ resolution: "react-intl@npm:7.1.0"
dependencies:
- "@formatjs/ecma402-abstract": "npm:2.2.3"
- "@formatjs/icu-messageformat-parser": "npm:2.9.3"
- "@formatjs/intl": "npm:2.10.13"
- "@formatjs/intl-displaynames": "npm:6.8.3"
- "@formatjs/intl-listformat": "npm:7.7.3"
+ "@formatjs/ecma402-abstract": "npm:2.3.2"
+ "@formatjs/icu-messageformat-parser": "npm:2.9.8"
+ "@formatjs/intl": "npm:3.1.0"
"@types/hoist-non-react-statics": "npm:3"
"@types/react": "npm:16 || 17 || 18"
hoist-non-react-statics: "npm:3"
- intl-messageformat: "npm:10.7.5"
+ intl-messageformat: "npm:10.7.11"
tslib: "npm:2"
peerDependencies:
react: ^16.6.0 || 17 || 18
- typescript: ^4.7 || 5
+ typescript: 5
peerDependenciesMeta:
typescript:
optional: true
- checksum: 10c0/ec88cc2b1d0edf089f04c8061ffda730840fb3317177d0dc1a6df208245ec278f52b4ca8546ff9511b0b28ff2d89863c5837a274d33731d4c8f75a3b3baafbe2
+ checksum: 10c0/9d69e316a5f5c6d31fa77f136b595079db2f75f63398cf8253d407878246dd5bcf0cc2eb4d7d4aa0646530ee58b16ce9a8c3876a5c2f0dc38fdda7e4f8c07615
languageName: node
linkType: hard
@@ -14971,15 +14966,15 @@ __metadata:
linkType: hard
"react-textarea-autosize@npm:^8.4.1":
- version: 8.5.6
- resolution: "react-textarea-autosize@npm:8.5.6"
+ version: 8.5.7
+ resolution: "react-textarea-autosize@npm:8.5.7"
dependencies:
"@babel/runtime": "npm:^7.20.13"
use-composed-ref: "npm:^1.3.0"
use-latest: "npm:^1.2.1"
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
- checksum: 10c0/652d290d316c55a253507ecf65ca27f2162801dace10c715f2241203e81d82e9de6d282095b758b26c6bc9e1af9ca552cab5c3a361b230e5fcf25bec31e1bd25
+ checksum: 10c0/ff004797ea28faca442460c42b30042d4c34a140f324eeeddee74508688dbc0f98966d21282c945630655006ad28a87edbcb59e6da7f9e762f4f3042c72f9f24
languageName: node
linkType: hard
@@ -15550,10 +15545,10 @@ __metadata:
languageName: node
linkType: hard
-"rrweb-cssom@npm:^0.7.1":
- version: 0.7.1
- resolution: "rrweb-cssom@npm:0.7.1"
- checksum: 10c0/127b8ca6c8aac45e2755abbae6138d4a813b1bedc2caabf79466ae83ab3cfc84b5bfab513b7033f0aa4561c7753edf787d0dd01163ceacdee2e8eb1b6bf7237e
+"rrweb-cssom@npm:^0.8.0":
+ version: 0.8.0
+ resolution: "rrweb-cssom@npm:0.8.0"
+ checksum: 10c0/56f2bfd56733adb92c0b56e274c43f864b8dd48784d6fe946ef5ff8d438234015e59ad837fc2ad54714b6421384141c1add4eb569e72054e350d1f8a50b8ac7b
languageName: node
linkType: hard
@@ -15652,8 +15647,8 @@ __metadata:
linkType: hard
"sass@npm:^1.62.1":
- version: 1.83.0
- resolution: "sass@npm:1.83.0"
+ version: 1.83.4
+ resolution: "sass@npm:1.83.4"
dependencies:
"@parcel/watcher": "npm:^2.4.1"
chokidar: "npm:^4.0.0"
@@ -15664,7 +15659,7 @@ __metadata:
optional: true
bin:
sass: sass.js
- checksum: 10c0/4415361229879a9041d77c953da85482e89032aa4321ba13250a9987d39c80fac6c88af3777f2a2d76a4e8b0c8afbd21c1970fdbe84e0b3ec25fb26741f92beb
+ checksum: 10c0/6f27f0eebfeb50222b14baaeef548ef58a05daf8abd9797e6c499334ed7ad40541767056c8693780d06ca83d8836348ea7396a923d3be439b133507993ca78be
languageName: node
linkType: hard
@@ -17872,11 +17867,11 @@ __metadata:
linkType: hard
"uuid@npm:^11.0.0":
- version: 11.0.3
- resolution: "uuid@npm:11.0.3"
+ version: 11.0.5
+ resolution: "uuid@npm:11.0.5"
bin:
uuid: dist/esm/bin/uuid
- checksum: 10c0/cee762fc76d949a2ff9205770334699e0043d52bb766472593a25f150077c9deed821230251ea3d6ab3943a5ea137d2826678797f1d5f6754c7ce5ce27e9f7a6
+ checksum: 10c0/6f59f0c605e02c14515401084ca124b9cb462b4dcac866916a49862bcf831874508a308588c23a7718269226ad11a92da29b39d761ad2b86e736623e3a33b6e7
languageName: node
linkType: hard
@@ -18294,13 +18289,13 @@ __metadata:
languageName: node
linkType: hard
-"whatwg-url@npm:^14.0.0":
- version: 14.0.0
- resolution: "whatwg-url@npm:14.0.0"
+"whatwg-url@npm:^14.0.0, whatwg-url@npm:^14.1.0":
+ version: 14.1.0
+ resolution: "whatwg-url@npm:14.1.0"
dependencies:
tr46: "npm:^5.0.0"
webidl-conversions: "npm:^7.0.0"
- checksum: 10c0/ac32e9ba9d08744605519bbe9e1371174d36229689ecc099157b6ba102d4251a95e81d81f3d80271eb8da182eccfa65653f07f0ab43ea66a6934e643fd091ba9
+ checksum: 10c0/f00104f1c67ce086ba8ffedab529cbbd9aefd8c0a6555320026de7aeff31f91c38680f95818b140a7c9cc657cde3781e567835dda552ddb1e2b8faaba0ac3cb6
languageName: node
linkType: hard