From eea2654236966fee69c4f18217a6a62b1c66d2da Mon Sep 17 00:00:00 2001
From: Claire
Date: Mon, 13 Nov 2023 17:58:00 +0100
Subject: [PATCH 01/28] Fix format-dependent redirects being cached regardless
of requested format (#27634)
---
config/routes.rb | 23 +++++++++++++++++++----
spec/requests/cache_spec.rb | 16 +++++++++++-----
2 files changed, 30 insertions(+), 9 deletions(-)
diff --git a/config/routes.rb b/config/routes.rb
index 5de8562a8c42a5..7e2f1aabed19fe 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -3,6 +3,18 @@
require 'sidekiq_unique_jobs/web'
require 'sidekiq-scheduler/web'
+class RedirectWithVary < ActionDispatch::Routing::PathRedirect
+ def serve(...)
+ super.tap do |_, headers, _|
+ headers['Vary'] = 'Origin, Accept'
+ end
+ end
+end
+
+def redirect_with_vary(path)
+ RedirectWithVary.new(301, path)
+end
+
Rails.application.routes.draw do
# Paths of routes on the web app that to not require to be indexed or
# have alternative format representations requiring separate controllers
@@ -90,10 +102,13 @@
confirmations: 'auth/confirmations',
}
- get '/users/:username', to: redirect('/@%{username}'), constraints: lambda { |req| req.format.nil? || req.format.html? }
- get '/users/:username/following', to: redirect('/@%{username}/following'), constraints: lambda { |req| req.format.nil? || req.format.html? }
- get '/users/:username/followers', to: redirect('/@%{username}/followers'), constraints: lambda { |req| req.format.nil? || req.format.html? }
- get '/users/:username/statuses/:id', to: redirect('/@%{username}/%{id}'), constraints: lambda { |req| req.format.nil? || req.format.html? }
+ # rubocop:disable Style/FormatStringToken - those do not go through the usual formatting functions and are not safe to correct
+ get '/users/:username', to: redirect_with_vary('/@%{username}'), constraints: lambda { |req| req.format.nil? || req.format.html? }
+ get '/users/:username/following', to: redirect_with_vary('/@%{username}/following'), constraints: lambda { |req| req.format.nil? || req.format.html? }
+ get '/users/:username/followers', to: redirect_with_vary('/@%{username}/followers'), constraints: lambda { |req| req.format.nil? || req.format.html? }
+ get '/users/:username/statuses/:id', to: redirect_with_vary('/@%{username}/%{id}'), constraints: lambda { |req| req.format.nil? || req.format.html? }
+ # rubocop:enable Style/FormatStringToken
+
get '/authorize_follow', to: redirect { |_, request| "/authorize_interaction?#{request.params.to_query}" }
resources :accounts, path: 'users', only: [:show], param: :username do
diff --git a/spec/requests/cache_spec.rb b/spec/requests/cache_spec.rb
index c391c8b3da9f51..d40895fc3b5e17 100644
--- a/spec/requests/cache_spec.rb
+++ b/spec/requests/cache_spec.rb
@@ -124,7 +124,7 @@ module DisabledAnonymousAPI
expect(response.cookies).to be_empty
end
- it 'sets public cache control' do
+ it 'sets public cache control', :aggregate_failures do
# expect(response.cache_control[:max_age]&.to_i).to be_positive
expect(response.cache_control[:public]).to be_truthy
expect(response.cache_control[:private]).to be_falsy
@@ -141,11 +141,8 @@ module DisabledAnonymousAPI
end
shared_examples 'non-cacheable error' do
- it 'does not return HTTP success' do
+ it 'does not return HTTP success and does not have cache headers', :aggregate_failures do
expect(response).to_not have_http_status(200)
- end
-
- it 'does not have cache headers' do
expect(response.cache_control[:public]).to be_falsy
end
end
@@ -182,6 +179,15 @@ module DisabledAnonymousAPI
end
context 'when anonymously accessed' do
+ describe '/users/alice' do
+ it 'redirects with proper cache header', :aggregate_failures do
+ get '/users/alice'
+
+ expect(response).to redirect_to('/@alice')
+ expect(response.headers['Vary']&.split(',')&.map { |x| x.strip.downcase }).to include('accept')
+ end
+ end
+
TestEndpoints::ALWAYS_CACHED.each do |endpoint|
describe endpoint do
before { get endpoint }
From ef149674f093e97ffbeb12910e2ebdd668bed7c2 Mon Sep 17 00:00:00 2001
From: Claire
Date: Mon, 23 Oct 2023 14:27:07 +0200
Subject: [PATCH 02/28] Change Content-Security-Policy to be tighter on media
paths (#26889)
---
config/initializers/content_security_policy.rb | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/config/initializers/content_security_policy.rb b/config/initializers/content_security_policy.rb
index 6ce84a6e4253d5..85a328b448b6e3 100644
--- a/config/initializers/content_security_policy.rb
+++ b/config/initializers/content_security_policy.rb
@@ -5,7 +5,11 @@
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
def host_to_url(str)
- "http#{Rails.configuration.x.use_https ? 's' : ''}://#{str.split('/').first}" if str.present?
+ return if str.blank?
+
+ uri = Addressable::URI.parse("http#{Rails.configuration.x.use_https ? 's' : ''}://#{str}")
+ uri.path += '/' unless uri.path.blank? || uri.path.end_with?('/')
+ uri.to_s
end
base_host = Rails.configuration.x.web_domain
From 156d32689b3d084a7fe9f0aa6a8788cd4e831f17 Mon Sep 17 00:00:00 2001
From: gunchleoc
Date: Thu, 28 Sep 2023 09:13:44 +0100
Subject: [PATCH 03/28] Only strip country code when language not listed in
SUPPORTED_LOCALES (#27099)
---
app/helpers/languages_helper.rb | 1 +
1 file changed, 1 insertion(+)
diff --git a/app/helpers/languages_helper.rb b/app/helpers/languages_helper.rb
index a8c66552cf14e9..c42c4c23efc44c 100644
--- a/app/helpers/languages_helper.rb
+++ b/app/helpers/languages_helper.rb
@@ -254,6 +254,7 @@ def standard_locale_name(locale)
def valid_locale_or_nil(str)
return if str.blank?
+ return str if valid_locale?(str)
code, = str.to_s.split(/[_-]/) # Strip out the region from e.g. en_US or ja-JP
From aa69ca74ed113e4f0526522931f24db55de90b9a Mon Sep 17 00:00:00 2001
From: Claire
Date: Fri, 6 Oct 2023 17:46:04 +0200
Subject: [PATCH 04/28] Fix incorrect serialization of regional languages in
`contentMap` (#27207)
---
app/lib/activitypub/case_transform.rb | 2 ++
.../serializers/activitypub/note_serializer_spec.rb | 13 ++++++++++---
2 files changed, 12 insertions(+), 3 deletions(-)
diff --git a/app/lib/activitypub/case_transform.rb b/app/lib/activitypub/case_transform.rb
index da2c5eb8b05774..bf5de722103a88 100644
--- a/app/lib/activitypub/case_transform.rb
+++ b/app/lib/activitypub/case_transform.rb
@@ -14,6 +14,8 @@ def camel_lower(value)
when String
camel_lower_cache[value] ||= if value.start_with?('_:')
"_:#{value.delete_prefix('_:').underscore.camelize(:lower)}"
+ elsif LanguagesHelper::ISO_639_1_REGIONAL.key?(value.to_sym)
+ value
else
value.underscore.camelize(:lower)
end
diff --git a/spec/serializers/activitypub/note_serializer_spec.rb b/spec/serializers/activitypub/note_serializer_spec.rb
index 4b2b8ec875667f..31ee31f132f4c3 100644
--- a/spec/serializers/activitypub/note_serializer_spec.rb
+++ b/spec/serializers/activitypub/note_serializer_spec.rb
@@ -7,7 +7,7 @@
let!(:account) { Fabricate(:account) }
let!(:other) { Fabricate(:account) }
- let!(:parent) { Fabricate(:status, account: account, visibility: :public) }
+ let!(:parent) { Fabricate(:status, account: account, visibility: :public, language: 'zh-TW') }
let!(:reply_by_account_first) { Fabricate(:status, account: account, thread: parent, visibility: :public) }
let!(:reply_by_account_next) { Fabricate(:status, account: account, thread: parent, visibility: :public) }
let!(:reply_by_other_first) { Fabricate(:status, account: other, thread: parent, visibility: :public) }
@@ -18,8 +18,15 @@
@serialization = ActiveModelSerializers::SerializableResource.new(parent, serializer: described_class, adapter: ActivityPub::Adapter)
end
- it 'has a Note type' do
- expect(subject['type']).to eql('Note')
+ it 'has the expected shape' do
+ expect(subject).to include({
+ '@context' => include('https://www.w3.org/ns/activitystreams'),
+ 'type' => 'Note',
+ 'attributedTo' => ActivityPub::TagManager.instance.uri_for(account),
+ 'contentMap' => include({
+ 'zh-TW' => a_kind_of(String),
+ }),
+ })
end
it 'has a replies collection' do
From cdedae6d63ff6ac21dd8810896d39f016fd8ec68 Mon Sep 17 00:00:00 2001
From: Claire
Date: Mon, 23 Oct 2023 14:19:38 +0200
Subject: [PATCH 05/28] Fix some link anchors being recognized as hashtags
(#27271)
---
app/models/tag.rb | 2 +-
spec/models/tag_spec.rb | 26 +++++++++++++++-----------
2 files changed, 16 insertions(+), 12 deletions(-)
diff --git a/app/models/tag.rb b/app/models/tag.rb
index 672d80c8b871c3..413f6f5004766e 100644
--- a/app/models/tag.rb
+++ b/app/models/tag.rb
@@ -35,7 +35,7 @@ class Tag < ApplicationRecord
HASHTAG_LAST_SEQUENCE = '([[:word:]_]*[[:alpha:]][[:word:]_]*)'
HASHTAG_NAME_PAT = "#{HASHTAG_FIRST_SEQUENCE}|#{HASHTAG_LAST_SEQUENCE}"
- HASHTAG_RE = %r{(?:^|[^/)\w])#(#{HASHTAG_NAME_PAT})}i
+ HASHTAG_RE = %r{(?
Date: Fri, 13 Oct 2023 18:15:47 +0900
Subject: [PATCH 06/28] Fix when unfollow a tag, my post also disappears from
the home timeline (#27391)
---
app/lib/feed_manager.rb | 1 +
spec/lib/feed_manager_spec.rb | 38 +++++++++++++++++++++++++++++++++++
2 files changed, 39 insertions(+)
diff --git a/app/lib/feed_manager.rb b/app/lib/feed_manager.rb
index 26e5b78e8f8864..8b7f208115d2b3 100644
--- a/app/lib/feed_manager.rb
+++ b/app/lib/feed_manager.rb
@@ -192,6 +192,7 @@ def unmerge_tag_from_home(from_tag, into_account)
# also tagged with another followed hashtag or from a followed user
scope = from_tag.statuses
.where(id: timeline_status_ids)
+ .where.not(account: into_account)
.where.not(account: into_account.following)
.tagged_with_none(TagFollow.where(account: into_account).pluck(:tag_id))
diff --git a/spec/lib/feed_manager_spec.rb b/spec/lib/feed_manager_spec.rb
index 25edaada64e5d7..f4dd42f8451f40 100644
--- a/spec/lib/feed_manager_spec.rb
+++ b/spec/lib/feed_manager_spec.rb
@@ -525,6 +525,44 @@
end
end
+ describe '#unmerge_tag_from_home' do
+ let(:receiver) { Fabricate(:account) }
+ let(:tag) { Fabricate(:tag) }
+
+ it 'leaves a tagged status' do
+ status = Fabricate(:status)
+ status.tags << tag
+ described_class.instance.push_to_home(receiver, status)
+
+ described_class.instance.unmerge_tag_from_home(tag, receiver)
+
+ expect(redis.zrange("feed:home:#{receiver.id}", 0, -1)).to_not include(status.id.to_s)
+ end
+
+ it 'remains a tagged status written by receiver\'s followee' do
+ followee = Fabricate(:account)
+ receiver.follow!(followee)
+
+ status = Fabricate(:status, account: followee)
+ status.tags << tag
+ described_class.instance.push_to_home(receiver, status)
+
+ described_class.instance.unmerge_tag_from_home(tag, receiver)
+
+ expect(redis.zrange("feed:home:#{receiver.id}", 0, -1)).to include(status.id.to_s)
+ end
+
+ it 'remains a tagged status written by receiver' do
+ status = Fabricate(:status, account: receiver)
+ status.tags << tag
+ described_class.instance.push_to_home(receiver, status)
+
+ described_class.instance.unmerge_tag_from_home(tag, receiver)
+
+ expect(redis.zrange("feed:home:#{receiver.id}", 0, -1)).to include(status.id.to_s)
+ end
+ end
+
describe '#clear_from_home' do
let(:account) { Fabricate(:account) }
let(:followed_account) { Fabricate(:account) }
From 13205b54fd69f8cce1975aff8e784aa2f24e82ff Mon Sep 17 00:00:00 2001
From: Claire
Date: Mon, 16 Oct 2023 15:24:14 +0200
Subject: [PATCH 07/28] Fix handling of `inLanguage` attribute in preview card
processing (#27423)
---
app/lib/link_details_extractor.rb | 3 ++-
spec/lib/link_details_extractor_spec.rb | 10 ++++++++++
2 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/app/lib/link_details_extractor.rb b/app/lib/link_details_extractor.rb
index b95ec805190848..a96612cab0d131 100644
--- a/app/lib/link_details_extractor.rb
+++ b/app/lib/link_details_extractor.rb
@@ -36,7 +36,8 @@ def description
end
def language
- json['inLanguage']
+ lang = json['inLanguage']
+ lang.is_a?(Hash) ? (lang['alternateName'] || lang['name']) : lang
end
def type
diff --git a/spec/lib/link_details_extractor_spec.rb b/spec/lib/link_details_extractor_spec.rb
index 599bc4e6de2f60..8c485cef2afb5b 100644
--- a/spec/lib/link_details_extractor_spec.rb
+++ b/spec/lib/link_details_extractor_spec.rb
@@ -82,6 +82,10 @@
'name' => 'Pet News',
'url' => 'https://example.com',
},
+ 'inLanguage' => {
+ name: 'English',
+ alternateName: 'en',
+ },
}.to_json
end
@@ -115,6 +119,12 @@
expect(subject.provider_name).to eq 'Pet News'
end
end
+
+ describe '#language' do
+ it 'returns the language from structured data' do
+ expect(subject.language).to eq 'en'
+ end
+ end
end
context 'when is wrapped in CDATA tags' do
From 700ae1f9183a01afe574687a91afb6e7a99825fc Mon Sep 17 00:00:00 2001
From: Claire
Date: Thu, 26 Oct 2023 19:03:31 +0200
Subject: [PATCH 08/28] Fix report processing notice not mentioning the report
number when performing a custom action (#27442)
---
app/controllers/admin/account_actions_controller.rb | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/controllers/admin/account_actions_controller.rb b/app/controllers/admin/account_actions_controller.rb
index e89404b6098e0f..e674bf55a028b0 100644
--- a/app/controllers/admin/account_actions_controller.rb
+++ b/app/controllers/admin/account_actions_controller.rb
@@ -21,7 +21,7 @@ def create
account_action.save!
if account_action.with_report?
- redirect_to admin_reports_path, notice: I18n.t('admin.reports.processed_msg', id: params[:report_id])
+ redirect_to admin_reports_path, notice: I18n.t('admin.reports.processed_msg', id: resource_params[:report_id])
else
redirect_to admin_account_path(@account.id)
end
From bece853e3cc45a7b8b8b19b78b6f772c65e80b2a Mon Sep 17 00:00:00 2001
From: Claire
Date: Fri, 27 Oct 2023 10:35:21 +0200
Subject: [PATCH 09/28] Fix error and incorrect URLs in
`/api/v1/accounts/:id/featured_tags` for remote accounts (#27459)
---
.../rest/featured_tag_serializer.rb | 4 +-
config/routes.rb | 2 +-
.../accounts/featured_tags_controller_spec.rb | 23 ---------
.../api/v1/accounts/featured_tags_spec.rb | 50 +++++++++++++++++++
4 files changed, 54 insertions(+), 25 deletions(-)
delete mode 100644 spec/controllers/api/v1/accounts/featured_tags_controller_spec.rb
create mode 100644 spec/requests/api/v1/accounts/featured_tags_spec.rb
diff --git a/app/serializers/rest/featured_tag_serializer.rb b/app/serializers/rest/featured_tag_serializer.rb
index c4b35ab03ab96f..c1ff4602aa83f6 100644
--- a/app/serializers/rest/featured_tag_serializer.rb
+++ b/app/serializers/rest/featured_tag_serializer.rb
@@ -10,7 +10,9 @@ def id
end
def url
- short_account_tag_url(object.account, object.tag)
+ # The path is hardcoded because we have to deal with both local and
+ # remote users, which are different routes
+ account_with_domain_url(object.account, "tagged/#{object.tag.to_param}")
end
def name
diff --git a/config/routes.rb b/config/routes.rb
index 7e2f1aabed19fe..379262f4cd7549 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -149,7 +149,7 @@ def redirect_with_vary(path)
get '/@:account_username/:id/embed', to: 'statuses#embed', as: :embed_short_account_status
end
- get '/@:username_with_domain/(*any)', to: 'home#index', constraints: { username_with_domain: %r{([^/])+?} }, format: false
+ get '/@:username_with_domain/(*any)', to: 'home#index', constraints: { username_with_domain: %r{([^/])+?} }, as: :account_with_domain, format: false
get '/settings', to: redirect('/settings/profile')
draw(:settings)
diff --git a/spec/controllers/api/v1/accounts/featured_tags_controller_spec.rb b/spec/controllers/api/v1/accounts/featured_tags_controller_spec.rb
deleted file mode 100644
index 53ac1e2a7a23c0..00000000000000
--- a/spec/controllers/api/v1/accounts/featured_tags_controller_spec.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-# frozen_string_literal: true
-
-require 'rails_helper'
-
-describe Api::V1::Accounts::FeaturedTagsController do
- render_views
-
- let(:user) { Fabricate(:user) }
- let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:accounts') }
- let(:account) { Fabricate(:account) }
-
- before do
- allow(controller).to receive(:doorkeeper_token) { token }
- end
-
- describe 'GET #index' do
- it 'returns http success' do
- get :index, params: { account_id: account.id, limit: 2 }
-
- expect(response).to have_http_status(200)
- end
- end
-end
diff --git a/spec/requests/api/v1/accounts/featured_tags_spec.rb b/spec/requests/api/v1/accounts/featured_tags_spec.rb
new file mode 100644
index 00000000000000..bae7d448b6daee
--- /dev/null
+++ b/spec/requests/api/v1/accounts/featured_tags_spec.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe 'account featured tags API' do
+ let(:user) { Fabricate(:user) }
+ let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
+ let(:scopes) { 'read:accounts' }
+ let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
+ let(:account) { Fabricate(:account) }
+
+ describe 'GET /api/v1/accounts/:id/featured_tags' do
+ subject do
+ get "/api/v1/accounts/#{account.id}/featured_tags", headers: headers
+ end
+
+ before do
+ account.featured_tags.create!(name: 'foo')
+ account.featured_tags.create!(name: 'bar')
+ end
+
+ it 'returns the expected tags', :aggregate_failures do
+ subject
+
+ expect(response).to have_http_status(200)
+ expect(body_as_json).to contain_exactly(a_hash_including({
+ name: 'bar',
+ url: "https://cb6e6126.ngrok.io/@#{account.username}/tagged/bar",
+ }), a_hash_including({
+ name: 'foo',
+ url: "https://cb6e6126.ngrok.io/@#{account.username}/tagged/foo",
+ }))
+ end
+
+ context 'when the account is remote' do
+ it 'returns the expected tags', :aggregate_failures do
+ subject
+
+ expect(response).to have_http_status(200)
+ expect(body_as_json).to contain_exactly(a_hash_including({
+ name: 'bar',
+ url: "https://cb6e6126.ngrok.io/@#{account.pretty_acct}/tagged/bar",
+ }), a_hash_including({
+ name: 'foo',
+ url: "https://cb6e6126.ngrok.io/@#{account.pretty_acct}/tagged/foo",
+ }))
+ end
+ end
+ end
+end
From c66ade7de80e72d1dfdd1833c04cb3be47754f71 Mon Sep 17 00:00:00 2001
From: Claire
Date: Fri, 20 Oct 2023 10:45:46 +0200
Subject: [PATCH 10/28] Fix processing LDSigned activities from actors with
unknown public keys (#27474)
---
app/lib/activitypub/linked_data_signature.rb | 6 ++--
.../activitypub/linked_data_signature_spec.rb | 34 +++++++++++++++++++
2 files changed, 38 insertions(+), 2 deletions(-)
diff --git a/app/lib/activitypub/linked_data_signature.rb b/app/lib/activitypub/linked_data_signature.rb
index ea59879f3b7eed..faea63e8f12c6e 100644
--- a/app/lib/activitypub/linked_data_signature.rb
+++ b/app/lib/activitypub/linked_data_signature.rb
@@ -18,8 +18,8 @@ def verify_actor!
return unless type == 'RsaSignature2017'
- creator = ActivityPub::TagManager.instance.uri_to_actor(creator_uri)
- creator ||= ActivityPub::FetchRemoteKeyService.new.call(creator_uri, id: false)
+ creator = ActivityPub::TagManager.instance.uri_to_actor(creator_uri)
+ creator = ActivityPub::FetchRemoteKeyService.new.call(creator_uri, id: false) if creator&.public_key.blank?
return if creator.nil?
@@ -28,6 +28,8 @@ def verify_actor!
to_be_verified = options_hash + document_hash
creator if creator.keypair.public_key.verify(OpenSSL::Digest.new('SHA256'), Base64.decode64(signature), to_be_verified)
+ rescue OpenSSL::PKey::RSAError
+ false
end
def sign!(creator, sign_with: nil)
diff --git a/spec/lib/activitypub/linked_data_signature_spec.rb b/spec/lib/activitypub/linked_data_signature_spec.rb
index 6a6ad1a706430a..03d0c5a8785c19 100644
--- a/spec/lib/activitypub/linked_data_signature_spec.rb
+++ b/spec/lib/activitypub/linked_data_signature_spec.rb
@@ -38,6 +38,40 @@
end
end
+ context 'when local account record is missing a public key' do
+ let(:raw_signature) do
+ {
+ 'creator' => 'http://example.com/alice',
+ 'created' => '2017-09-23T20:21:34Z',
+ }
+ end
+
+ let(:signature) { raw_signature.merge('type' => 'RsaSignature2017', 'signatureValue' => sign(sender, raw_signature, raw_json)) }
+
+ let(:service_stub) { instance_double(ActivityPub::FetchRemoteKeyService) }
+
+ before do
+ # Ensure signature is computed with the old key
+ signature
+
+ # Unset key
+ old_key = sender.public_key
+ sender.update!(private_key: '', public_key: '')
+
+ allow(ActivityPub::FetchRemoteKeyService).to receive(:new).and_return(service_stub)
+
+ allow(service_stub).to receive(:call).with('http://example.com/alice', id: false) do
+ sender.update!(public_key: old_key)
+ sender
+ end
+ end
+
+ it 'fetches key and returns creator' do
+ expect(subject.verify_actor!).to eq sender
+ expect(service_stub).to have_received(:call).with('http://example.com/alice', id: false).once
+ end
+ end
+
context 'when signature is missing' do
let(:signature) { nil }
From d5bc10b711dede8c84087de9240eea85c14ab7b0 Mon Sep 17 00:00:00 2001
From: Renaud Chaput
Date: Thu, 19 Oct 2023 13:22:44 +0200
Subject: [PATCH 11/28] The `class` props should be `className` (#27462)
---
.../mastodon/features/ui/components/link_footer.jsx | 2 +-
.../mastodon/features/ui/components/navigation_panel.jsx | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/app/javascript/mastodon/features/ui/components/link_footer.jsx b/app/javascript/mastodon/features/ui/components/link_footer.jsx
index 9585df2ec49854..6b1555243b7668 100644
--- a/app/javascript/mastodon/features/ui/components/link_footer.jsx
+++ b/app/javascript/mastodon/features/ui/components/link_footer.jsx
@@ -100,7 +100,7 @@ class LinkFooter extends PureComponent {
{DividingCircle}
{DividingCircle}
- v{version}
+ v{version}
);
diff --git a/app/javascript/mastodon/features/ui/components/navigation_panel.jsx b/app/javascript/mastodon/features/ui/components/navigation_panel.jsx
index 8006ca89a24a39..22eee79c0ace88 100644
--- a/app/javascript/mastodon/features/ui/components/navigation_panel.jsx
+++ b/app/javascript/mastodon/features/ui/components/navigation_panel.jsx
@@ -59,10 +59,10 @@ class NavigationPanel extends Component {
{transientSingleColumn ? (
-
+
From 4fc252354624d5b0fbafd86251204cc19edc525d Mon Sep 17 00:00:00 2001
From: Renaud Chaput
Date: Thu, 19 Oct 2023 19:36:08 +0200
Subject: [PATCH 12/28] Do not display the navigation banner in the logo
container (#27476)
---
.../ui/components/navigation_panel.jsx | 30 +++++++++++--------
.../styles/mastodon/components.scss | 1 +
2 files changed, 19 insertions(+), 12 deletions(-)
diff --git a/app/javascript/mastodon/features/ui/components/navigation_panel.jsx b/app/javascript/mastodon/features/ui/components/navigation_panel.jsx
index 22eee79c0ace88..b16eb5e1795131 100644
--- a/app/javascript/mastodon/features/ui/components/navigation_panel.jsx
+++ b/app/javascript/mastodon/features/ui/components/navigation_panel.jsx
@@ -53,24 +53,30 @@ class NavigationPanel extends Component {
const { intl } = this.props;
const { signedIn, disabledAccountId } = this.context.identity;
+ let banner = undefined;
+
+ if(transientSingleColumn)
+ banner = ();
+
return (
-
- {transientSingleColumn ? (
-
- ) : (
-
- )}
+ {!banner &&
}
+ {banner &&
+
+ {banner}
+
+ }
+
{signedIn && (
<>
diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss
index 567397a07b6c1a..8c88b7dd6ccb6b 100644
--- a/app/javascript/styles/mastodon/components.scss
+++ b/app/javascript/styles/mastodon/components.scss
@@ -2466,6 +2466,7 @@ $ui-header-height: 55px;
.navigation-panel__sign-in-banner,
.navigation-panel__logo,
+ .navigation-panel__banner,
.getting-started__trends {
display: none;
}
From 4c3870647414f4cbd05373b0767d79e89a0db9a2 Mon Sep 17 00:00:00 2001
From: Claire
Date: Wed, 25 Oct 2023 15:55:57 +0200
Subject: [PATCH 13/28] Fix batch attachment deletion when using OpenStack
Swift (#27554)
---
app/lib/attachment_batch.rb | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/lib/attachment_batch.rb b/app/lib/attachment_batch.rb
index b75938bdd76215..78bd5931603535 100644
--- a/app/lib/attachment_batch.rb
+++ b/app/lib/attachment_batch.rb
@@ -75,7 +75,7 @@ def remove_files
end
when :fog
logger.debug { "Deleting #{attachment.path(style)}" }
- attachment.directory.files.new(key: attachment.path(style)).destroy
+ attachment.send(:directory).files.new(key: attachment.path(style)).destroy
when :azure
logger.debug { "Deleting #{attachment.path(style)}" }
attachment.destroy
From de86e822f4f51d7e20a59d48b2c7e66431e374fe Mon Sep 17 00:00:00 2001
From: Claire
Date: Thu, 26 Oct 2023 15:09:48 +0200
Subject: [PATCH 14/28] Fix error when trying to delete already-deleted file
with OpenStack Swift (#27569)
---
app/lib/attachment_batch.rb | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/app/lib/attachment_batch.rb b/app/lib/attachment_batch.rb
index 78bd5931603535..13a9da828f5cf2 100644
--- a/app/lib/attachment_batch.rb
+++ b/app/lib/attachment_batch.rb
@@ -75,7 +75,12 @@ def remove_files
end
when :fog
logger.debug { "Deleting #{attachment.path(style)}" }
- attachment.send(:directory).files.new(key: attachment.path(style)).destroy
+
+ begin
+ attachment.send(:directory).files.new(key: attachment.path(style)).destroy
+ rescue Fog::Storage::OpenStack::NotFound
+ # Ignore failure to delete a file that has already been deleted
+ end
when :azure
logger.debug { "Deleting #{attachment.path(style)}" }
attachment.destroy
From e6f4c91c5c7a28b5185f31c0dc5b58c242f4270f Mon Sep 17 00:00:00 2001
From: Claire
Date: Fri, 27 Oct 2023 16:04:51 +0200
Subject: [PATCH 15/28] Fix hashtag matching pattern matching some URLs
(#27584)
---
app/models/tag.rb | 2 +-
spec/models/tag_spec.rb | 4 ++++
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/app/models/tag.rb b/app/models/tag.rb
index 413f6f5004766e..8fab98fb5c0107 100644
--- a/app/models/tag.rb
+++ b/app/models/tag.rb
@@ -35,7 +35,7 @@ class Tag < ApplicationRecord
HASHTAG_LAST_SEQUENCE = '([[:word:]_]*[[:alpha:]][[:word:]_]*)'
HASHTAG_NAME_PAT = "#{HASHTAG_FIRST_SEQUENCE}|#{HASHTAG_LAST_SEQUENCE}"
- HASHTAG_RE = %r{(?
Date: Mon, 30 Oct 2023 23:32:25 +0100
Subject: [PATCH 16/28] Fix posts from force-sensitized accounts being able to
trend (#27620)
---
app/models/trends/statuses.rb | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/models/trends/statuses.rb b/app/models/trends/statuses.rb
index 427fa8e797653a..c47fb8427b55c2 100644
--- a/app/models/trends/statuses.rb
+++ b/app/models/trends/statuses.rb
@@ -106,7 +106,7 @@ def klass
private
def eligible?(status)
- status.public_visibility? && status.account.discoverable? && !status.account.silenced? && status.spoiler_text.blank? && !status.sensitive? && !status.reply? && valid_locale?(status.language)
+ status.public_visibility? && status.account.discoverable? && !status.account.silenced? && !status.account.sensitized? && status.spoiler_text.blank? && !status.sensitive? && !status.reply? && valid_locale?(status.language)
end
def calculate_scores(statuses, at_time)
From 54a07731d14ed3809375b31e2ce86e347afed271 Mon Sep 17 00:00:00 2001
From: Claire
Date: Thu, 2 Nov 2023 15:58:37 +0100
Subject: [PATCH 17/28] Fix posts from threads received out-of-order sometimes
not being inserted into timelines (#27653)
---
app/services/fan_out_on_write_service.rb | 8 +-
app/workers/thread_resolve_worker.rb | 9 +-
spec/lib/activitypub/activity/create_spec.rb | 103 +++++++++++++++++++
3 files changed, 116 insertions(+), 4 deletions(-)
diff --git a/app/services/fan_out_on_write_service.rb b/app/services/fan_out_on_write_service.rb
index 2554756a5d705e..f2a79c9fc96068 100644
--- a/app/services/fan_out_on_write_service.rb
+++ b/app/services/fan_out_on_write_service.rb
@@ -8,6 +8,7 @@ class FanOutOnWriteService < BaseService
# @param [Hash] options
# @option options [Boolean] update
# @option options [Array] silenced_account_ids
+ # @option options [Boolean] skip_notifications
def call(status, options = {})
@status = status
@account = status.account
@@ -37,8 +38,11 @@ def check_race_condition!
def fan_out_to_local_recipients!
deliver_to_self!
- notify_mentioned_accounts!
- notify_about_update! if update?
+
+ unless @options[:skip_notifications]
+ notify_mentioned_accounts!
+ notify_about_update! if update?
+ end
case @status.visibility.to_sym
when :public, :unlisted, :private
diff --git a/app/workers/thread_resolve_worker.rb b/app/workers/thread_resolve_worker.rb
index 3206c45f6399e5..d4cefb3fdc0627 100644
--- a/app/workers/thread_resolve_worker.rb
+++ b/app/workers/thread_resolve_worker.rb
@@ -7,13 +7,18 @@ class ThreadResolveWorker
sidekiq_options queue: 'pull', retry: 3
def perform(child_status_id, parent_url, options = {})
- child_status = Status.find(child_status_id)
- parent_status = FetchRemoteStatusService.new.call(parent_url, **options.deep_symbolize_keys)
+ child_status = Status.find(child_status_id)
+ return if child_status.in_reply_to_id.present?
+
+ parent_status = ActivityPub::TagManager.instance.uri_to_resource(parent_url, Status)
+ parent_status ||= FetchRemoteStatusService.new.call(parent_url, **options.deep_symbolize_keys)
return if parent_status.nil?
child_status.thread = parent_status
child_status.save!
+
+ DistributionWorker.perform_async(child_status_id, { 'skip_notifications' => true }) if child_status.within_realtime_window?
rescue ActiveRecord::RecordNotFound
true
end
diff --git a/spec/lib/activitypub/activity/create_spec.rb b/spec/lib/activitypub/activity/create_spec.rb
index f6c24754c015f7..8425f2127cd4a0 100644
--- a/spec/lib/activitypub/activity/create_spec.rb
+++ b/spec/lib/activitypub/activity/create_spec.rb
@@ -23,6 +23,109 @@
stub_request(:get, 'http://example.com/emojib.png').to_return(body: attachment_fixture('emojo.png'), headers: { 'Content-Type' => 'application/octet-stream' })
end
+ describe 'processing posts received out of order' do
+ let(:follower) { Fabricate(:account, username: 'bob') }
+
+ let(:object_json) do
+ {
+ id: [ActivityPub::TagManager.instance.uri_for(sender), 'post1'].join('/'),
+ type: 'Note',
+ to: [
+ 'https://www.w3.org/ns/activitystreams#Public',
+ ActivityPub::TagManager.instance.uri_for(follower),
+ ],
+ content: '@bob lorem ipsum',
+ published: 1.hour.ago.utc.iso8601,
+ updated: 1.hour.ago.utc.iso8601,
+ tag: {
+ type: 'Mention',
+ href: ActivityPub::TagManager.instance.uri_for(follower),
+ },
+ }
+ end
+
+ let(:reply_json) do
+ {
+ id: [ActivityPub::TagManager.instance.uri_for(sender), 'reply'].join('/'),
+ type: 'Note',
+ inReplyTo: object_json[:id],
+ to: [
+ 'https://www.w3.org/ns/activitystreams#Public',
+ ActivityPub::TagManager.instance.uri_for(follower),
+ ],
+ content: '@bob lorem ipsum',
+ published: Time.now.utc.iso8601,
+ updated: Time.now.utc.iso8601,
+ tag: {
+ type: 'Mention',
+ href: ActivityPub::TagManager.instance.uri_for(follower),
+ },
+ }
+ end
+
+ def activity_for_object(json)
+ {
+ '@context': 'https://www.w3.org/ns/activitystreams',
+ id: [json[:id], 'activity'].join('/'),
+ type: 'Create',
+ actor: ActivityPub::TagManager.instance.uri_for(sender),
+ object: json,
+ }.with_indifferent_access
+ end
+
+ before do
+ follower.follow!(sender)
+ end
+
+ around do |example|
+ Sidekiq::Testing.fake! do
+ example.run
+ Sidekiq::Worker.clear_all
+ end
+ end
+
+ it 'correctly processes posts and inserts them in timelines', :aggregate_failures do
+ # Simulate a temporary failure preventing from fetching the parent post
+ stub_request(:get, object_json[:id]).to_return(status: 500)
+
+ # When receiving the reply…
+ described_class.new(activity_for_object(reply_json), sender, delivery: true).perform
+
+ # NOTE: Refering explicitly to the workers is a bit awkward
+ DistributionWorker.drain
+ FeedInsertWorker.drain
+
+ # …it creates a status with an unknown parent
+ reply = Status.find_by(uri: reply_json[:id])
+ expect(reply.reply?).to be true
+ expect(reply.in_reply_to_id).to be_nil
+
+ # …and creates a notification
+ expect(LocalNotificationWorker.jobs.size).to eq 1
+
+ # …but does not insert it into timelines
+ expect(redis.zscore(FeedManager.instance.key(:home, follower.id), reply.id)).to be_nil
+
+ # When receiving the parent…
+ described_class.new(activity_for_object(object_json), sender, delivery: true).perform
+
+ Sidekiq::Worker.drain_all
+
+ # …it creates a status and insert it into timelines
+ parent = Status.find_by(uri: object_json[:id])
+ expect(parent.reply?).to be false
+ expect(parent.in_reply_to_id).to be_nil
+ expect(reply.reload.in_reply_to_id).to eq parent.id
+
+ # Check that the both statuses have been inserted into the home feed
+ expect(redis.zscore(FeedManager.instance.key(:home, follower.id), parent.id)).to be_within(0.1).of(parent.id.to_f)
+ expect(redis.zscore(FeedManager.instance.key(:home, follower.id), reply.id)).to be_within(0.1).of(reply.id.to_f)
+
+ # Creates two notifications
+ expect(Notification.count).to eq 2
+ end
+ end
+
describe '#perform' do
context 'when fetching' do
subject { described_class.new(json, sender) }
From 1076a6cd62a80f1e331980e7c81174fa1a67f435 Mon Sep 17 00:00:00 2001
From: Claire
Date: Mon, 6 Nov 2023 10:28:14 +0100
Subject: [PATCH 18/28] Fix incoming status creation date not being restricted
to standard ISO8601 (#27655)
---
app/lib/activitypub/parser/status_parser.rb | 3 +-
spec/lib/activitypub/activity/create_spec.rb | 48 ++++++++++++++++++--
2 files changed, 45 insertions(+), 6 deletions(-)
diff --git a/app/lib/activitypub/parser/status_parser.rb b/app/lib/activitypub/parser/status_parser.rb
index 3ba154d01551fa..45f5fc5bf2d549 100644
--- a/app/lib/activitypub/parser/status_parser.rb
+++ b/app/lib/activitypub/parser/status_parser.rb
@@ -53,7 +53,8 @@ def title
end
def created_at
- @object['published']&.to_datetime
+ datetime = @object['published']&.to_datetime
+ datetime if datetime.present? && (0..9999).cover?(datetime.year)
rescue ArgumentError
nil
end
diff --git a/spec/lib/activitypub/activity/create_spec.rb b/spec/lib/activitypub/activity/create_spec.rb
index 8425f2127cd4a0..7594efd5912fa8 100644
--- a/spec/lib/activitypub/activity/create_spec.rb
+++ b/spec/lib/activitypub/activity/create_spec.rb
@@ -134,29 +134,67 @@ def activity_for_object(json)
subject.perform
end
- context 'when object has been edited' 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: '2022-01-22T15:00:00Z',
- updated: '2022-01-22T16:00:00Z',
+ published: '-0977-11-03T08:31:22Z',
}
end
- it 'creates status' do
+ it 'creates status with a valid creation date', :aggregate_failures do
+ status = sender.statuses.first
+
+ expect(status).to_not be_nil
+ expect(status.text).to eq 'Lorem ipsum'
+
+ expect(status.created_at).to be_within(30).of(Time.now.utc)
+ end
+ end
+
+ 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',
+ }
+ end
+
+ it 'creates status with a valid creation date', :aggregate_failures do
status = sender.statuses.first
expect(status).to_not be_nil
expect(status.text).to eq 'Lorem ipsum'
+
+ expect(status.created_at).to be_within(30).of(Time.now.utc)
+ end
+ end
+
+ 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',
+ published: '2022-01-22T15:00:00Z',
+ updated: '2022-01-22T16:00:00Z',
+ }
end
- it 'marks status as edited' do
+ it 'creates status with appropriate creation and edition dates', :aggregate_failures do
status = sender.statuses.first
expect(status).to_not be_nil
+ expect(status.text).to eq 'Lorem ipsum'
+
+ expect(status.created_at).to eq '2022-01-22T15:00:00Z'.to_datetime
+
expect(status.edited?).to be true
+ expect(status.edited_at).to eq '2022-01-22T16:00:00Z'.to_datetime
end
end
From 8d02e58ff42d28f62a354c1ce8282924d54c455f Mon Sep 17 00:00:00 2001
From: Claire
Date: Thu, 16 Nov 2023 14:43:02 +0100
Subject: [PATCH 19/28] Fix upper border radius of onboarding columns (#27890)
---
app/javascript/styles/mastodon/components.scss | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss
index 8c88b7dd6ccb6b..fbad5e2dbf896a 100644
--- a/app/javascript/styles/mastodon/components.scss
+++ b/app/javascript/styles/mastodon/components.scss
@@ -2239,8 +2239,7 @@ $ui-header-height: 55px;
> .scrollable {
background: $ui-base-color;
- border-bottom-left-radius: 4px;
- border-bottom-right-radius: 4px;
+ border-radius: 0 0 4px 4px;
}
}
From 252ea2fc67a8e95532c1887fc9f2842ddae189f3 Mon Sep 17 00:00:00 2001
From: Jonathan de Jong
Date: Fri, 27 Oct 2023 16:55:00 +0200
Subject: [PATCH 20/28] Have `Follow` activities bypass availability (#27586)
Co-authored-by: Claire
---
app/services/follow_service.rb | 2 +-
app/workers/activitypub/delivery_worker.rb | 5 +++--
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/app/services/follow_service.rb b/app/services/follow_service.rb
index feea40e3c0a945..1aa0241fe62325 100644
--- a/app/services/follow_service.rb
+++ b/app/services/follow_service.rb
@@ -71,7 +71,7 @@ def request_follow!
if @target_account.local?
LocalNotificationWorker.perform_async(@target_account.id, follow_request.id, follow_request.class.name, 'follow_request')
elsif @target_account.activitypub?
- ActivityPub::DeliveryWorker.perform_async(build_json(follow_request), @source_account.id, @target_account.inbox_url)
+ ActivityPub::DeliveryWorker.perform_async(build_json(follow_request), @source_account.id, @target_account.inbox_url, { 'bypass_availability' => true })
end
follow_request
diff --git a/app/workers/activitypub/delivery_worker.rb b/app/workers/activitypub/delivery_worker.rb
index 7c1c14766b70a5..376c237a98493a 100644
--- a/app/workers/activitypub/delivery_worker.rb
+++ b/app/workers/activitypub/delivery_worker.rb
@@ -23,9 +23,10 @@ class ActivityPub::DeliveryWorker
HEADERS = { 'Content-Type' => 'application/activity+json' }.freeze
def perform(json, source_account_id, inbox_url, options = {})
- return unless DeliveryFailureTracker.available?(inbox_url)
-
@options = options.with_indifferent_access
+
+ return unless @options[:bypass_availability] || DeliveryFailureTracker.available?(inbox_url)
+
@json = json
@source_account = Account.find(source_account_id)
@inbox_url = inbox_url
From e11100d7824258cafbd08b56945f340720a8029a Mon Sep 17 00:00:00 2001
From: Claire
Date: Mon, 27 Nov 2023 14:25:54 +0100
Subject: [PATCH 21/28] Clamp dates when serializing to Elasticsearch API
(#28081)
---
app/chewy/accounts_index.rb | 4 +++-
app/chewy/concerns/datetime_clamping_concern.rb | 14 ++++++++++++++
app/chewy/public_statuses_index.rb | 4 +++-
app/chewy/statuses_index.rb | 4 +++-
app/chewy/tags_index.rb | 4 +++-
5 files changed, 26 insertions(+), 4 deletions(-)
create mode 100644 app/chewy/concerns/datetime_clamping_concern.rb
diff --git a/app/chewy/accounts_index.rb b/app/chewy/accounts_index.rb
index 00db257ac711db..59f2f991f2bda0 100644
--- a/app/chewy/accounts_index.rb
+++ b/app/chewy/accounts_index.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class AccountsIndex < Chewy::Index
+ include DatetimeClampingConcern
+
settings index: index_preset(refresh_interval: '30s'), analysis: {
filter: {
english_stop: {
@@ -60,7 +62,7 @@ class AccountsIndex < Chewy::Index
field(:following_count, type: 'long')
field(:followers_count, type: 'long')
field(:properties, type: 'keyword', value: ->(account) { account.searchable_properties })
- field(:last_status_at, type: 'date', value: ->(account) { account.last_status_at || account.created_at })
+ field(:last_status_at, type: 'date', value: ->(account) { clamp_date(account.last_status_at || account.created_at) })
field(:display_name, type: 'text', analyzer: 'verbatim') { field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'verbatim' }
field(:username, type: 'text', analyzer: 'verbatim', value: ->(account) { [account.username, account.domain].compact.join('@') }) { field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'verbatim' }
field(:text, type: 'text', analyzer: 'verbatim', value: ->(account) { account.searchable_text }) { field :stemmed, type: 'text', analyzer: 'natural' }
diff --git a/app/chewy/concerns/datetime_clamping_concern.rb b/app/chewy/concerns/datetime_clamping_concern.rb
new file mode 100644
index 00000000000000..7f176b6e5489f4
--- /dev/null
+++ b/app/chewy/concerns/datetime_clamping_concern.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module DatetimeClampingConcern
+ extend ActiveSupport::Concern
+
+ MIN_ISO8601_DATETIME = '0000-01-01T00:00:00Z'.to_datetime.freeze
+ MAX_ISO8601_DATETIME = '9999-12-31T23:59:59Z'.to_datetime.freeze
+
+ class_methods do
+ def clamp_date(datetime)
+ datetime.clamp(MIN_ISO8601_DATETIME, MAX_ISO8601_DATETIME)
+ end
+ end
+end
diff --git a/app/chewy/public_statuses_index.rb b/app/chewy/public_statuses_index.rb
index 4be204d4a9bfd4..076f72e5258ae5 100644
--- a/app/chewy/public_statuses_index.rb
+++ b/app/chewy/public_statuses_index.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class PublicStatusesIndex < Chewy::Index
+ include DatetimeClampingConcern
+
settings index: index_preset(refresh_interval: '30s', number_of_shards: 5), analysis: {
filter: {
english_stop: {
@@ -62,6 +64,6 @@ class PublicStatusesIndex < Chewy::Index
field(:tags, type: 'text', analyzer: 'hashtag', value: ->(status) { status.tags.map(&:display_name) })
field(:language, type: 'keyword')
field(:properties, type: 'keyword', value: ->(status) { status.searchable_properties })
- field(:created_at, type: 'date')
+ field(:created_at, type: 'date', value: ->(status) { clamp_date(status.created_at) })
end
end
diff --git a/app/chewy/statuses_index.rb b/app/chewy/statuses_index.rb
index 6b25dc9dff8d33..c717de66ff465b 100644
--- a/app/chewy/statuses_index.rb
+++ b/app/chewy/statuses_index.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class StatusesIndex < Chewy::Index
+ include DatetimeClampingConcern
+
settings index: index_preset(refresh_interval: '30s', number_of_shards: 5), analysis: {
filter: {
english_stop: {
@@ -60,6 +62,6 @@ class StatusesIndex < Chewy::Index
field(:searchable_by, type: 'long', value: ->(status) { status.searchable_by })
field(:language, type: 'keyword')
field(:properties, type: 'keyword', value: ->(status) { status.searchable_properties })
- field(:created_at, type: 'date')
+ field(:created_at, type: 'date', value: ->(status) { clamp_date(status.created_at) })
end
end
diff --git a/app/chewy/tags_index.rb b/app/chewy/tags_index.rb
index 5b6349a964963a..c99218a47fcdcb 100644
--- a/app/chewy/tags_index.rb
+++ b/app/chewy/tags_index.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class TagsIndex < Chewy::Index
+ include DatetimeClampingConcern
+
settings index: index_preset(refresh_interval: '30s'), analysis: {
analyzer: {
content: {
@@ -42,6 +44,6 @@ class TagsIndex < Chewy::Index
field(:name, type: 'text', analyzer: 'content', value: :display_name) { field(:edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'content') }
field(:reviewed, type: 'boolean', value: ->(tag) { tag.reviewed? })
field(:usage, type: 'long', value: ->(tag, crutches) { tag.history.aggregate(crutches.time_period).accounts })
- field(:last_status_at, type: 'date', value: ->(tag) { tag.last_status_at || tag.created_at })
+ field(:last_status_at, type: 'date', value: ->(tag) { clamp_date(tag.last_status_at || tag.created_at) })
end
end
From 09115731d617c556135ac465421645ed54e92ca9 Mon Sep 17 00:00:00 2001
From: Claire
Date: Fri, 24 Nov 2023 10:31:28 +0100
Subject: [PATCH 22/28] Change GIF max matrix size error to explicitly mention
GIF files (#27927)
---
app/models/concerns/attachmentable.rb | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/app/models/concerns/attachmentable.rb b/app/models/concerns/attachmentable.rb
index c0ee1bdce7cbb6..4cdbdeb4738a49 100644
--- a/app/models/concerns/attachmentable.rb
+++ b/app/models/concerns/attachmentable.rb
@@ -52,9 +52,13 @@ def check_image_dimension(attachment)
return if attachment.blank? || !/image.*/.match?(attachment.content_type) || attachment.queued_for_write[:original].blank?
width, height = FastImage.size(attachment.queued_for_write[:original].path)
- matrix_limit = attachment.content_type == 'image/gif' ? GIF_MATRIX_LIMIT : MAX_MATRIX_LIMIT
+ return unless width.present? && height.present?
- raise Mastodon::DimensionsValidationError, "#{width}x#{height} images are not supported" if width.present? && height.present? && (width * height > matrix_limit)
+ if attachment.content_type == 'image/gif' && width * height > GIF_MATRIX_LIMIT
+ raise Mastodon::DimensionsValidationError, "#{width}x#{height} GIF files are not supported"
+ elsif width * height > MAX_MATRIX_LIMIT
+ raise Mastodon::DimensionsValidationError, "#{width}x#{height} images are not supported"
+ end
end
def appropriate_extension(attachment)
From 7b9496322fd726a70c6fec1eee77c8cd04f0fd9e Mon Sep 17 00:00:00 2001
From: Claire
Date: Thu, 30 Nov 2023 12:45:54 +0100
Subject: [PATCH 23/28] Change dismissed banners to be stored server-side
(#27055)
---
.../components/dismissable_banner.tsx | 25 ++++++++++++++++---
app/javascript/mastodon/reducers/settings.js | 9 +++++++
2 files changed, 31 insertions(+), 3 deletions(-)
diff --git a/app/javascript/mastodon/components/dismissable_banner.tsx b/app/javascript/mastodon/components/dismissable_banner.tsx
index 04a28e3cbe7c65..6984691409ad53 100644
--- a/app/javascript/mastodon/components/dismissable_banner.tsx
+++ b/app/javascript/mastodon/components/dismissable_banner.tsx
@@ -1,9 +1,16 @@
+/* eslint-disable @typescript-eslint/no-unsafe-call,
+ @typescript-eslint/no-unsafe-return,
+ @typescript-eslint/no-unsafe-assignment,
+ @typescript-eslint/no-unsafe-member-access
+ -- the settings store is not yet typed */
import type { PropsWithChildren } from 'react';
-import { useCallback, useState } from 'react';
+import { useCallback, useState, useEffect } from 'react';
import { defineMessages, useIntl } from 'react-intl';
+import { changeSetting } from 'mastodon/actions/settings';
import { bannerSettings } from 'mastodon/settings';
+import { useAppSelector, useAppDispatch } from 'mastodon/store';
import { IconButton } from './icon_button';
@@ -19,13 +26,25 @@ export const DismissableBanner: React.FC> = ({
id,
children,
}) => {
- const [visible, setVisible] = useState(!bannerSettings.get(id));
+ const dismissed = useAppSelector((state) =>
+ state.settings.getIn(['dismissed_banners', id], false),
+ );
+ const dispatch = useAppDispatch();
+
+ const [visible, setVisible] = useState(!bannerSettings.get(id) && !dismissed);
const intl = useIntl();
const handleDismiss = useCallback(() => {
setVisible(false);
bannerSettings.set(id, true);
- }, [id]);
+ dispatch(changeSetting(['dismissed_banners', id], true));
+ }, [id, dispatch]);
+
+ useEffect(() => {
+ if (!visible && !dismissed) {
+ dispatch(changeSetting(['dismissed_banners', id], true));
+ }
+ }, [id, dispatch, visible, dismissed]);
if (!visible) {
return null;
diff --git a/app/javascript/mastodon/reducers/settings.js b/app/javascript/mastodon/reducers/settings.js
index 07d1bda0f4d08b..a605ecbb8bd62d 100644
--- a/app/javascript/mastodon/reducers/settings.js
+++ b/app/javascript/mastodon/reducers/settings.js
@@ -100,6 +100,15 @@ const initialState = ImmutableMap({
body: '',
}),
}),
+
+ dismissed_banners: ImmutableMap({
+ 'public_timeline': false,
+ 'community_timeline': false,
+ 'home.explore_prompt': false,
+ 'explore/links': false,
+ 'explore/statuses': false,
+ 'explore/tags': false,
+ }),
});
const defaultColumns = fromJS([
From 4b8fe9df73cf27db68f3c92178e37eff9deb3bd3 Mon Sep 17 00:00:00 2001
From: Claire
Date: Mon, 27 Nov 2023 15:00:52 +0100
Subject: [PATCH 24/28] Bump version to v4.2.2
---
CHANGELOG.md | 27 +++++++++++++++++++++++++++
SECURITY.md | 2 +-
docker-compose.yml | 6 +++---
lib/mastodon/version.rb | 2 +-
4 files changed, 32 insertions(+), 5 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f9303f011550e2..54bc2b4038ea4e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,33 @@
All notable changes to this project will be documented in this file.
+## [4.2.2] - 2023-12-04
+
+### Changed
+
+- Change dismissed banners to be stored server-side ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27055))
+- Change GIF max matrix size error to explicitly mention GIF files ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27927))
+- Change `Follow` activities delivery to bypass availability check ([ShadowJonathan](https://github.com/mastodon/mastodon/pull/27586))
+- Change single-column navigation notice to be displayed outside of the logo container ([renchap](https://github.com/mastodon/mastodon/pull/27462), [renchap](https://github.com/mastodon/mastodon/pull/27476))
+- Change Content-Security-Policy to be tighter on media paths ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26889))
+- Change post language code to include country code when relevant ([gunchleoc](https://github.com/mastodon/mastodon/pull/27099), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/27207))
+
+### Fixed
+
+- Fix upper border radius of onboarding columns ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27890))
+- Fix incoming status creation date not being restricted to standard ISO8601 ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27655), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/28081))
+- Fix some posts from threads received out-of-order sometimes not being inserted into timelines ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27653))
+- Fix posts from force-sensitized accounts being able to trend ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27620))
+- Fix error when trying to delete already-deleted file with OpenStack Swift ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27569))
+- Fix batch attachment deletion when using OpenStack Swift ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27554))
+- Fix processing LDSigned activities from actors with unknown public keys ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27474))
+- Fix error and incorrect URLs in `/api/v1/accounts/:id/featured_tags` for remote accounts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27459))
+- Fix report processing notice not mentioning the report number when performing a custom action ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27442))
+- Fix handling of `inLanguage` attribute in preview card processing ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27423))
+- Fix own posts being removed from home timeline when unfollowing a used hashtag ([kmycode](https://github.com/mastodon/mastodon/pull/27391))
+- Fix some link anchors being recognized as hashtags ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27271), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/27584))
+- Fix format-dependent redirects being cached regardless of requested format ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27634))
+
## [4.2.1] - 2023-10-10
### Added
diff --git a/SECURITY.md b/SECURITY.md
index 3e13377db63eb4..954ff73a247425 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -17,6 +17,6 @@ A "vulnerability in Mastodon" is a vulnerability in the code distributed through
| ------- | ---------------- |
| 4.2.x | Yes |
| 4.1.x | Yes |
-| 4.0.x | Until 2023-10-31 |
+| 4.0.x | No |
| 3.5.x | Until 2023-12-31 |
| < 3.5 | No |
diff --git a/docker-compose.yml b/docker-compose.yml
index 1a180b089070f1..efb8e528eb826c 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -56,7 +56,7 @@ services:
web:
build: .
- image: ghcr.io/mastodon/mastodon:v4.2.1
+ image: ghcr.io/mastodon/mastodon:v4.2.2
restart: always
env_file: .env.production
command: bash -c "rm -f /mastodon/tmp/pids/server.pid; bundle exec rails s -p 3000"
@@ -77,7 +77,7 @@ services:
streaming:
build: .
- image: ghcr.io/mastodon/mastodon:v4.2.1
+ image: ghcr.io/mastodon/mastodon:v4.2.2
restart: always
env_file: .env.production
command: node ./streaming
@@ -95,7 +95,7 @@ services:
sidekiq:
build: .
- image: ghcr.io/mastodon/mastodon:v4.2.1
+ image: ghcr.io/mastodon/mastodon:v4.2.2
restart: always
env_file: .env.production
command: bundle exec sidekiq
diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb
index 00538d07cc5866..7d5578d8232377 100644
--- a/lib/mastodon/version.rb
+++ b/lib/mastodon/version.rb
@@ -13,7 +13,7 @@ def minor
end
def patch
- 1
+ 2
end
def default_prerelease
From 71b60b09f494ddd4670128cf252b6372a293a51e Mon Sep 17 00:00:00 2001
From: Claire
Date: Tue, 5 Dec 2023 14:15:33 +0100
Subject: [PATCH 25/28] Update dependency json-ld to v3.3.1
---
Gemfile.lock | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/Gemfile.lock b/Gemfile.lock
index a003cd18dd0d55..76925a2bf29975 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -148,6 +148,7 @@ GEM
net-http-persistent (~> 4.0)
nokogiri (~> 1, >= 1.10.8)
base64 (0.1.1)
+ bcp47_spec (0.2.1)
bcrypt (3.1.18)
better_errors (2.10.1)
erubi (>= 1.0.0)
@@ -377,19 +378,19 @@ GEM
ipaddress (0.8.3)
jmespath (1.6.2)
json (2.6.3)
- json-canonicalization (0.3.2)
+ json-canonicalization (1.0.0)
json-jwt (1.15.3)
activesupport (>= 4.2)
aes_key_wrap
bindata
httpclient
- json-ld (3.2.5)
+ json-ld (3.3.1)
htmlentities (~> 4.3)
- json-canonicalization (~> 0.3, >= 0.3.2)
+ json-canonicalization (~> 1.0)
link_header (~> 0.0, >= 0.0.8)
multi_json (~> 1.15)
rack (>= 2.2, < 4)
- rdf (~> 3.2, >= 3.2.10)
+ rdf (~> 3.3)
json-ld-preloaded (3.2.2)
json-ld (~> 3.2)
rdf (~> 3.2)
@@ -593,7 +594,8 @@ GEM
zeitwerk (~> 2.5)
rainbow (3.1.1)
rake (13.0.6)
- rdf (3.2.11)
+ rdf (3.3.1)
+ bcp47_spec (~> 0.2)
link_header (~> 0.0, >= 0.0.8)
rdf-normalize (0.6.1)
rdf (~> 3.2)
From 90371a4fc4da90c692e632b3231d804e310de916 Mon Sep 17 00:00:00 2001
From: Claire
Date: Tue, 5 Dec 2023 14:21:18 +0100
Subject: [PATCH 26/28] Bump version to v4.2.3
---
CHANGELOG.md | 6 ++++++
docker-compose.yml | 6 +++---
lib/mastodon/version.rb | 2 +-
3 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 54bc2b4038ea4e..5f970519e4ad58 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,12 @@
All notable changes to this project will be documented in this file.
+## [4.2.3] - 2023-12-05
+
+### Fixed
+
+- Fix dependency on `json-canonicalization` version that has been made unavailable since last release
+
## [4.2.2] - 2023-12-04
### Changed
diff --git a/docker-compose.yml b/docker-compose.yml
index efb8e528eb826c..dda71cb091089c 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -56,7 +56,7 @@ services:
web:
build: .
- image: ghcr.io/mastodon/mastodon:v4.2.2
+ image: ghcr.io/mastodon/mastodon:v4.2.3
restart: always
env_file: .env.production
command: bash -c "rm -f /mastodon/tmp/pids/server.pid; bundle exec rails s -p 3000"
@@ -77,7 +77,7 @@ services:
streaming:
build: .
- image: ghcr.io/mastodon/mastodon:v4.2.2
+ image: ghcr.io/mastodon/mastodon:v4.2.3
restart: always
env_file: .env.production
command: node ./streaming
@@ -95,7 +95,7 @@ services:
sidekiq:
build: .
- image: ghcr.io/mastodon/mastodon:v4.2.2
+ image: ghcr.io/mastodon/mastodon:v4.2.3
restart: always
env_file: .env.production
command: bundle exec sidekiq
diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb
index 7d5578d8232377..2e68df47445095 100644
--- a/lib/mastodon/version.rb
+++ b/lib/mastodon/version.rb
@@ -13,7 +13,7 @@ def minor
end
def patch
- 2
+ 3
end
def default_prerelease
From 73d712ae2a8f82aeeef15e0db2b6f00c9a30c5af Mon Sep 17 00:00:00 2001
From: Nina Pypchenko <22447785+nina-py@users.noreply.github.com>
Date: Wed, 6 Dec 2023 09:57:29 +1100
Subject: [PATCH 27/28] Fix CodeQL errors
---
spec/models/tag_spec.rb | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/spec/models/tag_spec.rb b/spec/models/tag_spec.rb
index 6177b7a25a6d02..77d9ba9a83b3bc 100644
--- a/spec/models/tag_spec.rb
+++ b/spec/models/tag_spec.rb
@@ -29,15 +29,15 @@
end
it 'does not match URLs with hashtag-like anchors' do
- expect(subject.match('https://en.wikipedia.org/wiki/Ghostbusters_(song)#Lawsuit')).to be_nil
+ expect(subject.match('https://en\.wikipedia\.org/wiki/Ghostbusters_(song)#Lawsuit')).to be_nil
end
it 'does not match URLs with hashtag-like anchors after a numeral' do
- expect(subject.match('https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111895#c4')).to be_nil
+ expect(subject.match('https://gcc\.gnu\.org/bugzilla/show_bug.cgi?id=111895#c4')).to be_nil
end
it 'does not match URLs with hashtag-like anchors after an empty query parameter' do
- expect(subject.match('https://en.wikipedia.org/wiki/Ghostbusters_(song)?foo=#Lawsuit')).to be_nil
+ expect(subject.match('https://en\.wikipedia\.org/wiki/Ghostbusters_(song)?foo=#Lawsuit')).to be_nil
end
it 'matches #aesthetic' do
From e04a2c92007b2a265449244eee8aebf7ecc0e071 Mon Sep 17 00:00:00 2001
From: Nina Pypchenko <22447785+nina-py@users.noreply.github.com>
Date: Wed, 6 Dec 2023 10:09:06 +1100
Subject: [PATCH 28/28] Fix linting errors
---
.glean/metrics.yaml | 5 ++---
app/javascript/mastodon/features/interaction_modal/index.jsx | 1 -
app/javascript/packs/auto_redirect_sign-in.js | 4 ++--
app/views/auth/sessions/new.html.haml | 2 +-
4 files changed, 5 insertions(+), 7 deletions(-)
diff --git a/.glean/metrics.yaml b/.glean/metrics.yaml
index 62abbf3691c0c5..0db8ae76c263fd 100644
--- a/.glean/metrics.yaml
+++ b/.glean/metrics.yaml
@@ -3,7 +3,7 @@
---
$schema: moz://mozilla.org/schemas/glean/metrics/2-0-0
-
+
identifiers:
fxa_account_id:
type: string
@@ -66,7 +66,6 @@ identifiers:
send_in_pings:
- events
-
user_agent:
type: string
description: >
@@ -108,4 +107,4 @@ backend:
object_state:
description: >
A JSON representation of the latest state of the object.
- type: string
\ No newline at end of file
+ type: string
diff --git a/app/javascript/mastodon/features/interaction_modal/index.jsx b/app/javascript/mastodon/features/interaction_modal/index.jsx
index 8dba0fa8c1756e..6971eacaf7d7be 100644
--- a/app/javascript/mastodon/features/interaction_modal/index.jsx
+++ b/app/javascript/mastodon/features/interaction_modal/index.jsx
@@ -376,7 +376,6 @@ class InteractionModal extends React.PureComponent {
}
let signupButton;
- let signUpOrSignInButton;
if (sso_redirect) {
signupButton = (
diff --git a/app/javascript/packs/auto_redirect_sign-in.js b/app/javascript/packs/auto_redirect_sign-in.js
index 1b743acd737c4b..0310e1d1a0aeba 100644
--- a/app/javascript/packs/auto_redirect_sign-in.js
+++ b/app/javascript/packs/auto_redirect_sign-in.js
@@ -3,8 +3,8 @@ import ready from '../mastodon/ready';
ready(() => {
const form = document.querySelector('#sso_form')
form.insertAdjacentHTML('beforeend', 'Redirecting...
')
- form.elements['commit'].style.setProperty('visibility', 'hidden')
- form.elements['intent'].value = localStorage.getItem('mozsoc.auth_intent')
+ form.elements.commit.style.setProperty('visibility', 'hidden')
+ form.elements.intent.value = localStorage.getItem('mozsoc.auth_intent')
localStorage.removeItem('mozsoc.auth_intent');
form.submit();
});
diff --git a/app/views/auth/sessions/new.html.haml b/app/views/auth/sessions/new.html.haml
index 48f92025a49f74..d130177c1006d3 100644
--- a/app/views/auth/sessions/new.html.haml
+++ b/app/views/auth/sessions/new.html.haml
@@ -26,4 +26,4 @@
= hidden_field_tag 'intent'
= submit_tag(t('auth.login'), class: 'button button--block')
- else
- .form-footer= render 'auth/shared/links'
\ No newline at end of file
+ .form-footer= render 'auth/shared/links'