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/workflows/lint-haml.yml b/.github/workflows/lint-haml.yml
index c596261eb0..1dc0800dbb 100644
--- a/.github/workflows/lint-haml.yml
+++ b/.github/workflows/lint-haml.yml
@@ -46,4 +46,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/.rubocop/style.yml b/.rubocop/style.yml
index df1da2ba36..f59340d452 100644
--- a/.rubocop/style.yml
+++ b/.rubocop/style.yml
@@ -58,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 d128d3f679..14774acde0 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:
@@ -73,20 +73,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'
@@ -107,10 +98,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/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/Dockerfile b/Dockerfile
index 97c7a91499..deeac8b466 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -153,6 +153,7 @@ RUN \
libpq-dev \
libssl-dev \
libtool \
+ libyaml-dev \
meson \
nasm \
pkg-config \
diff --git a/Gemfile b/Gemfile
index 8df00d8c0a..5becc118d3 100644
--- a/Gemfile
+++ b/Gemfile
@@ -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'
@@ -104,19 +104,19 @@ gem 'opentelemetry-api', '~> 1.4.0'
group :opentelemetry do
gem 'opentelemetry-exporter-otlp', '~> 0.29.0', require: false
- gem 'opentelemetry-instrumentation-active_job', '~> 0.7.1', require: false
- 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.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
- gem 'opentelemetry-instrumentation-pg', '~> 0.29.0', require: false
- gem 'opentelemetry-instrumentation-rack', '~> 0.25.0', require: false
- gem 'opentelemetry-instrumentation-rails', '~> 0.34.0', require: false
- gem 'opentelemetry-instrumentation-redis', '~> 0.25.3', require: false
- gem 'opentelemetry-instrumentation-sidekiq', '~> 0.25.2', require: false
+ gem 'opentelemetry-instrumentation-active_job', '~> 0.8.0', require: false
+ gem 'opentelemetry-instrumentation-active_model_serializers', '~> 0.22.0', require: false
+ gem 'opentelemetry-instrumentation-concurrent_ruby', '~> 0.22.0', require: false
+ gem 'opentelemetry-instrumentation-excon', '~> 0.23.0', require: false
+ gem 'opentelemetry-instrumentation-faraday', '~> 0.26.0', require: false
+ gem 'opentelemetry-instrumentation-http', '~> 0.24.0', require: false
+ gem 'opentelemetry-instrumentation-http_client', '~> 0.23.0', require: false
+ gem 'opentelemetry-instrumentation-net_http', '~> 0.23.0', require: false
+ gem 'opentelemetry-instrumentation-pg', '~> 0.30.0', require: false
+ gem 'opentelemetry-instrumentation-rack', '~> 0.26.0', require: false
+ gem 'opentelemetry-instrumentation-rails', '~> 0.35.0', require: false
+ gem 'opentelemetry-instrumentation-redis', '~> 0.26.0', require: false
+ gem 'opentelemetry-instrumentation-sidekiq', '~> 0.26.0', require: false
gem 'opentelemetry-sdk', '~> 1.4', require: false
end
@@ -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 36373bd337..80c733d202 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,20 +94,20 @@ GEM
ast (2.4.2)
attr_required (1.0.2)
aws-eventstream (1.3.0)
- aws-partitions (1.1032.0)
- aws-sdk-core (3.214.1)
+ aws-partitions (1.1038.0)
+ aws-sdk-core (3.216.0)
aws-eventstream (~> 1, >= 1.3.0)
aws-partitions (~> 1, >= 1.992.0)
aws-sigv4 (~> 1.9)
jmespath (~> 1, >= 1.6.1)
- aws-sdk-kms (1.96.0)
- aws-sdk-core (~> 3, >= 3.210.0)
+ aws-sdk-kms (1.97.0)
+ aws-sdk-core (~> 3, >= 3.216.0)
aws-sigv4 (~> 1.5)
- aws-sdk-s3 (1.177.0)
- aws-sdk-core (~> 3, >= 3.210.0)
+ aws-sdk-s3 (1.178.0)
+ aws-sdk-core (~> 3, >= 3.216.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.5)
- aws-sigv4 (1.10.1)
+ aws-sigv4 (1.11.0)
aws-eventstream (~> 1, >= 1.0.2)
azure-blob (0.5.4)
rexml
@@ -159,7 +159,7 @@ GEM
climate_control (1.2.0)
cocoon (1.2.15)
color_diff (0.1)
- concurrent-ruby (1.3.4)
+ concurrent-ruby (1.3.5)
connection_pool (2.5.0)
cose (1.3.1)
cbor (~> 0.5.9)
@@ -473,77 +473,78 @@ GEM
opentelemetry-common (~> 0.20)
opentelemetry-sdk (~> 1.2)
opentelemetry-semantic_conventions
- opentelemetry-helpers-sql-obfuscation (0.2.1)
+ opentelemetry-helpers-sql-obfuscation (0.3.0)
opentelemetry-common (~> 0.21)
- opentelemetry-instrumentation-action_mailer (0.3.0)
+ opentelemetry-instrumentation-action_mailer (0.4.0)
opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-active_support (~> 0.7)
- opentelemetry-instrumentation-base (~> 0.22.1)
- opentelemetry-instrumentation-action_pack (0.10.0)
+ opentelemetry-instrumentation-base (~> 0.23.0)
+ opentelemetry-instrumentation-action_pack (0.11.0)
opentelemetry-api (~> 1.0)
- opentelemetry-instrumentation-base (~> 0.22.1)
+ opentelemetry-instrumentation-base (~> 0.23.0)
opentelemetry-instrumentation-rack (~> 0.21)
- opentelemetry-instrumentation-action_view (0.8.0)
+ opentelemetry-instrumentation-action_view (0.9.0)
opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-active_support (~> 0.7)
- opentelemetry-instrumentation-base (~> 0.22.1)
- opentelemetry-instrumentation-active_job (0.7.8)
+ opentelemetry-instrumentation-base (~> 0.23.0)
+ opentelemetry-instrumentation-active_job (0.8.0)
opentelemetry-api (~> 1.0)
- opentelemetry-instrumentation-base (~> 0.22.1)
- opentelemetry-instrumentation-active_model_serializers (0.21.1)
+ opentelemetry-instrumentation-base (~> 0.23.0)
+ opentelemetry-instrumentation-active_model_serializers (0.22.0)
opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-active_support (>= 0.7.0)
- opentelemetry-instrumentation-base (~> 0.22.1)
- opentelemetry-instrumentation-active_record (0.8.1)
+ opentelemetry-instrumentation-base (~> 0.23.0)
+ opentelemetry-instrumentation-active_record (0.9.0)
opentelemetry-api (~> 1.0)
- opentelemetry-instrumentation-base (~> 0.22.1)
- opentelemetry-instrumentation-active_support (0.7.0)
+ opentelemetry-instrumentation-base (~> 0.23.0)
+ opentelemetry-instrumentation-active_support (0.8.0)
opentelemetry-api (~> 1.0)
- opentelemetry-instrumentation-base (~> 0.22.1)
- opentelemetry-instrumentation-base (0.22.6)
+ opentelemetry-instrumentation-base (~> 0.23.0)
+ opentelemetry-instrumentation-base (0.23.0)
opentelemetry-api (~> 1.0)
opentelemetry-common (~> 0.21)
opentelemetry-registry (~> 0.1)
- opentelemetry-instrumentation-concurrent_ruby (0.21.4)
+ opentelemetry-instrumentation-concurrent_ruby (0.22.0)
opentelemetry-api (~> 1.0)
- opentelemetry-instrumentation-base (~> 0.22.1)
- opentelemetry-instrumentation-excon (0.22.5)
+ opentelemetry-instrumentation-base (~> 0.23.0)
+ opentelemetry-instrumentation-excon (0.23.0)
opentelemetry-api (~> 1.0)
- opentelemetry-instrumentation-base (~> 0.22.1)
- opentelemetry-instrumentation-faraday (0.25.0)
+ opentelemetry-instrumentation-base (~> 0.23.0)
+ opentelemetry-instrumentation-faraday (0.26.0)
opentelemetry-api (~> 1.0)
- opentelemetry-instrumentation-base (~> 0.22.1)
- opentelemetry-instrumentation-http (0.23.5)
+ opentelemetry-instrumentation-base (~> 0.23.0)
+ opentelemetry-instrumentation-http (0.24.0)
opentelemetry-api (~> 1.0)
- opentelemetry-instrumentation-base (~> 0.22.1)
- opentelemetry-instrumentation-http_client (0.22.8)
+ opentelemetry-instrumentation-base (~> 0.23.0)
+ opentelemetry-instrumentation-http_client (0.23.0)
opentelemetry-api (~> 1.0)
- opentelemetry-instrumentation-base (~> 0.22.1)
- opentelemetry-instrumentation-net_http (0.22.8)
+ opentelemetry-instrumentation-base (~> 0.23.0)
+ opentelemetry-instrumentation-net_http (0.23.0)
opentelemetry-api (~> 1.0)
- opentelemetry-instrumentation-base (~> 0.22.1)
- opentelemetry-instrumentation-pg (0.29.2)
+ opentelemetry-instrumentation-base (~> 0.23.0)
+ opentelemetry-instrumentation-pg (0.30.0)
opentelemetry-api (~> 1.0)
opentelemetry-helpers-sql-obfuscation
- opentelemetry-instrumentation-base (~> 0.22.1)
- opentelemetry-instrumentation-rack (0.25.0)
+ opentelemetry-instrumentation-base (~> 0.23.0)
+ opentelemetry-instrumentation-rack (0.26.0)
opentelemetry-api (~> 1.0)
- opentelemetry-instrumentation-base (~> 0.22.1)
- opentelemetry-instrumentation-rails (0.34.0)
+ opentelemetry-instrumentation-base (~> 0.23.0)
+ opentelemetry-instrumentation-rails (0.35.0)
opentelemetry-api (~> 1.0)
- opentelemetry-instrumentation-action_mailer (~> 0.3.0)
- opentelemetry-instrumentation-action_pack (~> 0.10.0)
- opentelemetry-instrumentation-action_view (~> 0.8.0)
- opentelemetry-instrumentation-active_job (~> 0.7.0)
- opentelemetry-instrumentation-active_record (~> 0.8.0)
- opentelemetry-instrumentation-active_support (~> 0.7.0)
- opentelemetry-instrumentation-base (~> 0.22.1)
- opentelemetry-instrumentation-redis (0.25.7)
+ opentelemetry-instrumentation-action_mailer (~> 0.4.0)
+ opentelemetry-instrumentation-action_pack (~> 0.11.0)
+ opentelemetry-instrumentation-action_view (~> 0.9.0)
+ opentelemetry-instrumentation-active_job (~> 0.8.0)
+ opentelemetry-instrumentation-active_record (~> 0.9.0)
+ opentelemetry-instrumentation-active_support (~> 0.8.0)
+ opentelemetry-instrumentation-base (~> 0.23.0)
+ opentelemetry-instrumentation-concurrent_ruby (~> 0.22.0)
+ opentelemetry-instrumentation-redis (0.26.0)
opentelemetry-api (~> 1.0)
- opentelemetry-instrumentation-base (~> 0.22.1)
- opentelemetry-instrumentation-sidekiq (0.25.7)
+ opentelemetry-instrumentation-base (~> 0.23.0)
+ opentelemetry-instrumentation-sidekiq (0.26.0)
opentelemetry-api (~> 1.0)
- opentelemetry-instrumentation-base (~> 0.22.1)
+ opentelemetry-instrumentation-base (~> 0.23.0)
opentelemetry-registry (0.3.1)
opentelemetry-api (~> 1.1)
opentelemetry-sdk (1.6.0)
@@ -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)
@@ -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)
@@ -959,19 +960,19 @@ DEPENDENCIES
omniauth_openid_connect (~> 0.6.1)
opentelemetry-api (~> 1.4.0)
opentelemetry-exporter-otlp (~> 0.29.0)
- opentelemetry-instrumentation-active_job (~> 0.7.1)
- 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.25.0)
- opentelemetry-instrumentation-http (~> 0.23.2)
- opentelemetry-instrumentation-http_client (~> 0.22.3)
- opentelemetry-instrumentation-net_http (~> 0.22.4)
- opentelemetry-instrumentation-pg (~> 0.29.0)
- opentelemetry-instrumentation-rack (~> 0.25.0)
- opentelemetry-instrumentation-rails (~> 0.34.0)
- opentelemetry-instrumentation-redis (~> 0.25.3)
- opentelemetry-instrumentation-sidekiq (~> 0.25.2)
+ opentelemetry-instrumentation-active_job (~> 0.8.0)
+ opentelemetry-instrumentation-active_model_serializers (~> 0.22.0)
+ opentelemetry-instrumentation-concurrent_ruby (~> 0.22.0)
+ opentelemetry-instrumentation-excon (~> 0.23.0)
+ opentelemetry-instrumentation-faraday (~> 0.26.0)
+ opentelemetry-instrumentation-http (~> 0.24.0)
+ opentelemetry-instrumentation-http_client (~> 0.23.0)
+ opentelemetry-instrumentation-net_http (~> 0.23.0)
+ opentelemetry-instrumentation-pg (~> 0.30.0)
+ opentelemetry-instrumentation-rack (~> 0.26.0)
+ opentelemetry-instrumentation-rails (~> 0.35.0)
+ opentelemetry-instrumentation-redis (~> 0.26.0)
+ opentelemetry-instrumentation-sidekiq (~> 0.26.0)
opentelemetry-sdk (~> 1.4)
ox (~> 2.14)
parslet
@@ -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
diff --git a/app/controllers/activitypub/collections_controller.rb b/app/controllers/activitypub/collections_controller.rb
index ab1b98e646..c80db3500d 100644
--- a/app/controllers/activitypub/collections_controller.rb
+++ b/app/controllers/activitypub/collections_controller.rb
@@ -49,7 +49,7 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController
def collection_presenter
ActivityPub::CollectionPresenter.new(
- id: account_collection_url(@account, params[:id]),
+ id: ActivityPub::TagManager.instance.collection_uri_for(@account, params[:id]),
type: @type,
size: @size,
items: @items
diff --git a/app/controllers/activitypub/outboxes_controller.rb b/app/controllers/activitypub/outboxes_controller.rb
index 658cec9a4d..171161d491 100644
--- a/app/controllers/activitypub/outboxes_controller.rb
+++ b/app/controllers/activitypub/outboxes_controller.rb
@@ -41,12 +41,8 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
end
end
- def outbox_url(**)
- if params[:account_username].present?
- account_outbox_url(@account, **)
- else
- instance_actor_outbox_url(**)
- end
+ def outbox_url(...)
+ ActivityPub::TagManager.instance.outbox_uri_for(@account, ...)
end
def next_page
diff --git a/app/controllers/api/v1/push/subscriptions_controller.rb b/app/controllers/api/v1/push/subscriptions_controller.rb
index e1ad89ee3e..d74b5d958f 100644
--- a/app/controllers/api/v1/push/subscriptions_controller.rb
+++ b/app/controllers/api/v1/push/subscriptions_controller.rb
@@ -21,6 +21,7 @@ class Api::V1::Push::SubscriptionsController < Api::BaseController
endpoint: subscription_params[:endpoint],
key_p256dh: subscription_params[:keys][:p256dh],
key_auth: subscription_params[:keys][:auth],
+ standard: subscription_params[:standard] || false,
data: data_params,
user_id: current_user.id,
access_token_id: doorkeeper_token.id
@@ -55,7 +56,7 @@ class Api::V1::Push::SubscriptionsController < Api::BaseController
end
def subscription_params
- params.require(:subscription).permit(:endpoint, keys: [:auth, :p256dh])
+ params.require(:subscription).permit(:endpoint, :standard, keys: [:auth, :p256dh])
end
def data_params
diff --git a/app/controllers/api/v2/notifications_controller.rb b/app/controllers/api/v2/notifications_controller.rb
index c070c0e5e7..cc38b95114 100644
--- a/app/controllers/api/v2/notifications_controller.rb
+++ b/app/controllers/api/v2/notifications_controller.rb
@@ -80,10 +80,31 @@ class Api::V2::NotificationsController < Api::BaseController
return [] if @notifications.empty?
MastodonOTELTracer.in_span('Api::V2::NotificationsController#load_grouped_notifications') do
- NotificationGroup.from_notifications(@notifications, pagination_range: (@notifications.last.id)..(@notifications.first.id), grouped_types: params[:grouped_types])
+ pagination_range = (@notifications.last.id)..@notifications.first.id
+
+ # If the page is incomplete, we know we are on the last page
+ if incomplete_page?
+ if paginating_up?
+ pagination_range = @notifications.last.id...(params[:max_id]&.to_i)
+ else
+ range_start = params[:since_id]&.to_i
+ range_start += 1 unless range_start.nil?
+ pagination_range = range_start..(@notifications.first.id)
+ end
+ end
+
+ NotificationGroup.from_notifications(@notifications, pagination_range: pagination_range, grouped_types: params[:grouped_types])
end
end
+ def incomplete_page?
+ @notifications.size < limit_param(DEFAULT_NOTIFICATIONS_LIMIT)
+ end
+
+ def paginating_up?
+ params[:min_id].present?
+ end
+
def browserable_account_notifications
current_account.notifications.without_suspended.browserable(
types: Array(browserable_params[:types]),
diff --git a/app/controllers/api/web/push_subscriptions_controller.rb b/app/controllers/api/web/push_subscriptions_controller.rb
index f515961427..7eb51c6846 100644
--- a/app/controllers/api/web/push_subscriptions_controller.rb
+++ b/app/controllers/api/web/push_subscriptions_controller.rb
@@ -66,7 +66,7 @@ class Api::Web::PushSubscriptionsController < Api::Web::BaseController
end
def subscription_params
- @subscription_params ||= params.require(:subscription).permit(:endpoint, keys: [:auth, :p256dh])
+ @subscription_params ||= params.require(:subscription).permit(:standard, :endpoint, keys: [:auth, :p256dh])
end
def web_push_subscription_params
@@ -76,6 +76,7 @@ class Api::Web::PushSubscriptionsController < Api::Web::BaseController
endpoint: subscription_params[:endpoint],
key_auth: subscription_params[:keys][:auth],
key_p256dh: subscription_params[:keys][:p256dh],
+ standard: subscription_params[:standard] || false,
user_id: active_session.user_id,
}
end
diff --git a/app/controllers/auth/setup_controller.rb b/app/controllers/auth/setup_controller.rb
index ad872dc607..376a30c16f 100644
--- a/app/controllers/auth/setup_controller.rb
+++ b/app/controllers/auth/setup_controller.rb
@@ -18,7 +18,7 @@ class Auth::SetupController < ApplicationController
if @user.update(user_params)
@user.resend_confirmation_instructions unless @user.confirmed?
- redirect_to auth_setup_path, notice: I18n.t('auth.setup.new_confirmation_instructions_sent')
+ redirect_to auth_setup_path, notice: t('auth.setup.new_confirmation_instructions_sent')
else
render :show
end
diff --git a/app/controllers/follower_accounts_controller.rb b/app/controllers/follower_accounts_controller.rb
index 44d90ec671..85f6ccc5e4 100644
--- a/app/controllers/follower_accounts_controller.rb
+++ b/app/controllers/follower_accounts_controller.rb
@@ -46,7 +46,7 @@ class FollowerAccountsController < ApplicationController
end
def page_url(page)
- account_followers_url(@account, page: page) unless page.nil?
+ ActivityPub::TagManager.instance.followers_uri_for(@account, page: page) unless page.nil?
end
def next_page_url
diff --git a/app/helpers/jsonld_helper.rb b/app/helpers/json_ld_helper.rb
similarity index 100%
rename from app/helpers/jsonld_helper.rb
rename to app/helpers/json_ld_helper.rb
diff --git a/app/helpers/theme_helper.rb b/app/helpers/theme_helper.rb
index 7e4fd0a002..f4d88a1ef0 100644
--- a/app/helpers/theme_helper.rb
+++ b/app/helpers/theme_helper.rb
@@ -63,7 +63,9 @@ module ThemeHelper
end
def cached_custom_css_digest
- Rails.cache.read(:setting_digest_custom_css)
+ Rails.cache.fetch(:setting_digest_custom_css) do
+ Setting.custom_css&.then { |content| Digest::SHA256.hexdigest(content) }
+ end
end
def theme_color_for(theme)
diff --git a/app/javascript/mastodon/actions/push_notifications/registerer.js b/app/javascript/mastodon/actions/push_notifications/registerer.js
index b3d3850e31..647a6bd9fb 100644
--- a/app/javascript/mastodon/actions/push_notifications/registerer.js
+++ b/app/javascript/mastodon/actions/push_notifications/registerer.js
@@ -33,7 +33,7 @@ const unsubscribe = ({ registration, subscription }) =>
subscription ? subscription.unsubscribe().then(() => registration) : registration;
const sendSubscriptionToBackend = (subscription) => {
- const params = { subscription };
+ const params = { subscription: { ...subscription.toJSON(), standard: true } };
if (me) {
const data = pushNotificationsSetting.get(me);
diff --git a/app/javascript/mastodon/api_types/accounts.ts b/app/javascript/mastodon/api_types/accounts.ts
index 80d575cad6..9d7974eda0 100644
--- a/app/javascript/mastodon/api_types/accounts.ts
+++ b/app/javascript/mastodon/api_types/accounts.ts
@@ -45,7 +45,7 @@ export interface BaseApiAccountJSON {
avatar_static: string;
bot: boolean;
created_at: string;
- discoverable: boolean;
+ discoverable?: boolean;
indexable: boolean;
display_name: string;
emojis: ApiCustomEmojiJSON[];
diff --git a/app/javascript/mastodon/features/getting_started/components/announcements.jsx b/app/javascript/mastodon/features/getting_started/components/announcements.jsx
index 713ad9f069..ad66d2e5fa 100644
--- a/app/javascript/mastodon/features/getting_started/components/announcements.jsx
+++ b/app/javascript/mastodon/features/getting_started/components/announcements.jsx
@@ -335,15 +335,29 @@ class Announcement extends ImmutablePureComponent {
const endsAt = announcement.get('ends_at') && new Date(announcement.get('ends_at'));
const now = new Date();
const hasTimeRange = startsAt && endsAt;
- const skipYear = hasTimeRange && startsAt.getFullYear() === endsAt.getFullYear() && endsAt.getFullYear() === now.getFullYear();
- const skipEndDate = hasTimeRange && startsAt.getDate() === endsAt.getDate() && startsAt.getMonth() === endsAt.getMonth() && startsAt.getFullYear() === endsAt.getFullYear();
const skipTime = announcement.get('all_day');
+ let timestamp = null;
+ if (hasTimeRange) {
+ const skipYear = startsAt.getFullYear() === endsAt.getFullYear() && endsAt.getFullYear() === now.getFullYear();
+ const skipEndDate = startsAt.getDate() === endsAt.getDate() && startsAt.getMonth() === endsAt.getMonth() && startsAt.getFullYear() === endsAt.getFullYear();
+ timestamp = (
+ <>
+
rel="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.
@@ -1916,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/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/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 0f757c6370..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,16 +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ēts %{date}
+ published_on_html: Publicēti %{date}
+ save_draft: Saglabāt melnrakstu
+ title: Pakalpojuma izmantošanas noteikumi
title: Pārvaldība
trends:
allow: Atļaut
@@ -1157,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}
@@ -1777,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)
@@ -1826,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/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 0bfeacd9cd..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
@@ -130,8 +133,17 @@ cs:
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
@@ -144,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í
@@ -222,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
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 be2ed2135e..59fad146d4 100644
--- a/config/locales/simple_form.eo.yml
+++ b/config/locales/simple_form.eo.yml
@@ -140,6 +140,7 @@ eo:
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.
@@ -333,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.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.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 5fd258d7e0..2d2c1c0060 100644
--- a/config/locales/simple_form.sv.yml
+++ b/config/locales/simple_form.sv.yml
@@ -134,7 +134,9 @@ sv:
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
@@ -157,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
@@ -330,6 +332,9 @@ sv:
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/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/mastodon.yml b/config/mastodon.yml
index 2822afebe3..2a3f642795 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://kmy.blue/update-check') %>
+ source:
+ base_url: <%= ENV.fetch('SOURCE_BASE_URL', nil) %>
+ repository: <%= ENV.fetch('GITHUB_REPOSITORY', 'kmycode/mastodon') %>
+ tag: <%= ENV.fetch('SOURCE_TAG', nil) %>
+ version:
+ metadata: <%= ENV.fetch('MASTODON_VERSION_METADATA', nil) %>
+ prerelease: <%= ENV.fetch('MASTODON_VERSION_PRERELEASE', nil) %>
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 a8406a46ce..08cdfeffc9 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[8.0].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 "pg_catalog.plpgsql"
@@ -1573,6 +1573,7 @@ ActiveRecord::Schema[8.0].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/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 12611319c3..39b0efb7f1 100644
--- a/lib/mastodon/version.rb
+++ b/lib/mastodon/version.rb
@@ -35,11 +35,11 @@ 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 to_a_of_kmyblue
@@ -63,7 +63,7 @@ module Mastodon
end
def build_metadata_of_mastodon
- ENV.fetch('MASTODON_VERSION_METADATA', nil)
+ version_configuration[:metadata]
end
def to_a
@@ -102,16 +102,16 @@ module Mastodon
end
def repository
- ENV.fetch('GITHUB_REPOSITORY', 'kmycode/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
@@ -129,5 +129,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/lib/tasks/mastodon.rake b/lib/tasks/mastodon.rake
index b72c6387f7..0e00bf47e5 100644
--- a/lib/tasks/mastodon.rake
+++ b/lib/tasks/mastodon.rake
@@ -8,16 +8,10 @@ namespace :mastodon do
prompt = TTY::Prompt.new
env = {}
- # When the application code gets loaded, it runs `lib/mastodon/redis_configuration.rb`.
- # This happens before application environment configuration and sets REDIS_URL etc.
- # These variables are then used even when REDIS_HOST etc. are changed, so clear them
- # out so they don't interfere with our new configuration.
- ENV.delete('REDIS_URL')
- ENV.delete('CACHE_REDIS_URL')
- ENV.delete('SIDEKIQ_REDIS_URL')
+ clear_environment!
begin
- errors = false
+ errors = []
prompt.say('Your instance is identified by its domain name. Changing it afterward will break things.')
env['LOCAL_DOMAIN'] = prompt.ask('Domain name:') do |q|
@@ -109,7 +103,7 @@ namespace :mastodon do
unless prompt.yes?('Try again?')
return prompt.warn 'Nothing saved. Bye!' unless prompt.yes?('Continue anyway?')
- errors = true
+ errors << 'Database connection could not be established.'
break
end
end
@@ -155,7 +149,7 @@ namespace :mastodon do
unless prompt.yes?('Try again?')
return prompt.warn 'Nothing saved. Bye!' unless prompt.yes?('Continue anyway?')
- errors = true
+ errors << 'Redis connection could not be established.'
break
end
end
@@ -450,7 +444,7 @@ namespace :mastodon do
unless prompt.yes?('Try again?')
return prompt.warn 'Nothing saved. Bye!' unless prompt.yes?('Continue anyway?')
- errors = true
+ errors << 'E-email was not sent successfully.'
break
end
end
@@ -498,7 +492,7 @@ namespace :mastodon do
prompt.ok 'Done!'
else
prompt.error 'That failed! Perhaps your configuration is not right'
- errors = true
+ errors << 'Preparing the database failed'
end
end
@@ -515,14 +509,15 @@ namespace :mastodon do
prompt.say 'Done!'
else
prompt.error 'That failed! Maybe you need swap space?'
- errors = true
+ errors << 'Compiling assets failed.'
end
end
end
prompt.say "\n"
- if errors
- prompt.warn 'Your Mastodon server is set up, but there were some errors along the way, you may have to fix them.'
+ if errors.any?
+ prompt.warn 'Your Mastodon server is set up, but there were some errors along the way, you may have to fix them:'
+ errors.each { |error| prompt.warn "- #{error}" }
else
prompt.ok 'All done! You can now power on the Mastodon server 🐘'
end
@@ -579,6 +574,17 @@ namespace :mastodon do
private
+ def clear_environment!
+ # When the application code gets loaded, it runs `lib/mastodon/redis_configuration.rb`.
+ # This happens before application environment configuration and sets REDIS_URL etc.
+ # These variables are then used even when REDIS_HOST etc. are changed, so clear them
+ # out so they don't interfere with our new configuration.
+
+ ENV.delete('REDIS_URL')
+ ENV.delete('CACHE_REDIS_URL')
+ ENV.delete('SIDEKIQ_REDIS_URL')
+ end
+
def generate_header(include_warning)
default_message = "# Generated with mastodon:setup on #{Time.now.utc}\n\n"
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 ee9b45d8f4..454cb42ce5 100644
--- a/package.json
+++ b/package.json
@@ -104,7 +104,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",
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/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb
index 4ee951628e..2e7a59db05 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/helpers/theme_helper_spec.rb b/spec/helpers/theme_helper_spec.rb
index c811b7c981..51611b8211 100644
--- a/spec/helpers/theme_helper_spec.rb
+++ b/spec/helpers/theme_helper_spec.rb
@@ -80,18 +80,37 @@ RSpec.describe ThemeHelper do
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, '1a2s3d4f1a2s3d4f') }
+ before { Rails.cache.write(:setting_digest_custom_css, custom_digest) }
it 'returns value from settings' do
expect(custom_stylesheet)
- .to match('/css/custom-1a2s3d4f.css')
+ .to match("/css/custom-#{custom_digest[...8]}.css")
end
end
- context 'when custom css setting value digest is not present' do
+ 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
diff --git a/spec/lib/activitypub/activity/create_spec.rb b/spec/lib/activitypub/activity/create_spec.rb
index f302c893b4..f5528f13a7 100644
--- a/spec/lib/activitypub/activity/create_spec.rb
+++ b/spec/lib/activitypub/activity/create_spec.rb
@@ -182,12 +182,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
@@ -204,12 +201,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
@@ -226,13 +220,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
@@ -252,13 +243,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
@@ -274,11 +262,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
@@ -287,13 +273,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)
@@ -316,12 +296,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
@@ -336,12 +313,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
@@ -356,12 +330,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
@@ -376,12 +347,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
@@ -396,12 +364,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
@@ -416,12 +381,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
@@ -479,12 +441,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
@@ -499,16 +458,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
@@ -525,12 +481,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
@@ -597,16 +550,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' do
@@ -921,12 +871,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
@@ -946,17 +893,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
@@ -971,16 +915,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
@@ -1301,10 +1242,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',
@@ -1316,8 +1254,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
@@ -1333,10 +1271,7 @@ 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',
@@ -1344,8 +1279,8 @@ RSpec.describe ActivityPub::Activity::Create do
url: 'http://example.com/attachment.png',
name: '*' * MediaAttachment::MAX_DESCRIPTION_LENGTH,
},
- ],
- }
+ ]
+ )
end
it 'creates status' do
@@ -1360,10 +1295,7 @@ RSpec.describe ActivityPub::Activity::Create do
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',
@@ -1371,8 +1303,8 @@ RSpec.describe ActivityPub::Activity::Create do
url: 'http://example.com/attachment.png',
summary: '*' * MediaAttachment::MAX_DESCRIPTION_LENGTH,
},
- ],
- }
+ ]
+ )
end
it 'creates status' do
@@ -1387,10 +1319,7 @@ RSpec.describe ActivityPub::Activity::Create do
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',
@@ -1398,8 +1327,8 @@ RSpec.describe ActivityPub::Activity::Create do
url: 'http://example.com/attachment.png',
focalPoint: [0.5, -0.7],
},
- ],
- }
+ ]
+ )
end
it 'creates status' do
@@ -1414,17 +1343,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
@@ -1437,18 +1363,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
@@ -1463,10 +1386,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: [
{
@@ -1474,8 +1394,8 @@ RSpec.describe ActivityPub::Activity::Create do
href: 'http://example.com/blah',
name: '#test',
},
- ],
- }
+ ]
+ )
end
before do
@@ -1512,17 +1432,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
@@ -1535,18 +1452,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
@@ -1559,9 +1473,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: [
{
@@ -1571,8 +1483,8 @@ RSpec.describe ActivityPub::Activity::Create do
},
name: 'tinking',
},
- ],
- }
+ ]
+ )
end
it 'creates status' do
@@ -1587,9 +1499,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: [
{
@@ -1599,8 +1509,8 @@ RSpec.describe ActivityPub::Activity::Create do
},
name: 'tinkong',
},
- ],
- }
+ ]
+ )
end
it 'creates status' do
@@ -1615,9 +1525,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: [
{
@@ -1626,8 +1534,8 @@ RSpec.describe ActivityPub::Activity::Create do
url: 'http://example.com/emoji.png',
},
},
- ],
- }
+ ]
+ )
end
it 'creates status' do
@@ -1640,17 +1548,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
@@ -1663,8 +1569,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: [
@@ -1682,8 +1587,8 @@ RSpec.describe ActivityPub::Activity::Create do
totalItems: 3,
},
},
- ],
- }
+ ]
+ )
end
it 'creates status with a poll' do
@@ -1706,12 +1611,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
@@ -1759,12 +1662,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
@@ -2509,10 +2410,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',
@@ -2522,8 +2420,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
@@ -2552,12 +2450,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
@@ -2623,13 +2518,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
@@ -2644,12 +2533,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
@@ -2715,13 +2601,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
@@ -2741,12 +2625,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
@@ -2797,17 +2678,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 2bc7820542..d4e6df09ce 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/annual_report_spec.rb b/spec/lib/annual_report_spec.rb
index bd4d0f3387..fa898d3ac5 100644
--- a/spec/lib/annual_report_spec.rb
+++ b/spec/lib/annual_report_spec.rb
@@ -13,4 +13,13 @@ RSpec.describe AnnualReport do
.to change(GeneratedAnnualReport, :count).by(1)
end
end
+
+ describe '.prepare' do
+ before { Fabricate :status }
+
+ it 'generates records from source class which prepare data' do
+ expect { described_class.prepare(Time.current.year) }
+ .to change(AnnualReport::StatusesPerAccountCount, :count).by(1)
+ 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 fe14c9093a..588c0da93e 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,16 @@ RSpec.describe Account do
end
describe '#local?' do
- it 'returns true when domain is null' do
- account = Fabricate(:account, domain: nil)
- expect(account).to be_local
+ context 'when the domain is null' do
+ subject { Fabricate.build :account, domain: nil }
+
+ it { is_expected.to be_local }
end
- it 'returns false when domain is present' do
- account = Fabricate(:account, domain: 'foreign.tld')
- expect(account).to_not be_local
+ context 'when the domain is present' do
+ subject { Fabricate.build :account, domain: 'host.example' }
+
+ it { is_expected.to_not be_local }
end
end
@@ -66,12 +69,6 @@ RSpec.describe Account do
it { is_expected.to_not be_remote }
end
- context 'when the domain is blank' do
- subject { Fabricate.build :account, domain: '' }
-
- it { is_expected.to_not be_remote }
- end
-
context 'when the domain is present' do
subject { Fabricate.build :account, domain: 'host.example' }
@@ -79,6 +76,20 @@ RSpec.describe Account do
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
+
describe 'Local domain user methods' do
subject { Fabricate(:account, domain: nil, username: 'alice') }
@@ -540,298 +551,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
-
- context 'when limiting search to follower accounts' do
- it 'accepts ?, \, : and space as delimiter' do
- match = Fabricate(
- :account,
- display_name: 'A & l & i & c & e',
- username: 'username',
- domain: 'example.com'
- )
- match.follow!(account)
-
- results = described_class.advanced_search_for('A?l\i:c e', account, limit: 10, follower: true)
- expect(results).to eq [match]
- end
-
- it 'does not return non-follower 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, follower: 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) }
@@ -1030,6 +749,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 }
@@ -1068,6 +789,11 @@ RSpec.describe Account do
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
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 06775f5dd6..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: (described_class::MAX_STRIKE_AGE * 2).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/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/status_spec.rb b/spec/models/status_spec.rb
index 1e29a33ffc..13acb7d909 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,178 +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 '#compute_searchability' do
- subject { Fabricate(:status, account: account, searchability: status_searchability) }
-
- let(:account_searchability) { :public }
- let(:status_searchability) { :public }
- let(:account_domain) { 'example.com' }
- let(:silenced_at) { nil }
- let(:account) { Fabricate(:account, domain: account_domain, searchability: account_searchability, silenced_at: silenced_at) }
-
- context 'when public-public' do
- it 'returns public' do
- expect(subject.compute_searchability).to eq 'public'
- end
- end
-
- context 'when public-public but silenced' do
- let(:silenced_at) { Time.now.utc }
-
- it 'returns private' do
- expect(subject.compute_searchability).to eq 'private'
- end
- end
-
- context 'when public-public_unlisted but silenced' do
- let(:silenced_at) { Time.now.utc }
- let(:status_searchability) { :public_unlisted }
-
- it 'returns private' do
- expect(subject.compute_searchability).to eq 'private'
- end
- end
-
- context 'when public-public_unlisted' do
- let(:status_searchability) { :public_unlisted }
-
- it 'returns public' do
- expect(subject.compute_searchability).to eq 'public'
- end
-
- it 'returns public_unlisted for local' do
- expect(subject.compute_searchability_local).to eq 'public_unlisted'
- end
- end
-
- context 'when public-private' do
- let(:status_searchability) { :private }
-
- it 'returns private' do
- expect(subject.compute_searchability).to eq 'private'
- end
- end
-
- context 'when public-direct' do
- let(:status_searchability) { :direct }
-
- it 'returns direct' do
- expect(subject.compute_searchability).to eq 'direct'
- end
- end
-
- context 'when private-public' do
- let(:account_searchability) { :private }
-
- it 'returns private' do
- expect(subject.compute_searchability).to eq 'private'
- end
- end
-
- context 'when direct-public' do
- let(:account_searchability) { :direct }
-
- it 'returns direct' do
- expect(subject.compute_searchability).to eq 'direct'
- end
- end
-
- context 'when limited-public' do
- let(:account_searchability) { :limited }
-
- it 'returns limited' do
- expect(subject.compute_searchability).to eq 'limited'
- end
- end
-
- context 'when private-limited' do
- let(:account_searchability) { :private }
- let(:status_searchability) { :limited }
-
- it 'returns limited' do
- expect(subject.compute_searchability).to eq 'limited'
- end
- end
-
- context 'when private-public of local account' do
- let(:account_searchability) { :private }
- let(:account_domain) { nil }
- let(:status_searchability) { :public }
-
- it 'returns public' do
- expect(subject.compute_searchability).to eq 'public'
- end
- end
-
- context 'when direct-public of local account' do
- let(:account_searchability) { :direct }
- let(:account_domain) { nil }
- let(:status_searchability) { :public }
-
- it 'returns public' do
- expect(subject.compute_searchability).to eq 'public'
- end
- end
-
- context 'when limited-public of local account' do
- let(:account_searchability) { :limited }
- let(:account_domain) { nil }
- let(:status_searchability) { :public }
-
- it 'returns public' do
- expect(subject.compute_searchability).to eq 'public'
- end
- end
-
- context 'when public-public_unlisted of local account' do
- let(:account_searchability) { :public }
- let(:account_domain) { nil }
- let(:status_searchability) { :public_unlisted }
-
- it 'returns public' do
- expect(subject.compute_searchability).to eq 'public'
- end
-
- it 'returns public_unlisted for local' do
- expect(subject.compute_searchability_local).to eq 'public_unlisted'
- end
-
- it 'returns private for activitypub' do
- expect(subject.compute_searchability_activitypub).to eq 'private'
- end
- end
- end
-
describe '#quote' do
let(:target_status) { Fabricate(:status) }
let(:quote) { true }
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 999c855867..3d522eedcd 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 c1d741baf2..b066391e4c 100644
--- a/spec/models/trends/tags_spec.rb
+++ b/spec/models/trends/tags_spec.rb
@@ -56,6 +56,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/presenters/instance_presenter_spec.rb b/spec/presenters/instance_presenter_spec.rb
index 51b034e674..1c295134c9 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/kmycode/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/admin/domain_blocks_spec.rb b/spec/requests/api/v1/admin/domain_blocks_spec.rb
index 7e37300a77..c43f69b7d6 100644
--- a/spec/requests/api/v1/admin/domain_blocks_spec.rb
+++ b/spec/requests/api/v1/admin/domain_blocks_spec.rb
@@ -258,6 +258,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/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/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/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/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/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..4fcf4759c5
--- /dev/null
+++ b/spec/support/examples/models/concerns/account/search.rb
@@ -0,0 +1,295 @@
+# 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
+
+ context 'when limiting search to follower accounts' do
+ it 'accepts ?, \, : and space as delimiter' do
+ match = Fabricate(
+ :account,
+ display_name: 'A & l & i & c & e',
+ username: 'username',
+ domain: 'example.com'
+ )
+ match.follow!(account)
+
+ results = described_class.advanced_search_for('A?l\i:c e', account, limit: 10, follower: true)
+ expect(results).to eq [match]
+ end
+
+ it 'does not return non-follower 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, follower: 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..7b26b8fdeb
--- /dev/null
+++ b/spec/support/examples/models/concerns/status/visibility.rb
@@ -0,0 +1,326 @@
+# 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
+
+ describe '#compute_searchability' do
+ subject { Fabricate(:status, account: account, searchability: status_searchability) }
+
+ let(:account_searchability) { :public }
+ let(:status_searchability) { :public }
+ let(:account_domain) { 'example.com' }
+ let(:silenced_at) { nil }
+ let(:account) { Fabricate(:account, domain: account_domain, searchability: account_searchability, silenced_at: silenced_at) }
+
+ context 'when public-public' do
+ it 'returns public' do
+ expect(subject.compute_searchability).to eq 'public'
+ end
+ end
+
+ context 'when public-public but silenced' do
+ let(:silenced_at) { Time.now.utc }
+
+ it 'returns private' do
+ expect(subject.compute_searchability).to eq 'private'
+ end
+ end
+
+ context 'when public-public_unlisted but silenced' do
+ let(:silenced_at) { Time.now.utc }
+ let(:status_searchability) { :public_unlisted }
+
+ it 'returns private' do
+ expect(subject.compute_searchability).to eq 'private'
+ end
+ end
+
+ context 'when public-public_unlisted' do
+ let(:status_searchability) { :public_unlisted }
+
+ it 'returns public' do
+ expect(subject.compute_searchability).to eq 'public'
+ end
+
+ it 'returns public_unlisted for local' do
+ expect(subject.compute_searchability_local).to eq 'public_unlisted'
+ end
+ end
+
+ context 'when public-private' do
+ let(:status_searchability) { :private }
+
+ it 'returns private' do
+ expect(subject.compute_searchability).to eq 'private'
+ end
+ end
+
+ context 'when public-direct' do
+ let(:status_searchability) { :direct }
+
+ it 'returns direct' do
+ expect(subject.compute_searchability).to eq 'direct'
+ end
+ end
+
+ context 'when private-public' do
+ let(:account_searchability) { :private }
+
+ it 'returns private' do
+ expect(subject.compute_searchability).to eq 'private'
+ end
+ end
+
+ context 'when direct-public' do
+ let(:account_searchability) { :direct }
+
+ it 'returns direct' do
+ expect(subject.compute_searchability).to eq 'direct'
+ end
+ end
+
+ context 'when limited-public' do
+ let(:account_searchability) { :limited }
+
+ it 'returns limited' do
+ expect(subject.compute_searchability).to eq 'limited'
+ end
+ end
+
+ context 'when private-limited' do
+ let(:account_searchability) { :private }
+ let(:status_searchability) { :limited }
+
+ it 'returns limited' do
+ expect(subject.compute_searchability).to eq 'limited'
+ end
+ end
+
+ context 'when private-public of local account' do
+ let(:account_searchability) { :private }
+ let(:account_domain) { nil }
+ let(:status_searchability) { :public }
+
+ it 'returns public' do
+ expect(subject.compute_searchability).to eq 'public'
+ end
+ end
+
+ context 'when direct-public of local account' do
+ let(:account_searchability) { :direct }
+ let(:account_domain) { nil }
+ let(:status_searchability) { :public }
+
+ it 'returns public' do
+ expect(subject.compute_searchability).to eq 'public'
+ end
+ end
+
+ context 'when limited-public of local account' do
+ let(:account_searchability) { :limited }
+ let(:account_domain) { nil }
+ let(:status_searchability) { :public }
+
+ it 'returns public' do
+ expect(subject.compute_searchability).to eq 'public'
+ end
+ end
+
+ context 'when public-public_unlisted of local account' do
+ let(:account_searchability) { :public }
+ let(:account_domain) { nil }
+ let(:status_searchability) { :public_unlisted }
+
+ it 'returns public' do
+ expect(subject.compute_searchability).to eq 'public'
+ end
+
+ it 'returns public_unlisted for local' do
+ expect(subject.compute_searchability_local).to eq 'public_unlisted'
+ end
+
+ it 'returns private for activitypub' do
+ expect(subject.compute_searchability_activitypub).to eq 'private'
+ end
+ 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/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/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 1bd395a284..752352d1d4 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,17 +2280,6 @@ __metadata:
languageName: node
linkType: hard
-"@formatjs/ecma402-abstract@npm:2.2.4":
- version: 2.2.4
- resolution: "@formatjs/ecma402-abstract@npm:2.2.4"
- dependencies:
- "@formatjs/fast-memoize": "npm:2.2.3"
- "@formatjs/intl-localematcher": "npm:0.5.8"
- tslib: "npm:2"
- checksum: 10c0/3f262533fa704ea7a1a7a8107deee2609774a242c621f8cb5dd4bf4c97abf2fc12f5aeda3f4ce85be18147c484a0ca87303dca6abef53290717e685c55eabd2d
- languageName: node
- linkType: hard
-
"@formatjs/ecma402-abstract@npm:2.3.2":
version: 2.3.2
resolution: "@formatjs/ecma402-abstract@npm:2.3.2"
@@ -2299,15 +2301,6 @@ __metadata:
languageName: node
linkType: hard
-"@formatjs/fast-memoize@npm:2.2.3":
- version: 2.2.3
- resolution: "@formatjs/fast-memoize@npm:2.2.3"
- dependencies:
- tslib: "npm:2"
- checksum: 10c0/f1004c3b280de7e362bd37c5f48ff34c2ba1d6271d4a7b695fed561d1201a3379397824d8bffbf15fecee344d1e70398393bbb04297f242692310a305f12e75b
- languageName: node
- linkType: hard
-
"@formatjs/fast-memoize@npm:2.2.6":
version: 2.2.6
resolution: "@formatjs/fast-memoize@npm:2.2.6"
@@ -2328,17 +2321,6 @@ __metadata:
languageName: node
linkType: hard
-"@formatjs/icu-messageformat-parser@npm:2.9.4":
- version: 2.9.4
- resolution: "@formatjs/icu-messageformat-parser@npm:2.9.4"
- dependencies:
- "@formatjs/ecma402-abstract": "npm:2.2.4"
- "@formatjs/icu-skeleton-parser": "npm:1.8.8"
- tslib: "npm:2"
- checksum: 10c0/f1ed14ece7ef0abc9fb62e323b78c994fc772d346801ad5aaa9555e1a7d5c0fda791345f4f2e53a3223f0b82c1a4eaf9a83544c1c20cb39349d1a39bedcf1648
- languageName: node
- linkType: hard
-
"@formatjs/icu-messageformat-parser@npm:2.9.8":
version: 2.9.8
resolution: "@formatjs/icu-messageformat-parser@npm:2.9.8"
@@ -2370,38 +2352,6 @@ __metadata:
languageName: node
linkType: hard
-"@formatjs/icu-skeleton-parser@npm:1.8.8":
- version: 1.8.8
- resolution: "@formatjs/icu-skeleton-parser@npm:1.8.8"
- dependencies:
- "@formatjs/ecma402-abstract": "npm:2.2.4"
- tslib: "npm:2"
- checksum: 10c0/5ad78a5682e83b973e6fed4fca68660b944c41d1e941f0c84d69ff3d10ae835330062dc0a2cf0d237d2675ad3463405061a3963c14c2b9d8d1c1911f892b1a8d
- languageName: node
- linkType: hard
-
-"@formatjs/intl-displaynames@npm:6.8.5":
- version: 6.8.5
- resolution: "@formatjs/intl-displaynames@npm:6.8.5"
- dependencies:
- "@formatjs/ecma402-abstract": "npm:2.2.4"
- "@formatjs/intl-localematcher": "npm:0.5.8"
- tslib: "npm:2"
- checksum: 10c0/1092d6bac9ba7ee22470b85c9af16802244aa8a54f07e6cd560d15b96e8a08fc359f20dee88a064fe4c9ca8860f439abb109cbb7977b9ccceb846e28aacdf29c
- languageName: node
- linkType: hard
-
-"@formatjs/intl-listformat@npm:7.7.5":
- version: 7.7.5
- resolution: "@formatjs/intl-listformat@npm:7.7.5"
- dependencies:
- "@formatjs/ecma402-abstract": "npm:2.2.4"
- "@formatjs/intl-localematcher": "npm:0.5.8"
- tslib: "npm:2"
- checksum: 10c0/f514397f6b05ac29171fffbbd15636fbec086080058c79c159f24edd2038747c22579d46ebf339cbb672f8505ea408e5d960d6751064c16e02d18445cf4e7e61
- languageName: node
- linkType: hard
-
"@formatjs/intl-localematcher@npm:0.5.10":
version: 0.5.10
resolution: "@formatjs/intl-localematcher@npm:0.5.10"
@@ -2420,15 +2370,6 @@ __metadata:
languageName: node
linkType: hard
-"@formatjs/intl-localematcher@npm:0.5.8":
- version: 0.5.8
- resolution: "@formatjs/intl-localematcher@npm:0.5.8"
- dependencies:
- tslib: "npm:2"
- checksum: 10c0/7a660263986326b662d4cb537e8386331c34fda61fb830b105e6c62d49be58ace40728dae614883b27a41cec7b1df8b44f72f79e16e6028bfca65d398dc04f3b
- languageName: node
- linkType: hard
-
"@formatjs/intl-pluralrules@npm:^5.2.2":
version: 5.4.2
resolution: "@formatjs/intl-pluralrules@npm:5.4.2"
@@ -2441,23 +2382,21 @@ __metadata:
languageName: node
linkType: hard
-"@formatjs/intl@npm:2.10.15":
- version: 2.10.15
- resolution: "@formatjs/intl@npm:2.10.15"
+"@formatjs/intl@npm:3.1.0":
+ version: 3.1.0
+ resolution: "@formatjs/intl@npm:3.1.0"
dependencies:
- "@formatjs/ecma402-abstract": "npm:2.2.4"
- "@formatjs/fast-memoize": "npm:2.2.3"
- "@formatjs/icu-messageformat-parser": "npm:2.9.4"
- "@formatjs/intl-displaynames": "npm:6.8.5"
- "@formatjs/intl-listformat": "npm:7.7.5"
- intl-messageformat: "npm:10.7.7"
+ "@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/5d51fd0785d5547f375991d7df2d6303479b0083eeb35c42c30c9633aab77101895498f1eace419fd34fdb5c84aea19037c5280c3a9d85f9c3ffe6eef76b6f39
+ checksum: 10c0/a073768fffc51696eb7bd25fe1f0afdda1a0e38db3e2dd9b2fc3138ea799f00ef522f3d3083626ad3acbf913593254cfd728a6c6b08ef4f167dd132626a7e9fc
languageName: node
linkType: hard
@@ -3010,7 +2949,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"
@@ -3080,7 +3019,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"
@@ -4811,12 +4750,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
@@ -7073,12 +7010,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
@@ -7789,7 +7727,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
@@ -8946,14 +8884,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
@@ -9733,13 +9671,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
@@ -9998,19 +9936,7 @@ __metadata:
languageName: node
linkType: hard
-"intl-messageformat@npm:10.7.7":
- version: 10.7.7
- resolution: "intl-messageformat@npm:10.7.7"
- dependencies:
- "@formatjs/ecma402-abstract": "npm:2.2.4"
- "@formatjs/fast-memoize": "npm:2.2.3"
- "@formatjs/icu-messageformat-parser": "npm:2.9.4"
- tslib: "npm:2"
- checksum: 10c0/691895fb6a73a2feb2569658706e0d452861441de184dd1c9201e458a39fb80fc80080dd40d3d370400a52663f87de7a6d5a263c94245492f7265dd760441a95
- languageName: node
- linkType: hard
-
-"intl-messageformat@npm:^10.3.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:
@@ -11344,21 +11270,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"
@@ -11366,15 +11292,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
@@ -11841,6 +11767,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"
@@ -12427,12 +12360,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
@@ -12668,10 +12601,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
@@ -13068,12 +13001,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
@@ -13401,14 +13334,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
@@ -14304,13 +14237,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
@@ -14760,27 +14693,25 @@ __metadata:
languageName: node
linkType: hard
-"react-intl@npm:^6.4.2":
- version: 6.8.9
- resolution: "react-intl@npm:6.8.9"
+"react-intl@npm:^7.0.0":
+ version: 7.1.0
+ resolution: "react-intl@npm:7.1.0"
dependencies:
- "@formatjs/ecma402-abstract": "npm:2.2.4"
- "@formatjs/icu-messageformat-parser": "npm:2.9.4"
- "@formatjs/intl": "npm:2.10.15"
- "@formatjs/intl-displaynames": "npm:6.8.5"
- "@formatjs/intl-listformat": "npm:7.7.5"
+ "@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.7"
+ 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/d42a6252beac5448b4a248d84923b0f75dfbbee6208cd5c49ac2f525714ab94efe2a4933d464c64cb161ddccaa37b83dffb2dd0529428219b8a60ce548da3e57
+ checksum: 10c0/9d69e316a5f5c6d31fa77f136b595079db2f75f63398cf8253d407878246dd5bcf0cc2eb4d7d4aa0646530ee58b16ce9a8c3876a5c2f0dc38fdda7e4f8c07615
languageName: node
linkType: hard
@@ -15046,15 +14977,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
@@ -15625,10 +15556,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
@@ -15727,8 +15658,8 @@ __metadata:
linkType: hard
"sass@npm:^1.62.1":
- version: 1.83.1
- resolution: "sass@npm:1.83.1"
+ version: 1.83.4
+ resolution: "sass@npm:1.83.4"
dependencies:
"@parcel/watcher": "npm:^2.4.1"
chokidar: "npm:^4.0.0"
@@ -15739,7 +15670,7 @@ __metadata:
optional: true
bin:
sass: sass.js
- checksum: 10c0/9772506cd8290df7b5e800055098e91a8a65100840fd9e90c660deb74b248b3ddbbd1a274b8f7f09777d472d2c873575357bd87939a40fb5a80bdf654985486f
+ checksum: 10c0/6f27f0eebfeb50222b14baaeef548ef58a05daf8abd9797e6c499334ed7ad40541767056c8693780d06ca83d8836348ea7396a923d3be439b133507993ca78be
languageName: node
linkType: hard
@@ -17970,11 +17901,11 @@ __metadata:
linkType: hard
"uuid@npm:^11.0.0":
- version: 11.0.4
- resolution: "uuid@npm:11.0.4"
+ version: 11.0.5
+ resolution: "uuid@npm:11.0.5"
bin:
uuid: dist/esm/bin/uuid
- checksum: 10c0/3c13591c4dedaa3741f925e284df5974e3d6e0b1cb0f6f75f98c36f9c01d2a414350364fd067613ef600a21c6973dab0506530d4f499ff878f32a06f84569ead
+ checksum: 10c0/6f59f0c605e02c14515401084ca124b9cb462b4dcac866916a49862bcf831874508a308588c23a7718269226ad11a92da29b39d761ad2b86e736623e3a33b6e7
languageName: node
linkType: hard
@@ -18392,13 +18323,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