From 1dbfc1a55f21cbf13ee9412e36b94f2c0e48e469 Mon Sep 17 00:00:00 2001 From: Gyuhwan Park Date: Fri, 2 Apr 2021 13:46:00 +0900 Subject: [PATCH 01/81] feautre(config/themes): add custom fonts --- .gitignore | 4 ++++ app/javascript/styles/fonts/chosunilbonm.scss | 6 ++++++ app/javascript/styles/fonts/sawarabi-mincho.scss | 8 ++++++++ app/javascript/styles/goyang.scss | 12 ++++++++++++ app/javascript/styles/myeongjo.scss | 15 +++++++++++++++ app/javascript/styles/myeongjo_kronly.scss | 14 ++++++++++++++ config/themes.yml | 4 ++++ 7 files changed, 63 insertions(+) create mode 100644 app/javascript/styles/fonts/chosunilbonm.scss create mode 100644 app/javascript/styles/fonts/sawarabi-mincho.scss create mode 100644 app/javascript/styles/goyang.scss create mode 100644 app/javascript/styles/myeongjo.scss create mode 100644 app/javascript/styles/myeongjo_kronly.scss diff --git a/.gitignore b/.gitignore index 4545270b30b38e..6ada94fbf56dae 100644 --- a/.gitignore +++ b/.gitignore @@ -67,3 +67,7 @@ yarn-debug.log # Ignore Docker option files docker-compose.override.yml + +# twingyeo.kr +*.woff +*.woff2 diff --git a/app/javascript/styles/fonts/chosunilbonm.scss b/app/javascript/styles/fonts/chosunilbonm.scss new file mode 100644 index 00000000000000..9d483e5e957b6e --- /dev/null +++ b/app/javascript/styles/fonts/chosunilbonm.scss @@ -0,0 +1,6 @@ +@font-face { + font-family: 'ChosunilboNM'; + src: local('ChosunilboNM'), + url('../fonts/chosunilbonm/chosunilbonm.woff2') format('woff2'); +} + diff --git a/app/javascript/styles/fonts/sawarabi-mincho.scss b/app/javascript/styles/fonts/sawarabi-mincho.scss new file mode 100644 index 00000000000000..8f72cebfdbcfe3 --- /dev/null +++ b/app/javascript/styles/fonts/sawarabi-mincho.scss @@ -0,0 +1,8 @@ +@font-face { + font-family: 'Sawarabi Mincho'; + src: local('Sawarabi Mincho'), + url('../fonts/sawarabi-mincho/sawarabi-mincho-medium.woff2') format('woff2'); + font-weight: normal; + unicode-range: U+3000-303F, U+3040-309F, U+30A0-30FF, U+FF00-FFEF, U+4E00-9FAF; +} + diff --git a/app/javascript/styles/goyang.scss b/app/javascript/styles/goyang.scss new file mode 100644 index 00000000000000..e588408ba277fb --- /dev/null +++ b/app/javascript/styles/goyang.scss @@ -0,0 +1,12 @@ +@import 'application'; + +@font-face { + font-family: 'Goyang-NJ'; + src: local('GoyangNJ'), + url('../fonts/goyangnj/goyangnj.woff'); + unicode-range: U+20-7F, U+3130-318F, U+AC00-D7AF; +} + +html, body { + font-family: 'Goyang-NJ', 'Arial', sans-serif !important; +} diff --git a/app/javascript/styles/myeongjo.scss b/app/javascript/styles/myeongjo.scss new file mode 100644 index 00000000000000..c9a4e22e849352 --- /dev/null +++ b/app/javascript/styles/myeongjo.scss @@ -0,0 +1,15 @@ +@import 'application'; +@import './fonts/chosunilbonm.scss'; +@import './fonts/sawarabi-mincho.scss'; + +@font-face { + font-family: 'Hiragino Mincho ProN'; + src: local('Hiragino Mincho ProN'); + + unicode-range: U+3000-303F, U+3040-309F, U+30A0-30FF, U+FF00-FFEF, U+4E00-9FAF; +} + + +html, body { + font-family: 'Hiragino Mincho ProN', 'Sawarabi Mincho', 'ChosunilboNM', serif !important; +} diff --git a/app/javascript/styles/myeongjo_kronly.scss b/app/javascript/styles/myeongjo_kronly.scss new file mode 100644 index 00000000000000..aad37fa758e511 --- /dev/null +++ b/app/javascript/styles/myeongjo_kronly.scss @@ -0,0 +1,14 @@ +@import 'application'; +@import './fonts/chosunilbonm.scss'; + +@font-face { + font-family: 'Hiragino Mincho ProN'; + src: local('Hiragino Mincho ProN'); + + unicode-range: U+3000-303F, U+3040-309F, U+30A0-30FF, U+FF00-FFEF, U+4E00-9FAF; +} + + +html, body { + font-family: 'Hiragino Mincho ProN', 'ChosunilboNM', serif !important; +} diff --git a/config/themes.yml b/config/themes.yml index 9c21c9459f3bcf..ad9e3376c935db 100644 --- a/config/themes.yml +++ b/config/themes.yml @@ -1,3 +1,7 @@ default: styles/application.scss contrast: styles/contrast.scss mastodon-light: styles/mastodon-light.scss + +goyang: styles/goyang.scss +myeongjo: styles/myeongjo.scss +myeongjo_kronly: styles/myeongjo_kronly.scss From e62f488be5d468a2a09a9f0890ef0dad9209808d Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 23 Jun 2021 23:55:47 +0200 Subject: [PATCH 02/81] Fix newlines in accout notes added by the Move handler (#16415) * Fix newlines in account notes added by the move handler * Make MoveWorker more robust --- app/workers/move_worker.rb | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/app/workers/move_worker.rb b/app/workers/move_worker.rb index 39e3213166683f..53a6b87f194a2c 100644 --- a/app/workers/move_worker.rb +++ b/app/workers/move_worker.rb @@ -13,9 +13,13 @@ def perform(source_account_id, target_account_id) queue_follow_unfollows! end + @deferred_error = nil + copy_account_notes! carry_blocks_over! carry_mutes_over! + + raise @deferred_error unless @deferred_error.nil? rescue ActiveRecord::RecordNotFound true end @@ -36,6 +40,8 @@ def queue_follow_unfollows! @source_account.followers.local.select(:id).find_in_batches do |accounts| UnfollowFollowWorker.push_bulk(accounts.map(&:id)) { |follower_id| [follower_id, @source_account.id, @target_account.id, bypass_locked] } + rescue => e + @deferred_error = e end end @@ -47,10 +53,12 @@ def copy_account_notes! new_note = AccountNote.find_by(account: note.account, target_account: @target_account) if new_note.nil? - AccountNote.create!(account: note.account, target_account: @target_account, comment: [text, note.comment].join('\n')) + AccountNote.create!(account: note.account, target_account: @target_account, comment: [text, note.comment].join("\n")) else - new_note.update!(comment: [text, note.comment, '\n', new_note.comment].join('\n')) + new_note.update!(comment: [text, note.comment, "\n", new_note.comment].join("\n")) end + rescue => e + @deferred_error = e end end @@ -60,6 +68,8 @@ def carry_blocks_over! BlockService.new.call(block.account, @target_account) add_account_note_if_needed!(block.account, 'move_handler.carry_blocks_over_text') end + rescue => e + @deferred_error = e end end @@ -67,6 +77,8 @@ def carry_mutes_over! @source_account.muted_by_relationships.where(account: Account.local).find_each do |mute| MuteService.new.call(mute.account, @target_account, notifications: mute.hide_notifications) unless mute.account.muting?(@target_account) || mute.account.following?(@target_account) add_account_note_if_needed!(mute.account, 'move_handler.carry_mutes_over_text') + rescue => e + @deferred_error = e end end From 987f945930324f66f363287286f87d4ca337be5a Mon Sep 17 00:00:00 2001 From: Takeshi Umeda Date: Sun, 8 Aug 2021 22:31:02 +0900 Subject: [PATCH 03/81] Fix when MoveWorker cannot get locale from remote account (#16576) --- app/workers/move_worker.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/workers/move_worker.rb b/app/workers/move_worker.rb index 53a6b87f194a2c..cc2c17d32bf602 100644 --- a/app/workers/move_worker.rb +++ b/app/workers/move_worker.rb @@ -47,7 +47,7 @@ def queue_follow_unfollows! def copy_account_notes! AccountNote.where(target_account: @source_account).find_each do |note| - text = I18n.with_locale(note.account.user.locale || I18n.default_locale) do + text = I18n.with_locale(note.account.user&.locale || I18n.default_locale) do I18n.t('move_handler.copy_account_note_text', acct: @source_account.acct) end @@ -84,7 +84,7 @@ def carry_mutes_over! def add_account_note_if_needed!(account, id) unless AccountNote.where(account: account, target_account: @target_account).exists? - text = I18n.with_locale(account.user.locale || I18n.default_locale) do + text = I18n.with_locale(account.user&.locale || I18n.default_locale) do I18n.t(id, acct: @source_account.acct) end AccountNote.create!(account: account, target_account: @target_account, comment: text) From c02d6c46e3b46b21370465e3637c9b95cdd008d2 Mon Sep 17 00:00:00 2001 From: Takeshi Umeda Date: Mon, 9 Aug 2021 20:33:19 +0900 Subject: [PATCH 04/81] Fix invalid blurhash handling in Create activity (#16583) --- app/lib/activitypub/activity/create.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb index 9a296050721539..d70ce6103826ed 100644 --- a/app/lib/activitypub/activity/create.rb +++ b/app/lib/activitypub/activity/create.rb @@ -446,10 +446,14 @@ def unsupported_media_type?(mime_type) end def supported_blurhash?(blurhash) - components = blurhash.blank? ? nil : Blurhash.components(blurhash) + components = blurhash.blank? || !blurhash_valid_chars?(blurhash) ? nil : Blurhash.components(blurhash) components.present? && components.none? { |comp| comp > 5 } end + def blurhash_valid_chars?(blurhash) + /^[\w#$%*+-.:;=?@\[\]^{|}~]+$/.match?(blurhash) + end + def skip_download? return @skip_download if defined?(@skip_download) From 4f852448e1b5543117ee91b0f7cc6f5c6388d6c9 Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 11 Aug 2021 16:40:55 +0200 Subject: [PATCH 05/81] Fix crash when encountering invalid account fields (#16598) * Add test * Fix crash when encountering invalid account fields --- app/models/account.rb | 6 +++++- spec/services/activitypub/process_account_service_spec.rb | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/models/account.rb b/app/models/account.rb index 2c5455d8e57f4c..3c11b8e6f1f951 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -295,7 +295,11 @@ def also_known_as end def fields - (self[:fields] || []).map { |f| Field.new(self, f) } + (self[:fields] || []).map do |f| + Field.new(self, f) + rescue + nil + end.compact end def fields_attributes=(attributes) diff --git a/spec/services/activitypub/process_account_service_spec.rb b/spec/services/activitypub/process_account_service_spec.rb index 56e7f83211b4e0..1b1d878a7e6b30 100644 --- a/spec/services/activitypub/process_account_service_spec.rb +++ b/spec/services/activitypub/process_account_service_spec.rb @@ -12,6 +12,7 @@ attachment: [ { type: 'PropertyValue', name: 'Pronouns', value: 'They/them' }, { type: 'PropertyValue', name: 'Occupation', value: 'Unit test' }, + { type: 'PropertyValue', name: 'non-string', value: ['foo', 'bar'] }, ], }.with_indifferent_access end From f51c6cba1f88b68a23b86b164f08fb19e81c2497 Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 20 Aug 2021 08:40:33 +0200 Subject: [PATCH 06/81] Fix remotely-suspended accounts' toots being merged back into timelines (#16628) * Fix remotely-suspended accounts' toots being merged back into timelines * Mark remotely-deleted accounts as remotely suspended --- app/services/resolve_account_service.rb | 1 + app/services/unsuspend_account_service.rb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/services/resolve_account_service.rb b/app/services/resolve_account_service.rb index 5400612bfd194f..b266c019e29bdd 100644 --- a/app/services/resolve_account_service.rb +++ b/app/services/resolve_account_service.rb @@ -142,6 +142,7 @@ def not_yet_deleted? end def queue_deletion! + @account.suspend!(origin: :remote) AccountDeletionWorker.perform_async(@account.id, reserve_username: false, skip_activitypub: true) end diff --git a/app/services/unsuspend_account_service.rb b/app/services/unsuspend_account_service.rb index 949c670aac84c0..7e52a75944533a 100644 --- a/app/services/unsuspend_account_service.rb +++ b/app/services/unsuspend_account_service.rb @@ -7,7 +7,7 @@ def call(account) unsuspend! refresh_remote_account! - return if @account.nil? + return if @account.nil? || @account.suspended? merge_into_home_timelines! merge_into_list_timelines! From 2688f18d06340ff858bd66d50ea3e0fd1c288695 Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 25 Aug 2021 22:52:41 +0200 Subject: [PATCH 07/81] Fix authentication failures after going halfway through a sign-in attempt (#16607) * Add tests * Add security-related tests My first (unpublished) attempt at fixing the issues introduced (extremely hard-to-exploit) security vulnerabilities, addressing them in a test. * Fix authentication failures after going halfway through a sign-in attempt * Refactor `authenticate_with_sign_in_token` and `authenticate_with_two_factor` to make the two authentication steps more obvious --- app/controllers/auth/sessions_controller.rb | 16 ++- .../sign_in_token_authentication_concern.rb | 20 ++-- .../two_factor_authentication_concern.rb | 22 ++-- .../auth/sessions_controller_spec.rb | 109 ++++++++++++++++++ 4 files changed, 144 insertions(+), 23 deletions(-) diff --git a/app/controllers/auth/sessions_controller.rb b/app/controllers/auth/sessions_controller.rb index 13d158c67613a8..d2bd6c4a0da5fc 100644 --- a/app/controllers/auth/sessions_controller.rb +++ b/app/controllers/auth/sessions_controller.rb @@ -58,16 +58,20 @@ def webauthn_options protected def find_user - if session[:attempt_user_id] + if user_params[:email].present? + find_user_from_params + elsif session[:attempt_user_id] User.find_by(id: session[:attempt_user_id]) - else - user = User.authenticate_with_ldap(user_params) if Devise.ldap_authentication - user ||= User.authenticate_with_pam(user_params) if Devise.pam_authentication - user ||= User.find_for_authentication(email: user_params[:email]) - user end end + def find_user_from_params + user = User.authenticate_with_ldap(user_params) if Devise.ldap_authentication + user ||= User.authenticate_with_pam(user_params) if Devise.pam_authentication + user ||= User.find_for_authentication(email: user_params[:email]) + user + end + def user_params params.require(:user).permit(:email, :password, :otp_attempt, :sign_in_token_attempt, credential: {}) end diff --git a/app/controllers/concerns/sign_in_token_authentication_concern.rb b/app/controllers/concerns/sign_in_token_authentication_concern.rb index 3c95a4afd2cf66..33ea5cf8572124 100644 --- a/app/controllers/concerns/sign_in_token_authentication_concern.rb +++ b/app/controllers/concerns/sign_in_token_authentication_concern.rb @@ -16,14 +16,18 @@ def valid_sign_in_token_attempt?(user) end def authenticate_with_sign_in_token - user = self.resource = find_user - - if user.present? && session[:attempt_user_id].present? && session[:attempt_user_updated_at] != user.updated_at.to_s - restart_session - elsif user_params.key?(:sign_in_token_attempt) && session[:attempt_user_id] - authenticate_with_sign_in_token_attempt(user) - elsif user.present? && user.external_or_valid_password?(user_params[:password]) - prompt_for_sign_in_token(user) + if user_params[:email].present? + user = self.resource = find_user_from_params + prompt_for_sign_in_token(user) if user&.external_or_valid_password?(user_params[:password]) + elsif session[:attempt_user_id] + user = self.resource = User.find_by(id: session[:attempt_user_id]) + return if user.nil? + + if session[:attempt_user_updated_at] != user.updated_at.to_s + restart_session + elsif user_params.key?(:sign_in_token_attempt) + authenticate_with_sign_in_token_attempt(user) + end end end diff --git a/app/controllers/concerns/two_factor_authentication_concern.rb b/app/controllers/concerns/two_factor_authentication_concern.rb index 4d4ccf49c8ce67..402638eba91bd4 100644 --- a/app/controllers/concerns/two_factor_authentication_concern.rb +++ b/app/controllers/concerns/two_factor_authentication_concern.rb @@ -35,16 +35,20 @@ def valid_otp_attempt?(user) end def authenticate_with_two_factor - user = self.resource = find_user + if user_params[:email].present? + user = self.resource = find_user_from_params + prompt_for_two_factor(user) if user&.external_or_valid_password?(user_params[:password]) + elsif session[:attempt_user_id] + user = self.resource = User.find_by(id: session[:attempt_user_id]) + return if user.nil? - if user.present? && session[:attempt_user_id].present? && session[:attempt_user_updated_at] != user.updated_at.to_s - restart_session - elsif user.webauthn_enabled? && user_params.key?(:credential) && session[:attempt_user_id] - authenticate_with_two_factor_via_webauthn(user) - elsif user_params.key?(:otp_attempt) && session[:attempt_user_id] - authenticate_with_two_factor_via_otp(user) - elsif user.present? && user.external_or_valid_password?(user_params[:password]) - prompt_for_two_factor(user) + if session[:attempt_user_updated_at] != user.updated_at.to_s + restart_session + elsif user.webauthn_enabled? && user_params.key?(:credential) + authenticate_with_two_factor_via_webauthn(user) + elsif user_params.key?(:otp_attempt) + authenticate_with_two_factor_via_otp(user) + end end end diff --git a/spec/controllers/auth/sessions_controller_spec.rb b/spec/controllers/auth/sessions_controller_spec.rb index d03ae51e869a18..051a0807dba5a1 100644 --- a/spec/controllers/auth/sessions_controller_spec.rb +++ b/spec/controllers/auth/sessions_controller_spec.rb @@ -206,6 +206,38 @@ end end + context 'using email and password after an unfinished log-in attempt to a 2FA-protected account' do + let!(:other_user) do + Fabricate(:user, email: 'z@y.com', password: 'abcdefgh', otp_required_for_login: true, otp_secret: User.generate_otp_secret(32)) + end + + before do + post :create, params: { user: { email: other_user.email, password: other_user.password } } + post :create, params: { user: { email: user.email, password: user.password } } + end + + it 'renders two factor authentication page' do + expect(controller).to render_template("two_factor") + expect(controller).to render_template(partial: "_otp_authentication_form") + end + end + + context 'using email and password after an unfinished log-in attempt with a sign-in token challenge' do + let!(:other_user) do + Fabricate(:user, email: 'z@y.com', password: 'abcdefgh', otp_required_for_login: false, current_sign_in_at: 1.month.ago) + end + + before do + post :create, params: { user: { email: other_user.email, password: other_user.password } } + post :create, params: { user: { email: user.email, password: user.password } } + end + + it 'renders two factor authentication page' do + expect(controller).to render_template("two_factor") + expect(controller).to render_template(partial: "_otp_authentication_form") + end + end + context 'using upcase email and password' do before do post :create, params: { user: { email: user.email.upcase, password: user.password } } @@ -231,6 +263,21 @@ end end + context 'using a valid OTP, attempting to leverage previous half-login to bypass password auth' do + let!(:other_user) do + Fabricate(:user, email: 'z@y.com', password: 'abcdefgh', otp_required_for_login: false, current_sign_in_at: 1.month.ago) + end + + before do + post :create, params: { user: { email: other_user.email, password: other_user.password } } + post :create, params: { user: { email: user.email, otp_attempt: user.current_otp } }, session: { attempt_user_updated_at: user.updated_at.to_s } + end + + it "doesn't log the user in" do + expect(controller.current_user).to be_nil + end + end + context 'when the server has an decryption error' do before do allow_any_instance_of(User).to receive(:validate_and_consume_otp!).and_raise(OpenSSL::Cipher::CipherError) @@ -380,6 +427,52 @@ end end + context 'using email and password after an unfinished log-in attempt to a 2FA-protected account' do + let!(:other_user) do + Fabricate(:user, email: 'z@y.com', password: 'abcdefgh', otp_required_for_login: true, otp_secret: User.generate_otp_secret(32)) + end + + before do + post :create, params: { user: { email: other_user.email, password: other_user.password } } + post :create, params: { user: { email: user.email, password: user.password } } + end + + it 'renders sign in token authentication page' do + expect(controller).to render_template("sign_in_token") + end + + it 'generates sign in token' do + expect(user.reload.sign_in_token).to_not be_nil + end + + it 'sends sign in token e-mail' do + expect(UserMailer).to have_received(:sign_in_token) + end + end + + context 'using email and password after an unfinished log-in attempt with a sign-in token challenge' do + let!(:other_user) do + Fabricate(:user, email: 'z@y.com', password: 'abcdefgh', otp_required_for_login: false, current_sign_in_at: 1.month.ago) + end + + before do + post :create, params: { user: { email: other_user.email, password: other_user.password } } + post :create, params: { user: { email: user.email, password: user.password } } + end + + it 'renders sign in token authentication page' do + expect(controller).to render_template("sign_in_token") + end + + it 'generates sign in token' do + expect(user.reload.sign_in_token).to_not be_nil + end + + it 'sends sign in token e-mail' do + expect(UserMailer).to have_received(:sign_in_token).with(user, any_args) + end + end + context 'using a valid sign in token' do before do user.generate_sign_in_token && user.save @@ -395,6 +488,22 @@ end end + context 'using a valid sign in token, attempting to leverage previous half-login to bypass password auth' do + let!(:other_user) do + Fabricate(:user, email: 'z@y.com', password: 'abcdefgh', otp_required_for_login: false, current_sign_in_at: 1.month.ago) + end + + before do + user.generate_sign_in_token && user.save + post :create, params: { user: { email: other_user.email, password: other_user.password } } + post :create, params: { user: { email: user.email, sign_in_token_attempt: user.sign_in_token } }, session: { attempt_user_updated_at: user.updated_at.to_s } + end + + it "doesn't log the user in" do + expect(controller.current_user).to be_nil + end + end + context 'using an invalid sign in token' do before do post :create, params: { user: { sign_in_token_attempt: 'wrongotp' } }, session: { attempt_user_id: user.id, attempt_user_updated_at: user.updated_at.to_s } From 5899fe70b66fe98dac2b87e29fc08fad05a399c8 Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 1 Sep 2021 22:06:40 +0200 Subject: [PATCH 08/81] Fix processing mentions to domains with non-ascii TLDs (#16689) Fixes #16602 --- app/models/account.rb | 4 ++-- spec/services/process_mentions_service_spec.rb | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/app/models/account.rb b/app/models/account.rb index 3c11b8e6f1f951..176acd4181b7a9 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -58,8 +58,8 @@ class Account < ApplicationRecord hub_url ) - USERNAME_RE = /[a-z0-9_]+([a-z0-9_\.-]+[a-z0-9_]+)?/i - MENTION_RE = /(?<=^|[^\/[:word:]])@((#{USERNAME_RE})(?:@[[:word:]\.\-]+[a-z0-9]+)?)/i + USERNAME_RE = /[a-z0-9_]+([a-z0-9_\.-]+[a-z0-9_]+)?/i + MENTION_RE = /(?<=^|[^\/[:word:]])@((#{USERNAME_RE})(?:@[[:word:]\.\-]+[[:word:]]+)?)/i include AccountAssociations include AccountAvatar diff --git a/spec/services/process_mentions_service_spec.rb b/spec/services/process_mentions_service_spec.rb index 3b2f9d6984c9e1..d74e8dc624df62 100644 --- a/spec/services/process_mentions_service_spec.rb +++ b/spec/services/process_mentions_service_spec.rb @@ -42,6 +42,24 @@ expect(a_request(:post, remote_user.inbox_url)).to have_been_made.once end end + + context 'with an IDN TLD' do + let(:remote_user) { Fabricate(:account, username: 'foo', protocol: :activitypub, domain: 'xn--y9a3aq.xn--y9a3aq', inbox_url: 'http://example.com/inbox') } + let(:status) { Fabricate(:status, account: account, text: "Hello @foo@հայ.հայ") } + + before do + stub_request(:post, remote_user.inbox_url) + subject.call(status) + end + + it 'creates a mention' do + expect(remote_user.mentions.where(status: status).count).to eq 1 + end + + it 'sends activity to the inbox' do + expect(a_request(:post, remote_user.inbox_url)).to have_been_made.once + end + end end context 'Temporarily-unreachable ActivityPub user' do From 75441ac63de02cc5e65cd829a8448253f8c06140 Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 8 Sep 2021 23:33:23 +0200 Subject: [PATCH 09/81] Fix addressing of remote groups' followers (#16700) Fixes #16699 --- app/lib/activitypub/tag_manager.rb | 26 ++++++++++++++---------- spec/lib/activitypub/tag_manager_spec.rb | 8 ++++++++ 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/app/lib/activitypub/tag_manager.rb b/app/lib/activitypub/tag_manager.rb index f6b5e10d39eee6..f6b9741fa1820e 100644 --- a/app/lib/activitypub/tag_manager.rb +++ b/app/lib/activitypub/tag_manager.rb @@ -64,6 +64,10 @@ def replies_uri_for(target, page_params = nil) account_status_replies_url(target.account, target, page_params) end + def followers_uri_for(target) + target.local? ? account_followers_url(target) : target.followers_url.presence + end + # Primary audience of a status # Public statuses go out to primarily the public collection # Unlisted and private statuses go out primarily to the followers collection @@ -80,17 +84,17 @@ def to(status) account_ids = status.active_mentions.pluck(:account_id) to = status.account.followers.where(id: account_ids).each_with_object([]) do |account, result| result << uri_for(account) - result << account_followers_url(account) if account.group? + result << followers_uri_for(account) if account.group? end to.concat(FollowRequest.where(target_account_id: status.account_id, account_id: account_ids).each_with_object([]) do |request, result| result << uri_for(request.account) - result << account_followers_url(request.account) if request.account.group? - end) + result << followers_uri_for(request.account) if request.account.group? + end).compact else status.active_mentions.each_with_object([]) do |mention, result| result << uri_for(mention.account) - result << account_followers_url(mention.account) if mention.account.group? - end + result << followers_uri_for(mention.account) if mention.account.group? + end.compact end end end @@ -118,17 +122,17 @@ def cc(status) account_ids = status.active_mentions.pluck(:account_id) cc.concat(status.account.followers.where(id: account_ids).each_with_object([]) do |account, result| result << uri_for(account) - result << account_followers_url(account) if account.group? - end) + result << followers_uri_for(account) if account.group? + end.compact) cc.concat(FollowRequest.where(target_account_id: status.account_id, account_id: account_ids).each_with_object([]) do |request, result| result << uri_for(request.account) - result << account_followers_url(request.account) if request.account.group? - end) + result << followers_uri_for(request.account) if request.account.group? + end.compact) else cc.concat(status.active_mentions.each_with_object([]) do |mention, result| result << uri_for(mention.account) - result << account_followers_url(mention.account) if mention.account.group? - end) + result << followers_uri_for(mention.account) if mention.account.group? + end.compact) end end diff --git a/spec/lib/activitypub/tag_manager_spec.rb b/spec/lib/activitypub/tag_manager_spec.rb index 1c5c6f0edd3bdd..606a1de2e562d7 100644 --- a/spec/lib/activitypub/tag_manager_spec.rb +++ b/spec/lib/activitypub/tag_manager_spec.rb @@ -42,6 +42,14 @@ expect(subject.to(status)).to eq [subject.uri_for(mentioned)] end + it "returns URIs of mentioned group's followers for direct statuses to groups" do + status = Fabricate(:status, visibility: :direct) + mentioned = Fabricate(:account, domain: 'remote.org', uri: 'https://remote.org/group', followers_url: 'https://remote.org/group/followers', actor_type: 'Group') + status.mentions.create(account: mentioned) + expect(subject.to(status)).to include(subject.uri_for(mentioned)) + expect(subject.to(status)).to include(subject.followers_uri_for(mentioned)) + end + it "returns URIs of mentions for direct silenced author's status only if they are followers or requesting to be" do bob = Fabricate(:account, username: 'bob') alice = Fabricate(:account, username: 'alice') From 663b58aaaebb31de8479773b1d79db9a49322020 Mon Sep 17 00:00:00 2001 From: Holger Date: Thu, 9 Sep 2021 05:33:36 +0800 Subject: [PATCH 10/81] use relative path for `scope` (#16714) Use relative path for `scope` in web manifest to allow users use PWA correctly via alternate domains. --- app/serializers/manifest_serializer.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/serializers/manifest_serializer.rb b/app/serializers/manifest_serializer.rb index dafe8f55b9e8e4..4786aa760af92a 100644 --- a/app/serializers/manifest_serializer.rb +++ b/app/serializers/manifest_serializer.rb @@ -48,7 +48,7 @@ def start_url end def scope - root_url + '/' end def share_target From 2396c9061a6b6895dc77479c88e82c2106caae3a Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 30 Sep 2021 05:26:29 +0200 Subject: [PATCH 11/81] Fix webauthn secure key authentication (#16792) * Add tests * Fix webauthn secure key authentication Fixes #16769 --- app/controllers/auth/sessions_controller.rb | 2 +- .../auth/sessions_controller_spec.rb | 29 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/app/controllers/auth/sessions_controller.rb b/app/controllers/auth/sessions_controller.rb index d2bd6c4a0da5fc..ecb5a0a0384337 100644 --- a/app/controllers/auth/sessions_controller.rb +++ b/app/controllers/auth/sessions_controller.rb @@ -40,7 +40,7 @@ def destroy end def webauthn_options - user = find_user + user = User.find_by(id: session[:attempt_user_id]) if user.webauthn_enabled? options_for_get = WebAuthn::Credential.options_for_get( diff --git a/spec/controllers/auth/sessions_controller_spec.rb b/spec/controllers/auth/sessions_controller_spec.rb index 051a0807dba5a1..f718f5dd927246 100644 --- a/spec/controllers/auth/sessions_controller_spec.rb +++ b/spec/controllers/auth/sessions_controller_spec.rb @@ -519,4 +519,33 @@ end end end + + describe 'GET #webauthn_options' do + context 'with WebAuthn and OTP enabled as second factor' do + let(:domain) { "#{Rails.configuration.x.use_https ? 'https' : 'http' }://#{Rails.configuration.x.web_domain}" } + + let(:fake_client) { WebAuthn::FakeClient.new(domain) } + + let!(:user) do + Fabricate(:user, email: 'x@y.com', password: 'abcdefgh', otp_required_for_login: true, otp_secret: User.generate_otp_secret(32)) + end + + before do + user.update(webauthn_id: WebAuthn.generate_user_id) + public_key_credential = WebAuthn::Credential.from_create(fake_client.create) + user.webauthn_credentials.create( + nickname: 'SecurityKeyNickname', + external_id: public_key_credential.id, + public_key: public_key_credential.public_key, + sign_count: '1000' + ) + post :create, params: { user: { email: user.email, password: user.password } } + end + + it 'returns http success' do + get :webauthn_options + expect(response).to have_http_status :ok + end + end + end end From e63370db191eb9ae12112789afe8e027095f9112 Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 14 Oct 2021 19:59:21 +0200 Subject: [PATCH 12/81] Fix scheduled statuses decreasing statuses counts (#16791) * Add tests * Fix scheduled statuses decreasing statuses counts Fixes #16774 --- app/models/status.rb | 2 +- app/services/post_status_service.rb | 3 ++ spec/services/post_status_service_spec.rb | 50 ++++++++++++----------- 3 files changed, 31 insertions(+), 24 deletions(-) diff --git a/app/models/status.rb b/app/models/status.rb index 847921ac233dfb..3acf759f9de0a6 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -426,7 +426,7 @@ def increment_counter_caches end def decrement_counter_caches - return if direct_visibility? + return if direct_visibility? || new_record? account&.decrement_count!(:statuses_count) reblog&.decrement_count!(:reblogs_count) if reblog? diff --git a/app/services/post_status_service.rb b/app/services/post_status_service.rb index 0a383d6a392afe..85aaec4d6585d9 100644 --- a/app/services/post_status_service.rb +++ b/app/services/post_status_service.rb @@ -74,6 +74,9 @@ def schedule_status! status_for_validation = @account.statuses.build(status_attributes) if status_for_validation.valid? + # Marking the status as destroyed is necessary to prevent the status from being + # persisted when the associated media attachments get updated when creating the + # scheduled status. status_for_validation.destroy # The following transaction block is needed to wrap the UPDATEs to diff --git a/spec/services/post_status_service_spec.rb b/spec/services/post_status_service_spec.rb index 147a59fc315c7c..d21270c7938946 100644 --- a/spec/services/post_status_service_spec.rb +++ b/spec/services/post_status_service_spec.rb @@ -25,29 +25,33 @@ expect(status.thread).to eq in_reply_to_status end - it 'schedules a status' do - account = Fabricate(:account) - future = Time.now.utc + 2.hours - - status = subject.call(account, text: 'Hi future!', scheduled_at: future) - - expect(status).to be_a ScheduledStatus - expect(status.scheduled_at).to eq future - expect(status.params['text']).to eq 'Hi future!' - end - - it 'does not immediately create a status when scheduling a status' do - account = Fabricate(:account) - media = Fabricate(:media_attachment) - future = Time.now.utc + 2.hours - - status = subject.call(account, text: 'Hi future!', media_ids: [media.id], scheduled_at: future) - - expect(status).to be_a ScheduledStatus - expect(status.scheduled_at).to eq future - expect(status.params['text']).to eq 'Hi future!' - expect(media.reload.status).to be_nil - expect(Status.where(text: 'Hi future!').exists?).to be_falsey + context 'when scheduling a status' do + let!(:account) { Fabricate(:account) } + let!(:future) { Time.now.utc + 2.hours } + let!(:previous_status) { Fabricate(:status, account: account) } + + it 'schedules a status' do + status = subject.call(account, text: 'Hi future!', scheduled_at: future) + expect(status).to be_a ScheduledStatus + expect(status.scheduled_at).to eq future + expect(status.params['text']).to eq 'Hi future!' + end + + it 'does not immediately create a status' do + media = Fabricate(:media_attachment, account: account) + status = subject.call(account, text: 'Hi future!', media_ids: [media.id], scheduled_at: future) + + expect(status).to be_a ScheduledStatus + expect(status.scheduled_at).to eq future + expect(status.params['text']).to eq 'Hi future!' + expect(status.params['media_ids']).to eq [media.id] + expect(media.reload.status).to be_nil + expect(Status.where(text: 'Hi future!').exists?).to be_falsey + end + + it 'does not change statuses count' do + expect { subject.call(account, text: 'Hi future!', scheduled_at: future, thread: previous_status) }.not_to change { [account.statuses_count, previous_status.replies_count] } + end end it 'creates response to the original status of boost' do From 123a88b6b5b567901f0adbda84f6b356607cff0b Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 21 Oct 2021 20:39:35 +0200 Subject: [PATCH 13/81] Fix some link previews being incorrectly generated from other prior links (#16885) * Add tests * Fix some link previews being incorrectly generated from different prior links PR #12403 added a cache to avoid redundant queries when the OEmbed endpoint can be guessed from the URL. This caching mechanism is not perfectly correct as there is no guarantee that all pages from a given domain share the same OEmbed provider endpoint. This PR prevents the FetchOEmbedService from caching OEmbed endpoint that cannot be generalized by replacing a fully-qualified URL from the endpoint's parameters, greatly reducing the number of incorrect cached generalizations. --- app/services/fetch_oembed_service.rb | 5 ++- spec/fixtures/requests/oembed_youtube.html | 7 ++++ spec/services/fetch_oembed_service_spec.rb | 41 ++++++++++++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 spec/fixtures/requests/oembed_youtube.html diff --git a/app/services/fetch_oembed_service.rb b/app/services/fetch_oembed_service.rb index 60be9b9dc09e58..4cbaa04c623ab2 100644 --- a/app/services/fetch_oembed_service.rb +++ b/app/services/fetch_oembed_service.rb @@ -2,6 +2,7 @@ class FetchOEmbedService ENDPOINT_CACHE_EXPIRES_IN = 24.hours.freeze + URL_REGEX = /(=(http[s]?(%3A|:)(\/\/|%2F%2F)))([^&]*)/i.freeze attr_reader :url, :options, :format, :endpoint_url @@ -65,10 +66,12 @@ def parse_cached_endpoint! end def cache_endpoint! + return unless URL_REGEX.match?(@endpoint_url) + url_domain = Addressable::URI.parse(@url).normalized_host endpoint_hash = { - endpoint: @endpoint_url.gsub(/(=(http[s]?(%3A|:)(\/\/|%2F%2F)))([^&]*)/i, '={url}'), + endpoint: @endpoint_url.gsub(URL_REGEX, '={url}'), format: @format, } diff --git a/spec/fixtures/requests/oembed_youtube.html b/spec/fixtures/requests/oembed_youtube.html new file mode 100644 index 00000000000000..1508e4dd959830 --- /dev/null +++ b/spec/fixtures/requests/oembed_youtube.html @@ -0,0 +1,7 @@ + + + + + + + diff --git a/spec/services/fetch_oembed_service_spec.rb b/spec/services/fetch_oembed_service_spec.rb index a4262b040853f1..88f0113eddfd7f 100644 --- a/spec/services/fetch_oembed_service_spec.rb +++ b/spec/services/fetch_oembed_service_spec.rb @@ -13,6 +13,32 @@ describe 'discover_provider' do context 'when status code is 200 and MIME type is text/html' do + context 'when OEmbed endpoint contains URL as parameter' do + before do + stub_request(:get, 'https://www.youtube.com/watch?v=IPSbNdBmWKE').to_return( + status: 200, + headers: { 'Content-Type': 'text/html' }, + body: request_fixture('oembed_youtube.html'), + ) + stub_request(:get, 'https://www.youtube.com/oembed?format=json&url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DIPSbNdBmWKE').to_return( + status: 200, + headers: { 'Content-Type': 'text/html' }, + body: request_fixture('oembed_json_empty.html') + ) + end + + it 'returns new OEmbed::Provider for JSON provider' do + subject.call('https://www.youtube.com/watch?v=IPSbNdBmWKE') + expect(subject.endpoint_url).to eq 'https://www.youtube.com/oembed?format=json&url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DIPSbNdBmWKE' + expect(subject.format).to eq :json + end + + it 'stores URL template' do + subject.call('https://www.youtube.com/watch?v=IPSbNdBmWKE') + expect(Rails.cache.read('oembed_endpoint:www.youtube.com')[:endpoint]).to eq 'https://www.youtube.com/oembed?format=json&url={url}' + end + end + context 'Both of JSON and XML provider are discoverable' do before do stub_request(:get, 'https://host.test/oembed.html').to_return( @@ -33,6 +59,11 @@ expect(subject.endpoint_url).to eq 'https://host.test/provider.xml' expect(subject.format).to eq :xml end + + it 'does not cache OEmbed endpoint' do + subject.call('https://host.test/oembed.html', format: :xml) + expect(Rails.cache.exist?('oembed_endpoint:host.test')).to eq false + end end context 'JSON provider is discoverable while XML provider is not' do @@ -49,6 +80,11 @@ expect(subject.endpoint_url).to eq 'https://host.test/provider.json' expect(subject.format).to eq :json end + + it 'does not cache OEmbed endpoint' do + subject.call('https://host.test/oembed.html') + expect(Rails.cache.exist?('oembed_endpoint:host.test')).to eq false + end end context 'XML provider is discoverable while JSON provider is not' do @@ -65,6 +101,11 @@ expect(subject.endpoint_url).to eq 'https://host.test/provider.xml' expect(subject.format).to eq :xml end + + it 'does not cache OEmbed endpoint' do + subject.call('https://host.test/oembed.html') + expect(Rails.cache.exist?('oembed_endpoint:host.test')).to eq false + end end context 'Invalid XML provider is discoverable while JSON provider is not' do From aa828aea02fcf4c6e4aab180ae28a0891e18c052 Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 25 Oct 2021 16:34:15 +0200 Subject: [PATCH 14/81] Fix mastodon:setup to take dotenv/docker-compose differences into account (#16896) In order to work around https://github.com/mastodon/mastodon/issues/16895, add a warning to .env.production.sample, and change the mastodon:setup rake task to: - output a warning if a variable will be interpreted differently by dotenv and docker-compose - ensure the printed config is compatible with docker-compose --- .env.production.sample | 6 ++++++ lib/tasks/mastodon.rake | 15 +++++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/.env.production.sample b/.env.production.sample index 6f14c5804777ca..ff6db83caa13ea 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -4,6 +4,12 @@ # not demonstrate all available configuration options. Please look at # https://docs.joinmastodon.org/admin/config/ for the full documentation. +# Note that this file accepts slightly different syntax depending on whether +# you are using `docker-compose` or not. In particular, if you use +# `docker-compose`, the value of each declared variable will be taken verbatim, +# including surrounding quotes. +# See: https://github.com/mastodon/mastodon/issues/16895 + # Federation # ---------- # This identifies your server and cannot be changed safely later diff --git a/lib/tasks/mastodon.rake b/lib/tasks/mastodon.rake index 72bacb5eb25eb8..9146f78e108b44 100644 --- a/lib/tasks/mastodon.rake +++ b/lib/tasks/mastodon.rake @@ -333,8 +333,12 @@ namespace :mastodon do prompt.say 'This configuration will be written to .env.production' if prompt.yes?('Save configuration?') + incompatible_syntax = false + env_contents = env.each_pair.map do |key, value| if value.is_a?(String) && value =~ /[\s\#\\"]/ + incompatible_syntax = true + if value =~ /[']/ value = value.to_s.gsub(/[\\"\$]/) { |x| "\\#{x}" } "#{key}=\"#{value}\"" @@ -346,12 +350,19 @@ namespace :mastodon do end end.join("\n") - File.write(Rails.root.join('.env.production'), "# Generated with mastodon:setup on #{Time.now.utc}\n\n" + env_contents + "\n") + generated_header = "# Generated with mastodon:setup on #{Time.now.utc}\n\n" + + if incompatible_syntax + generated_header << "Some variables in this file will be interpreted differently whether you are\n" + generated_header << "using docker-compose or not.\n\n" + end + + File.write(Rails.root.join('.env.production'), "#{generated_header}#{env_contents}\n") if using_docker prompt.ok 'Below is your configuration, save it to an .env.production file outside Docker:' prompt.say "\n" - prompt.say File.read(Rails.root.join('.env.production')) + prompt.say "#{generated_header}#{env.each_pair.map { |key, value| "#{key}=#{value}" }.join("\n")}" prompt.say "\n" prompt.ok 'It is also saved within this container so you can proceed with this wizard.' end From f366a23a232e41c486845acc2c9d119dd825e6d0 Mon Sep 17 00:00:00 2001 From: Jeong Arm Date: Fri, 29 Oct 2021 02:30:44 +0900 Subject: [PATCH 15/81] Skip blocked domains media on tootctl media refresh (#16914) --- lib/mastodon/media_cli.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/mastodon/media_cli.rb b/lib/mastodon/media_cli.rb index 59c118500ac513..36ca71844f0dcd 100644 --- a/lib/mastodon/media_cli.rb +++ b/lib/mastodon/media_cli.rb @@ -230,6 +230,7 @@ def refresh processed, aggregate = parallelize_with_progress(scope) do |media_attachment| next if media_attachment.remote_url.blank? || (!options[:force] && media_attachment.file_file_name.present?) + next if DomainBlock.reject_media?(media_attachment.account.domain) unless options[:dry_run] media_attachment.reset_file! From e1cf8d4d3741f6950808e9042e078ea5891e7fe6 Mon Sep 17 00:00:00 2001 From: Jeong Arm Date: Thu, 4 Nov 2021 23:49:35 +0900 Subject: [PATCH 16/81] Fix statuses order in account's statuses admin page (#16937) --- app/controllers/admin/statuses_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/admin/statuses_controller.rb b/app/controllers/admin/statuses_controller.rb index ef279509d8759b..58a0eb84ca7f42 100644 --- a/app/controllers/admin/statuses_controller.rb +++ b/app/controllers/admin/statuses_controller.rb @@ -14,7 +14,7 @@ def index @statuses = @account.statuses.where(visibility: [:public, :unlisted]) if params[:media] - @statuses.merge!(Status.joins(:media_attachments).merge(@account.media_attachments.reorder(nil)).group(:id)) + @statuses.merge!(Status.joins(:media_attachments).merge(@account.media_attachments.reorder(nil)).group(:id)).reorder('statuses.id desc') end @statuses = @statuses.preload(:media_attachments, :mentions).page(params[:page]).per(PER_PAGE) From d5a50e9dfb2d64ba68271a06d4323da9869d92e8 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 10 Jul 2021 17:58:41 +0200 Subject: [PATCH 17/81] Add `configuration` attribute to `GET /api/v1/instance` (#16485) List various values like file size limits and supported mime types --- app/serializers/rest/instance_serializer.rb | 29 ++++++++++++++++++++- app/validators/status_length_validator.rb | 3 ++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/app/serializers/rest/instance_serializer.rb b/app/serializers/rest/instance_serializer.rb index d39092b56de7b9..0dc44b6239ccf0 100644 --- a/app/serializers/rest/instance_serializer.rb +++ b/app/serializers/rest/instance_serializer.rb @@ -5,7 +5,8 @@ class REST::InstanceSerializer < ActiveModel::Serializer attributes :uri, :title, :short_description, :description, :email, :version, :urls, :stats, :thumbnail, - :languages, :registrations, :approval_required, :invites_enabled + :languages, :registrations, :approval_required, :invites_enabled, + :configuration has_one :contact_account, serializer: REST::AccountSerializer @@ -53,6 +54,32 @@ def urls { streaming_api: Rails.configuration.x.streaming_api_base_url } end + def configuration + { + statuses: { + max_characters: StatusLengthValidator::MAX_CHARS, + max_media_attachments: 4, + characters_reserved_per_url: StatusLengthValidator::URL_PLACEHOLDER_CHARS, + }, + + media_attachments: { + supported_mime_types: MediaAttachment::IMAGE_MIME_TYPES + MediaAttachment::VIDEO_MIME_TYPES + MediaAttachment::AUDIO_MIME_TYPES, + image_size_limit: MediaAttachment::IMAGE_LIMIT, + image_matrix_limit: Attachmentable::MAX_MATRIX_LIMIT, + video_size_limit: MediaAttachment::VIDEO_LIMIT, + video_frame_rate_limit: MediaAttachment::MAX_VIDEO_FRAME_RATE, + video_matrix_limit: MediaAttachment::MAX_VIDEO_MATRIX_LIMIT, + }, + + polls: { + max_options: PollValidator::MAX_OPTIONS, + max_characters_per_option: PollValidator::MAX_OPTION_CHARS, + min_expiration: PollValidator::MIN_EXPIRATION, + max_expiration: PollValidator::MAX_EXPIRATION, + }, + } + end + def languages [I18n.default_locale] end diff --git a/app/validators/status_length_validator.rb b/app/validators/status_length_validator.rb index d036f19256b2de..4c258ec16ef8dd 100644 --- a/app/validators/status_length_validator.rb +++ b/app/validators/status_length_validator.rb @@ -2,7 +2,8 @@ class StatusLengthValidator < ActiveModel::Validator MAX_CHARS = 500 - URL_PLACEHOLDER = "\1#{'x' * 23}" + URL_PLACEHOLDER_CHARS = 23 + URL_PLACEHOLDER = "\1#{'x' * URL_PLACEHOLDER_CHARS}" def validate(status) return unless status.local? && !status.reblog? From c7f534ab953f713c1f4be82ebb23c757f3b063fd Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 30 Jun 2021 06:13:55 +0200 Subject: [PATCH 18/81] Fix missing on_delete: :cascade for canonical_email_blocks foreign key (#16448) --- .../20210416200740_create_canonical_email_blocks.rb | 2 +- ...000137_fix_canonical_email_blocks_foreign_key.rb | 13 +++++++++++++ db/schema.rb | 4 ++-- 3 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 db/migrate/20210630000137_fix_canonical_email_blocks_foreign_key.rb diff --git a/db/migrate/20210416200740_create_canonical_email_blocks.rb b/db/migrate/20210416200740_create_canonical_email_blocks.rb index a1f1660bfff109..32c44646c3fbb0 100644 --- a/db/migrate/20210416200740_create_canonical_email_blocks.rb +++ b/db/migrate/20210416200740_create_canonical_email_blocks.rb @@ -2,7 +2,7 @@ class CreateCanonicalEmailBlocks < ActiveRecord::Migration[6.1] def change create_table :canonical_email_blocks do |t| t.string :canonical_email_hash, null: false, default: '', index: { unique: true } - t.belongs_to :reference_account, null: false, foreign_key: { on_cascade: :delete, to_table: 'accounts' } + t.belongs_to :reference_account, null: false, foreign_key: { to_table: 'accounts' } t.timestamps end diff --git a/db/migrate/20210630000137_fix_canonical_email_blocks_foreign_key.rb b/db/migrate/20210630000137_fix_canonical_email_blocks_foreign_key.rb new file mode 100644 index 00000000000000..64cf84448b8cf4 --- /dev/null +++ b/db/migrate/20210630000137_fix_canonical_email_blocks_foreign_key.rb @@ -0,0 +1,13 @@ +class FixCanonicalEmailBlocksForeignKey < ActiveRecord::Migration[6.1] + def up + safety_assured do + execute 'ALTER TABLE canonical_email_blocks DROP CONSTRAINT fk_rails_1ecb262096, ADD CONSTRAINT fk_rails_1ecb262096 FOREIGN KEY (reference_account_id) REFERENCES accounts(id) ON DELETE CASCADE;' + end + end + + def down + safety_assured do + execute 'ALTER TABLE canonical_email_blocks DROP CONSTRAINT fk_rails_1ecb262096, ADD CONSTRAINT fk_rails_1ecb262096 FOREIGN KEY (reference_account_id) REFERENCES accounts(id);' + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 3fcee2d3043092..4ec50f89e9632b 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.define(version: 2021_05_26_193025) do +ActiveRecord::Schema.define(version: 2021_06_30_000137) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -985,7 +985,7 @@ add_foreign_key "blocks", "accounts", name: "fk_4269e03e65", on_delete: :cascade add_foreign_key "bookmarks", "accounts", on_delete: :cascade add_foreign_key "bookmarks", "statuses", on_delete: :cascade - add_foreign_key "canonical_email_blocks", "accounts", column: "reference_account_id" + add_foreign_key "canonical_email_blocks", "accounts", column: "reference_account_id", on_delete: :cascade add_foreign_key "conversation_mutes", "accounts", name: "fk_225b4212bb", on_delete: :cascade add_foreign_key "conversation_mutes", "conversations", on_delete: :cascade add_foreign_key "custom_filters", "accounts", on_delete: :cascade From 6b98fd0b4f79f887234ace4115914442183a84db Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 2 Jul 2021 12:03:15 +0200 Subject: [PATCH 19/81] Fix not being able to suspend accounts that already have a canonical e-mail block (#16455) --- app/models/account.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/models/account.rb b/app/models/account.rb index 176acd4181b7a9..c15b25d305f1cc 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -574,7 +574,11 @@ def clean_feed_manager def create_canonical_email_block! return unless local? && user_email.present? - CanonicalEmailBlock.create(reference_account: self, email: user_email) + begin + CanonicalEmailBlock.create(reference_account: self, email: user_email) + rescue ActiveRecord::RecordNotUnique + # A canonical e-mail block may already exist for the same e-mail + end end def destroy_canonical_email_block! From 9b34647c9b98ff751862782ea5b408d89adfcbdb Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 15 Sep 2021 18:51:16 +0200 Subject: [PATCH 20/81] Fix followers synchronization mechanism not working when URI has empty path (#16744) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow-up to #16510, forgot the controller exposing the actual followers… --- .../activitypub/followers_synchronizations_controller.rb | 4 ++-- .../activitypub/followers_synchronizations_controller_spec.rb | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/controllers/activitypub/followers_synchronizations_controller.rb b/app/controllers/activitypub/followers_synchronizations_controller.rb index 52503110582b03..940b77cf0a1d86 100644 --- a/app/controllers/activitypub/followers_synchronizations_controller.rb +++ b/app/controllers/activitypub/followers_synchronizations_controller.rb @@ -19,11 +19,11 @@ def show private def uri_prefix - signed_request_account.uri[/http(s?):\/\/[^\/]+\//] + signed_request_account.uri[Account::URL_PREFIX_RE] end def set_items - @items = @account.followers.where(Account.arel_table[:uri].matches(uri_prefix + '%', false, true)).pluck(:uri) + @items = @account.followers.where(Account.arel_table[:uri].matches("#{Account.sanitize_sql_like(uri_prefix)}/%", false, true)).or(@account.followers.where(uri: uri_prefix)).pluck(:uri) end def collection_presenter diff --git a/spec/controllers/activitypub/followers_synchronizations_controller_spec.rb b/spec/controllers/activitypub/followers_synchronizations_controller_spec.rb index d373f56bdb0cd2..3a382ff27ecf9f 100644 --- a/spec/controllers/activitypub/followers_synchronizations_controller_spec.rb +++ b/spec/controllers/activitypub/followers_synchronizations_controller_spec.rb @@ -5,11 +5,13 @@ let!(:follower_1) { Fabricate(:account, domain: 'example.com', uri: 'https://example.com/users/a') } let!(:follower_2) { Fabricate(:account, domain: 'example.com', uri: 'https://example.com/users/b') } let!(:follower_3) { Fabricate(:account, domain: 'foo.com', uri: 'https://foo.com/users/a') } + let!(:follower_4) { Fabricate(:account, username: 'instance-actor', domain: 'example.com', uri: 'https://example.com') } before do follower_1.follow!(account) follower_2.follow!(account) follower_3.follow!(account) + follower_4.follow!(account) end before do @@ -45,7 +47,7 @@ it 'returns orderedItems with followers from example.com' do expect(body[:orderedItems]).to be_an Array - expect(body[:orderedItems].sort).to eq [follower_1.uri, follower_2.uri] + expect(body[:orderedItems].sort).to eq [follower_4.uri, follower_1.uri, follower_2.uri] end it 'returns private Cache-Control header' do From 4cc7efcb0856ce980352ab0566bb8b63976e56d5 Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 13 Sep 2021 18:59:56 +0200 Subject: [PATCH 21/81] Fix some Rails frameworks being unnecessarily loaded (#16725) Saves about 10MiB of memory usage at boot --- config/application.rb | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/config/application.rb b/config/application.rb index 749b8aa5898881..1761bdc2e61c03 100644 --- a/config/application.rb +++ b/config/application.rb @@ -1,6 +1,21 @@ require_relative 'boot' -require 'rails/all' +require 'rails' + +require 'active_record/railtie' +#require 'active_storage/engine' +require 'action_controller/railtie' +require 'action_view/railtie' +require 'action_mailer/railtie' +require 'active_job/railtie' +#require 'action_cable/engine' +#require 'action_mailbox/engine' +#require 'action_text/engine' +#require 'rails/test_unit/railtie' +require 'sprockets/railtie' + +# Used to be implicitly required in action_mailbox/engine +require 'mail' # Require the gems listed in Gemfile, including any gems # you've limited to :test, :development, or :production. From 40eaa8706b81f0c7fb20dcc8668cdf2b443f5363 Mon Sep 17 00:00:00 2001 From: Claire Date: Sat, 4 Sep 2021 16:44:50 +0200 Subject: [PATCH 22/81] Fix suspicious sign-in mail text being out of date (#16690) Fixes #16687 --- config/locales/en.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/locales/en.yml b/config/locales/en.yml index 6274031dcc6e2e..9d5185fe6eb744 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1451,7 +1451,7 @@ en: otp_lost_help_html: If you lost access to both, you may get in touch with %{email} seamless_external_login: You are logged in via an external service, so password and e-mail settings are not available. signed_in_as: 'Signed in as:' - suspicious_sign_in_confirmation: You appear to not have logged in from this device before, and you haven't logged in for a while, so we're sending a security code to your e-mail address to confirm that it's you. + suspicious_sign_in_confirmation: You appear to not have logged in from this device before, so we're sending a security code to your e-mail address to confirm that it's you. verification: explanation_html: 'You can verify yourself as the owner of the links in your profile metadata. For that, the linked website must contain a link back to your Mastodon profile. The link back must have a rel="me" attribute. The text content of the link does not matter. Here is an example:' verification: Verification From e40d5414cce5a59083fd8ed50837186546a86ac4 Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 23 Jul 2021 02:53:17 +0200 Subject: [PATCH 23/81] Fix crashes with Microsoft Translate on Microsoft Edge (#16525) Fixes #16509 Microsoft Edge with translation enabled rewrites the DOM in ways that confuse react and prevent it from working properly. Wrapping the offending parts in a span avoids this issue. --- app/javascript/mastodon/components/status.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/javascript/mastodon/components/status.js b/app/javascript/mastodon/components/status.js index 295e83f5819607..ccc9067d124007 100644 --- a/app/javascript/mastodon/components/status.js +++ b/app/javascript/mastodon/components/status.js @@ -309,8 +309,8 @@ class Status extends ImmutablePureComponent { return (
- {status.getIn(['account', 'display_name']) || status.getIn(['account', 'username'])} - {status.get('content')} + {status.getIn(['account', 'display_name']) || status.getIn(['account', 'username'])} + {status.get('content')}
); From a1e5ff04e3278edbf7f71fe504f38c83de694d3b Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 14 Oct 2021 19:59:28 +0200 Subject: [PATCH 24/81] Fix tootctl self-destruct not sending Delete activities for recently-suspended accounts (#16688) * Do not block existing users' emails on self-destruct That is wasteful and unintuitive * Do not close registrations when running tootctl self-destruct with --dry-run * Close registrations on self-destruct regardless of known remote accounts * Fix tootctl self-destruct not sending Deletes for recently-suspended accounts * Suspend local users even if no remote account is known * Do not show scary confirmation text if ran with --dry-run --- lib/cli.rb | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/lib/cli.rb b/lib/cli.rb index 3f1658566b3acc..8815e137adbe44 100644 --- a/lib/cli.rb +++ b/lib/cli.rb @@ -94,17 +94,22 @@ def self_destruct exit(1) unless prompt.ask('Type in the domain of the server to confirm:', required: true) == Rails.configuration.x.local_domain - prompt.warn('This operation WILL NOT be reversible. It can also take a long time.') - prompt.warn('While the data won\'t be erased locally, the server will be in a BROKEN STATE afterwards.') - prompt.warn('A running Sidekiq process is required. Do not shut it down until queues clear.') + unless options[:dry_run] + prompt.warn('This operation WILL NOT be reversible. It can also take a long time.') + prompt.warn('While the data won\'t be erased locally, the server will be in a BROKEN STATE afterwards.') + prompt.warn('A running Sidekiq process is required. Do not shut it down until queues clear.') - exit(1) if prompt.no?('Are you sure you want to proceed?') + exit(1) if prompt.no?('Are you sure you want to proceed?') + end inboxes = Account.inboxes processed = 0 dry_run = options[:dry_run] ? ' (DRY RUN)' : '' + Setting.registrations_mode = 'none' unless options[:dry_run] + if inboxes.empty? + Account.local.without_suspended.in_batches.update_all(suspended_at: Time.now.utc, suspension_origin: :local) unless options[:dry_run] prompt.ok('It seems like your server has not federated with anything') prompt.ok('You can shut it down and delete it any time') return @@ -112,9 +117,7 @@ def self_destruct prompt.warn('Do NOT interrupt this process...') - Setting.registrations_mode = 'none' - - Account.local.without_suspended.find_each do |account| + delete_account = ->(account) do payload = ActiveModelSerializers::SerializableResource.new( account, serializer: ActivityPub::DeleteActorSerializer, @@ -128,12 +131,15 @@ def self_destruct [json, account.id, inbox_url] end - account.suspend! + account.suspend!(block_email: false) end processed += 1 end + Account.local.without_suspended.find_each { |account| delete_account.call(account) } + Account.local.suspended.joins(:deletion_request).find_each { |account| delete_account.call(account) } + prompt.ok("Queued #{inboxes.size * processed} items into Sidekiq for #{processed} accounts#{dry_run}") prompt.ok('Wait until Sidekiq processes all items, then you can shut everything down and delete the data') rescue TTY::Reader::InputInterrupt From 9a468c895be2b9970a9556e72a9162070337b87c Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 15 Jul 2021 15:56:58 +0200 Subject: [PATCH 25/81] Fix inefficiencies in auto-linking code (#16506) The auto-linking code basically rewrote the whole string escaping non-ascii characters in an inefficient way, and building a full character offset map between the unescaped and escaped texts before sending the contents to TwitterText's extractor. Instead of doing that, this commit changes the TwitterText regexps to include valid IRI characters in addition to valid URI characters. --- app/lib/formatter.rb | 31 +--------------------------- config/initializers/twitter_regex.rb | 4 ++++ 2 files changed, 5 insertions(+), 30 deletions(-) diff --git a/app/lib/formatter.rb b/app/lib/formatter.rb index fd653752627374..1610f36892512a 100644 --- a/app/lib/formatter.rb +++ b/app/lib/formatter.rb @@ -214,39 +214,10 @@ def rewrite(text, entities) result.flatten.join end - UNICODE_ESCAPE_BLACKLIST_RE = /\p{Z}|\p{P}/ - def utf8_friendly_extractor(text, options = {}) - old_to_new_index = [0] - - escaped = text.chars.map do |c| - output = begin - if c.ord.to_s(16).length > 2 && !UNICODE_ESCAPE_BLACKLIST_RE.match?(c) - CGI.escape(c) - else - c - end - end - - old_to_new_index << old_to_new_index.last + output.length - - output - end.join - # Note: I couldn't obtain list_slug with @user/list-name format # for mention so this requires additional check - special = Extractor.extract_urls_with_indices(escaped, options).map do |extract| - new_indices = [ - old_to_new_index.find_index(extract[:indices].first), - old_to_new_index.find_index(extract[:indices].last), - ] - - next extract.merge( - indices: new_indices, - url: text[new_indices.first..new_indices.last - 1] - ) - end - + special = Extractor.extract_urls_with_indices(text, options) standard = Extractor.extract_entities_with_indices(text, options) extra = Extractor.extract_extra_uris_with_indices(text, options) diff --git a/config/initializers/twitter_regex.rb b/config/initializers/twitter_regex.rb index 3ff2aa9e5de6ea..84c09ff35cf1f0 100644 --- a/config/initializers/twitter_regex.rb +++ b/config/initializers/twitter_regex.rb @@ -24,6 +24,10 @@ class Regex ) \) /iox + REGEXEN[:valid_iri_ucschar] = /[\u{A0}-\u{D7FF}\u{F900}-\u{FDCF}\u{FDF0}-\u{FFEF}\u{10000}-\u{1FFFD}\u{20000}-\u{2FFFD}\u{30000}-\u{3FFFD}\u{40000}-\u{4FFFD}\u{50000}-\u{5FFFD}\u{60000}-\u{6FFFD}\u{70000}-\u{7FFFD}\u{80000}-\u{8FFFD}\u{90000}-\u{9FFFD}\u{A0000}-\u{AFFFD}\u{B0000}-\u{BFFFD}\u{C0000}-\u{CFFFD}\u{D0000}-\u{DFFFD}\u{E1000}-\u{EFFFD}]/iou + REGEXEN[:valid_iri_iprivate] = /[\u{E000}-\u{F8FF}\u{F0000}-\u{FFFFD}\u{100000}-\u{10FFFD}]/iou + REGEXEN[:valid_url_query_chars] = /(?:#{REGEXEN[:valid_iri_ucschar]})|(?:#{REGEXEN[:valid_iri_iprivate]})|[a-z0-9!?\*'\(\);:&=\+\$\/%#\[\]\-_\.,~|@]/iou + REGEXEN[:valid_url_query_ending_chars] = /(?:#{REGEXEN[:valid_iri_ucschar]})|(?:#{REGEXEN[:valid_iri_iprivate]})|[a-z0-9_&=#\/\-]/iou REGEXEN[:valid_url_path] = /(?: (?: #{REGEXEN[:valid_general_url_path_chars]}* From aebcb722aa9a092000448b6e143c12a74f5bb78f Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 21 Jun 2021 20:14:47 +0200 Subject: [PATCH 26/81] Fix serialization of followers/following counts when user hides their network (#16418) * Add tests * Fix serialization of followers/following counts when user hides their network Fixes #16382 Signed-off-by: Claire --- .../follower_accounts_controller.rb | 2 +- .../following_accounts_controller.rb | 2 +- .../follower_accounts_controller_spec.rb | 19 ++++++++++++++++++- .../following_accounts_controller_spec.rb | 19 ++++++++++++++++++- 4 files changed, 38 insertions(+), 4 deletions(-) diff --git a/app/controllers/follower_accounts_controller.rb b/app/controllers/follower_accounts_controller.rb index ff4df2adfca9c1..b3589a39f86f1d 100644 --- a/app/controllers/follower_accounts_controller.rb +++ b/app/controllers/follower_accounts_controller.rb @@ -85,7 +85,7 @@ def restrict_fields_to if page_requested? || !@account.user_hides_network? # Return all fields else - %i(id type totalItems) + %i(id type total_items) end end end diff --git a/app/controllers/following_accounts_controller.rb b/app/controllers/following_accounts_controller.rb index 6bb95c4549838d..8a72dc475b95e6 100644 --- a/app/controllers/following_accounts_controller.rb +++ b/app/controllers/following_accounts_controller.rb @@ -85,7 +85,7 @@ def restrict_fields_to if page_requested? || !@account.user_hides_network? # Return all fields else - %i(id type totalItems) + %i(id type total_items) end end end diff --git a/spec/controllers/follower_accounts_controller_spec.rb b/spec/controllers/follower_accounts_controller_spec.rb index f6d55f69326b61..006274169109f9 100644 --- a/spec/controllers/follower_accounts_controller_spec.rb +++ b/spec/controllers/follower_accounts_controller_spec.rb @@ -3,7 +3,7 @@ describe FollowerAccountsController do render_views - let(:alice) { Fabricate(:account, username: 'alice') } + let(:alice) { Fabricate(:user).account } let(:follower0) { Fabricate(:account) } let(:follower1) { Fabricate(:account) } @@ -101,6 +101,23 @@ expect(body['partOf']).to be_blank end + context 'when account hides their network' do + before do + alice.user.settings.hide_network = true + end + + it 'returns followers count' do + expect(body['totalItems']).to eq 2 + end + + it 'does not return items' do + expect(body['items']).to be_blank + expect(body['orderedItems']).to be_blank + expect(body['first']).to be_blank + expect(body['last']).to be_blank + end + end + context 'when account is permanently suspended' do before do alice.suspend! diff --git a/spec/controllers/following_accounts_controller_spec.rb b/spec/controllers/following_accounts_controller_spec.rb index 0fc0967a632847..7ec0e3d0699e1d 100644 --- a/spec/controllers/following_accounts_controller_spec.rb +++ b/spec/controllers/following_accounts_controller_spec.rb @@ -3,7 +3,7 @@ describe FollowingAccountsController do render_views - let(:alice) { Fabricate(:account, username: 'alice') } + let(:alice) { Fabricate(:user).account } let(:followee0) { Fabricate(:account) } let(:followee1) { Fabricate(:account) } @@ -101,6 +101,23 @@ expect(body['partOf']).to be_blank end + context 'when account hides their network' do + before do + alice.user.settings.hide_network = true + end + + it 'returns followers count' do + expect(body['totalItems']).to eq 2 + end + + it 'does not return items' do + expect(body['items']).to be_blank + expect(body['orderedItems']).to be_blank + expect(body['first']).to be_blank + expect(body['last']).to be_blank + end + end + context 'when account is permanently suspended' do before do alice.suspend! From 34ab4111a7e32c1c0ae6055286bb94bf496295a2 Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 25 Jun 2021 04:45:17 +0200 Subject: [PATCH 27/81] Fix WebUI crash when a toot with a playing video gets deleted (#16384) * Fix WebUI crash when a toot with a playing video gets deleted * Fix pop-up player not closing the moment a status is deleted --- .../mastodon/actions/picture_in_picture.js | 21 ++++++++++++------- .../mastodon/reducers/picture_in_picture.js | 3 +++ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/app/javascript/mastodon/actions/picture_in_picture.js b/app/javascript/mastodon/actions/picture_in_picture.js index 4085cb59e0097f..33d8d57d475d05 100644 --- a/app/javascript/mastodon/actions/picture_in_picture.js +++ b/app/javascript/mastodon/actions/picture_in_picture.js @@ -22,13 +22,20 @@ export const PICTURE_IN_PICTURE_REMOVE = 'PICTURE_IN_PICTURE_REMOVE'; * @param {MediaProps} props * @return {object} */ -export const deployPictureInPicture = (statusId, accountId, playerType, props) => ({ - type: PICTURE_IN_PICTURE_DEPLOY, - statusId, - accountId, - playerType, - props, -}); +export const deployPictureInPicture = (statusId, accountId, playerType, props) => { + return (dispatch, getState) => { + // Do not open a player for a toot that does not exist + if (getState().hasIn(['statuses', statusId])) { + dispatch({ + type: PICTURE_IN_PICTURE_DEPLOY, + statusId, + accountId, + playerType, + props, + }); + } + }; +}; /* * @return {object} diff --git a/app/javascript/mastodon/reducers/picture_in_picture.js b/app/javascript/mastodon/reducers/picture_in_picture.js index 06cd8c5e875051..48772ae7f349f1 100644 --- a/app/javascript/mastodon/reducers/picture_in_picture.js +++ b/app/javascript/mastodon/reducers/picture_in_picture.js @@ -1,4 +1,5 @@ import { PICTURE_IN_PICTURE_DEPLOY, PICTURE_IN_PICTURE_REMOVE } from 'mastodon/actions/picture_in_picture'; +import { TIMELINE_DELETE } from '../actions/timelines'; const initialState = { statusId: null, @@ -16,6 +17,8 @@ export default function pictureInPicture(state = initialState, action) { return { statusId: action.statusId, accountId: action.accountId, type: action.playerType, ...action.props }; case PICTURE_IN_PICTURE_REMOVE: return { ...initialState }; + case TIMELINE_DELETE: + return (state.statusId === action.id) ? { ...initialState } : state; default: return state; } From 4bc1fde1059c6d953d91f5f57146781cd5559025 Mon Sep 17 00:00:00 2001 From: Claire Date: Sat, 3 Jul 2021 21:13:47 +0200 Subject: [PATCH 28/81] Fix anonymous access to outbox not being cached by the reverse proxy (#16458) * Fix anonymous access to outbox not being cached by the reverse proxy Up until now, anonymous access to outbox was marked as public, but with a 0 duration for caching, which means remote proxies would only serve from cache when the server was completely overwhelmed. Changed that cache duration to one minute, so that repeated anonymous access to one account's outbox can be appropriately cached. Also added `Signature` to the `Vary` header in case a page is requested, so that authenticated fetches are never served from cache (which only contains public toots). * Remove Vary: Accept header from webfinger controller Indeed, we have stopped returning xrd, and only ever return jrd, so the Accept request header does not matter anymore. * Cache negative webfinger hits for 3 minutes --- .../activitypub/outboxes_controller.rb | 10 +++++++++- .../well_known/webfinger_controller.rb | 3 ++- .../activitypub/outboxes_controller_spec.rb | 16 ++++++++++++---- .../well_known/webfinger_controller_spec.rb | 4 ++++ 4 files changed, 27 insertions(+), 6 deletions(-) diff --git a/app/controllers/activitypub/outboxes_controller.rb b/app/controllers/activitypub/outboxes_controller.rb index 4a52560aca8cd8..b2aab56a56f253 100644 --- a/app/controllers/activitypub/outboxes_controller.rb +++ b/app/controllers/activitypub/outboxes_controller.rb @@ -11,7 +11,11 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController before_action :set_cache_headers def show - expires_in(page_requested? ? 0 : 3.minutes, public: public_fetch_mode? && !(signed_request_account.present? && page_requested?)) + if page_requested? + expires_in(1.minute, public: public_fetch_mode? && signed_request_account.nil?) + else + expires_in(3.minutes, public: public_fetch_mode?) + end render json: outbox_presenter, serializer: ActivityPub::OutboxSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json' end @@ -76,4 +80,8 @@ def page_params def set_account @account = params[:account_username].present? ? Account.find_local!(username_param) : Account.representative end + + def set_cache_headers + response.headers['Vary'] = 'Signature' if authorized_fetch_mode? || page_requested? + end end diff --git a/app/controllers/well_known/webfinger_controller.rb b/app/controllers/well_known/webfinger_controller.rb index 0227f722a77c6d..2b296ea3be46ad 100644 --- a/app/controllers/well_known/webfinger_controller.rb +++ b/app/controllers/well_known/webfinger_controller.rb @@ -4,7 +4,6 @@ module WellKnown class WebfingerController < ActionController::Base include RoutingHelper - before_action { response.headers['Vary'] = 'Accept' } before_action :set_account before_action :check_account_suspension @@ -39,10 +38,12 @@ def check_account_suspension end def bad_request + expires_in(3.minutes, public: true) head 400 end def not_found + expires_in(3.minutes, public: true) head 404 end diff --git a/spec/controllers/activitypub/outboxes_controller_spec.rb b/spec/controllers/activitypub/outboxes_controller_spec.rb index d23f2c17cbcaca..1722690db1bff1 100644 --- a/spec/controllers/activitypub/outboxes_controller_spec.rb +++ b/spec/controllers/activitypub/outboxes_controller_spec.rb @@ -55,6 +55,10 @@ it_behaves_like 'cachable response' + it 'does not have a Vary header' do + expect(response.headers['Vary']).to be_nil + end + context 'when account is permanently suspended' do before do account.suspend! @@ -96,6 +100,10 @@ it_behaves_like 'cachable response' + it 'returns Vary header with Signature' do + expect(response.headers['Vary']).to include 'Signature' + end + context 'when account is permanently suspended' do before do account.suspend! @@ -144,7 +152,7 @@ end it 'returns private Cache-Control header' do - expect(response.headers['Cache-Control']).to eq 'max-age=0, private' + expect(response.headers['Cache-Control']).to eq 'max-age=60, private' end end @@ -170,7 +178,7 @@ end it 'returns private Cache-Control header' do - expect(response.headers['Cache-Control']).to eq 'max-age=0, private' + expect(response.headers['Cache-Control']).to eq 'max-age=60, private' end end @@ -195,7 +203,7 @@ end it 'returns private Cache-Control header' do - expect(response.headers['Cache-Control']).to eq 'max-age=0, private' + expect(response.headers['Cache-Control']).to eq 'max-age=60, private' end end @@ -220,7 +228,7 @@ end it 'returns private Cache-Control header' do - expect(response.headers['Cache-Control']).to eq 'max-age=0, private' + expect(response.headers['Cache-Control']).to eq 'max-age=60, private' end end end diff --git a/spec/controllers/well_known/webfinger_controller_spec.rb b/spec/controllers/well_known/webfinger_controller_spec.rb index 1075456f336859..8574d369d19d5e 100644 --- a/spec/controllers/well_known/webfinger_controller_spec.rb +++ b/spec/controllers/well_known/webfinger_controller_spec.rb @@ -24,6 +24,10 @@ expect(response).to have_http_status(200) end + it 'does not set a Vary header' do + expect(response.headers['Vary']).to be_nil + end + it 'returns application/jrd+json' do expect(response.media_type).to eq 'application/jrd+json' end From 8815e98aa231dd68cc9ff586f045f6c5fc3f0cd0 Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 5 Jul 2021 19:16:06 +0200 Subject: [PATCH 29/81] Fix pop-in player display when poster has long username or handle (#16468) --- app/javascript/styles/mastodon/components.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 7ca027e81db4e8..36c1461b0dfbc8 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -7284,6 +7284,7 @@ noscript { &__account { display: flex; text-decoration: none; + overflow: hidden; } .account__avatar { From be560337158c83d737fe4b4de3486caae718ca01 Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 7 Jul 2021 21:13:08 +0200 Subject: [PATCH 30/81] Change number_to_human calls to always use 3-digits precision (#16469) Fixes #16435 --- app/helpers/accounts_helper.rb | 6 +++--- app/helpers/application_helper.rb | 11 +++++++++++ app/views/about/more.html.haml | 4 ++-- app/views/about/show.html.haml | 4 ++-- app/views/accounts/_header.html.haml | 10 +++++----- app/views/accounts/show.html.haml | 2 +- app/views/admin/dashboard/index.html.haml | 16 ++++++++-------- .../follow_recommendations/_account.html.haml | 4 ++-- app/views/admin/instances/_instance.html.haml | 2 +- app/views/admin/tags/_tag.html.haml | 2 +- app/views/directories/index.html.haml | 4 ++-- app/views/relationships/_account.html.haml | 4 ++-- app/views/settings/featured_tags/index.html.haml | 2 +- app/views/statuses/_detailed_status.html.haml | 6 +++--- 14 files changed, 44 insertions(+), 33 deletions(-) diff --git a/app/helpers/accounts_helper.rb b/app/helpers/accounts_helper.rb index 134217734e5cd4..db3da2b0507a3a 100644 --- a/app/helpers/accounts_helper.rb +++ b/app/helpers/accounts_helper.rb @@ -80,17 +80,17 @@ def account_badge(account, all: false) def account_description(account) prepend_str = [ [ - number_to_human(account.statuses_count, strip_insignificant_zeros: true), + number_to_human(account.statuses_count, precision: 3, strip_insignificant_zeros: true), I18n.t('accounts.posts', count: account.statuses_count), ].join(' '), [ - number_to_human(account.following_count, strip_insignificant_zeros: true), + number_to_human(account.following_count, precision: 3, strip_insignificant_zeros: true), I18n.t('accounts.following', count: account.following_count), ].join(' '), [ - number_to_human(account.followers_count, strip_insignificant_zeros: true), + number_to_human(account.followers_count, precision: 3, strip_insignificant_zeros: true), I18n.t('accounts.followers', count: account.followers_count), ].join(' '), ].join(', ') diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index bf5742d34f2ff4..a39e8e5bf85e59 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -14,6 +14,17 @@ module ApplicationHelper ku ).freeze + def friendly_number_to_human(number, **options) + # By default, the number of precision digits used by number_to_human + # is looked up from the locales definition, and rails-i18n comes with + # values that don't seem to make much sense for many languages, so + # override these values with a default of 3 digits of precision. + options[:precision] = 3 + options[:strip_insignificant_zeros] = true + + number_to_human(number, **options) + end + def active_nav_class(*paths) paths.any? { |path| current_page?(path) } ? 'active' : '' end diff --git a/app/views/about/more.html.haml b/app/views/about/more.html.haml index cd93b0f5801bd8..f4429622c57c25 100644 --- a/app/views/about/more.html.haml +++ b/app/views/about/more.html.haml @@ -17,11 +17,11 @@ .row__information-board .information-board__section %span= t 'about.user_count_before' - %strong= number_to_human @instance_presenter.user_count, strip_insignificant_zeros: true + %strong= friendly_number_to_human @instance_presenter.user_count %span= t 'about.user_count_after', count: @instance_presenter.user_count .information-board__section %span= t 'about.status_count_before' - %strong= number_to_human @instance_presenter.status_count, strip_insignificant_zeros: true + %strong= friendly_number_to_human @instance_presenter.status_count %span= t 'about.status_count_after', count: @instance_presenter.status_count .row__mascot .landing-page__mascot diff --git a/app/views/about/show.html.haml b/app/views/about/show.html.haml index 565c4ed593e03d..6ae9e6ae07066b 100644 --- a/app/views/about/show.html.haml +++ b/app/views/about/show.html.haml @@ -70,10 +70,10 @@ .hero-widget__counters__wrapper .hero-widget__counter - %strong= number_to_human @instance_presenter.user_count, strip_insignificant_zeros: true + %strong= friendly_number_to_human @instance_presenter.user_count %span= t 'about.user_count_after', count: @instance_presenter.user_count .hero-widget__counter - %strong= number_to_human @instance_presenter.active_user_count, strip_insignificant_zeros: true + %strong= friendly_number_to_human @instance_presenter.active_user_count %span = t 'about.active_count_after' %abbr{ title: t('about.active_footnote') } * diff --git a/app/views/accounts/_header.html.haml b/app/views/accounts/_header.html.haml index cae5a5ac92b961..d9966723a742e4 100644 --- a/app/views/accounts/_header.html.haml +++ b/app/views/accounts/_header.html.haml @@ -15,17 +15,17 @@ .details-counters .counter{ class: active_nav_class(short_account_url(account), short_account_with_replies_url(account), short_account_media_url(account)) } = link_to short_account_url(account), class: 'u-url u-uid', title: number_with_delimiter(account.statuses_count) do - %span.counter-number= number_to_human account.statuses_count, strip_insignificant_zeros: true + %span.counter-number= friendly_number_to_human account.statuses_count %span.counter-label= t('accounts.posts', count: account.statuses_count) .counter{ class: active_nav_class(account_following_index_url(account)) } = link_to account_following_index_url(account), title: number_with_delimiter(account.following_count) do - %span.counter-number= number_to_human account.following_count, strip_insignificant_zeros: true + %span.counter-number= friendly_number_to_human account.following_count %span.counter-label= t('accounts.following', count: account.following_count) .counter{ class: active_nav_class(account_followers_url(account)) } = link_to account_followers_url(account), title: number_with_delimiter(account.followers_count) do - %span.counter-number= number_to_human account.followers_count, strip_insignificant_zeros: true + %span.counter-number= friendly_number_to_human account.followers_count %span.counter-label= t('accounts.followers', count: account.followers_count) .spacer .public-account-header__tabs__tabs__buttons @@ -36,8 +36,8 @@ .public-account-header__extra__links = link_to account_following_index_url(account) do - %strong= number_to_human account.following_count, strip_insignificant_zeros: true + %strong= friendly_number_to_human account.following_count = t('accounts.following', count: account.following_count) = link_to account_followers_url(account) do - %strong= number_to_human account.followers_count, strip_insignificant_zeros: true + %strong= friendly_number_to_human account.followers_count = t('accounts.followers', count: account.followers_count) diff --git a/app/views/accounts/show.html.haml b/app/views/accounts/show.html.haml index 1a81b96f6c90fc..72e9c661110834 100644 --- a/app/views/accounts/show.html.haml +++ b/app/views/accounts/show.html.haml @@ -81,6 +81,6 @@ = t('accounts.nothing_here') - else %time.formatted{ datetime: featured_tag.last_status_at.iso8601, title: l(featured_tag.last_status_at) }= l featured_tag.last_status_at - .trends__item__current= number_to_human featured_tag.statuses_count, strip_insignificant_zeros: true + .trends__item__current= friendly_number_to_human featured_tag.statuses_count = render 'application/sidebar' diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml index e8a2b46fd77149..05b585ab6d1fce 100644 --- a/app/views/admin/dashboard/index.html.haml +++ b/app/views/admin/dashboard/index.html.haml @@ -13,42 +13,42 @@ %div = link_to admin_accounts_url(local: 1, recent: 1) do .dashboard__counters__num{ title: number_with_delimiter(@users_count, strip_insignificant_zeros: true) } - = number_to_human @users_count, strip_insignificant_zeros: true + = friendly_number_to_human @users_count .dashboard__counters__label= t 'admin.dashboard.total_users' %div %div .dashboard__counters__num{ title: number_with_delimiter(@registrations_week, strip_insignificant_zeros: true) } - = number_to_human @registrations_week, strip_insignificant_zeros: true + = friendly_number_to_human @registrations_week .dashboard__counters__label= t 'admin.dashboard.week_users_new' %div %div .dashboard__counters__num{ title: number_with_delimiter(@logins_week, strip_insignificant_zeros: true) } - = number_to_human @logins_week, strip_insignificant_zeros: true + = friendly_number_to_human @logins_week .dashboard__counters__label= t 'admin.dashboard.week_users_active' %div = link_to admin_pending_accounts_path do .dashboard__counters__num{ title: number_with_delimiter(@pending_users_count, strip_insignificant_zeros: true) } - = number_to_human @pending_users_count, strip_insignificant_zeros: true + = friendly_number_to_human @pending_users_count .dashboard__counters__label= t 'admin.dashboard.pending_users' %div = link_to admin_reports_url do .dashboard__counters__num{ title: number_with_delimiter(@reports_count, strip_insignificant_zeros: true) } - = number_to_human @reports_count, strip_insignificant_zeros: true + = friendly_number_to_human @reports_count .dashboard__counters__label= t 'admin.dashboard.open_reports' %div = link_to admin_tags_path(pending_review: '1') do .dashboard__counters__num{ title: number_with_delimiter(@pending_tags_count, strip_insignificant_zeros: true) } - = number_to_human @pending_tags_count, strip_insignificant_zeros: true + = friendly_number_to_human @pending_tags_count .dashboard__counters__label= t 'admin.dashboard.pending_tags' %div %div .dashboard__counters__num{ title: number_with_delimiter(@interactions_week, strip_insignificant_zeros: true) } - = number_to_human @interactions_week, strip_insignificant_zeros: true + = friendly_number_to_human @interactions_week .dashboard__counters__label= t 'admin.dashboard.week_interactions' %div = link_to sidekiq_url do .dashboard__counters__num{ title: number_with_delimiter(@queue_backlog, strip_insignificant_zeros: true) } - = number_to_human @queue_backlog, strip_insignificant_zeros: true + = friendly_number_to_human @queue_backlog .dashboard__counters__label= t 'admin.dashboard.backlog' .dashboard__widgets diff --git a/app/views/admin/follow_recommendations/_account.html.haml b/app/views/admin/follow_recommendations/_account.html.haml index af5a4aaf7d0fce..00196dd01a88b3 100644 --- a/app/views/admin/follow_recommendations/_account.html.haml +++ b/app/views/admin/follow_recommendations/_account.html.haml @@ -7,10 +7,10 @@ %tr %td= account_link_to account %td.accounts-table__count.optional - = number_to_human account.statuses_count, strip_insignificant_zeros: true + = friendly_number_to_human account.statuses_count %small= t('accounts.posts', count: account.statuses_count).downcase %td.accounts-table__count.optional - = number_to_human account.followers_count, strip_insignificant_zeros: true + = friendly_number_to_human account.followers_count %small= t('accounts.followers', count: account.followers_count).downcase %td.accounts-table__count - if account.last_status_at.present? diff --git a/app/views/admin/instances/_instance.html.haml b/app/views/admin/instances/_instance.html.haml index 990cf9ec88d536..dc81007ac90bfa 100644 --- a/app/views/admin/instances/_instance.html.haml +++ b/app/views/admin/instances/_instance.html.haml @@ -30,4 +30,4 @@ = ' / ' %span.negative-hint = t('admin.instances.delivery.unavailable_message') - .trends__item__current{ title: t('admin.instances.known_accounts', count: instance.accounts_count) }= number_to_human instance.accounts_count, strip_insignificant_zeros: true + .trends__item__current{ title: t('admin.instances.known_accounts', count: instance.accounts_count) }= friendly_number_to_human instance.accounts_count diff --git a/app/views/admin/tags/_tag.html.haml b/app/views/admin/tags/_tag.html.haml index adf4ca7b2f82ef..ac0c728165a98b 100644 --- a/app/views/admin/tags/_tag.html.haml +++ b/app/views/admin/tags/_tag.html.haml @@ -16,4 +16,4 @@ = fa_icon 'fire fw' = t('admin.tags.trending_right_now') - .trends__item__current= number_to_human tag.history.first[:uses], strip_insignificant_zeros: true + .trends__item__current= friendly_number_to_human tag.history.first[:uses] diff --git a/app/views/directories/index.html.haml b/app/views/directories/index.html.haml index 7975ee9997b208..04639e32c228f3 100644 --- a/app/views/directories/index.html.haml +++ b/app/views/directories/index.html.haml @@ -39,10 +39,10 @@ .directory__card__extra .accounts-table__count - = number_to_human account.statuses_count, strip_insignificant_zeros: true + = friendly_number_to_human account.statuses_count %small= t('accounts.posts', count: account.statuses_count).downcase .accounts-table__count - = number_to_human account.followers_count, strip_insignificant_zeros: true + = friendly_number_to_human account.followers_count %small= t('accounts.followers', count: account.followers_count).downcase .accounts-table__count - if account.last_status_at.present? diff --git a/app/views/relationships/_account.html.haml b/app/views/relationships/_account.html.haml index f521aff2258ed2..0fa3cffb552fcc 100644 --- a/app/views/relationships/_account.html.haml +++ b/app/views/relationships/_account.html.haml @@ -9,10 +9,10 @@ = interrelationships_icon(@relationships, account.id) %td= account_link_to account %td.accounts-table__count.optional - = number_to_human account.statuses_count, strip_insignificant_zeros: true + = friendly_number_to_human account.statuses_count %small= t('accounts.posts', count: account.statuses_count).downcase %td.accounts-table__count.optional - = number_to_human account.followers_count, strip_insignificant_zeros: true + = friendly_number_to_human account.followers_count %small= t('accounts.followers', count: account.followers_count).downcase %td.accounts-table__count - if account.last_status_at.present? diff --git a/app/views/settings/featured_tags/index.html.haml b/app/views/settings/featured_tags/index.html.haml index 297379893ab24d..65de7f8f30d1d6 100644 --- a/app/views/settings/featured_tags/index.html.haml +++ b/app/views/settings/featured_tags/index.html.haml @@ -28,4 +28,4 @@ - else %time{ datetime: featured_tag.last_status_at.iso8601, title: l(featured_tag.last_status_at) }= l featured_tag.last_status_at = table_link_to 'trash', t('filters.index.delete'), settings_featured_tag_path(featured_tag), method: :delete, data: { confirm: t('admin.accounts.are_you_sure') } - .trends__item__current= number_to_human featured_tag.statuses_count, strip_insignificant_zeros: true + .trends__item__current= friendly_number_to_human featured_tag.statuses_count diff --git a/app/views/statuses/_detailed_status.html.haml b/app/views/statuses/_detailed_status.html.haml index daf164949c341b..6b3b8130672a19 100644 --- a/app/views/statuses/_detailed_status.html.haml +++ b/app/views/statuses/_detailed_status.html.haml @@ -55,18 +55,18 @@ = fa_icon('reply') - else = fa_icon('reply-all') - %span.detailed-status__reblogs>= number_to_human status.replies_count, strip_insignificant_zeros: true + %span.detailed-status__reblogs>= friendly_number_to_human status.replies_count = " " · - if status.public_visibility? || status.unlisted_visibility? = link_to remote_interaction_path(status, type: :reblog), class: 'modal-button detailed-status__link' do = fa_icon('retweet') - %span.detailed-status__reblogs>= number_to_human status.reblogs_count, strip_insignificant_zeros: true + %span.detailed-status__reblogs>= friendly_number_to_human status.reblogs_count = " " · = link_to remote_interaction_path(status, type: :favourite), class: 'modal-button detailed-status__link' do = fa_icon('star') - %span.detailed-status__favorites>= number_to_human status.favourites_count, strip_insignificant_zeros: true + %span.detailed-status__favorites>= friendly_number_to_human status.favourites_count = " " - if user_signed_in? From c79d4711e9529ea024e0b70d3f64821bbde018d2 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 13 Jul 2021 15:46:20 +0200 Subject: [PATCH 31/81] Change references to tootsuite/mastodon to mastodon/mastodon (#16491) * Change references to tootsuite/mastodon to mastodon/mastodon * Remove obsolete test fixture * Replace occurrences of tootsuite/mastodon with mastodon/mastodon in CHANGELOG And a few other places --- .github/CODEOWNERS | 2 +- AUTHORS.md | 4 +- CHANGELOG.md | 2650 ++++++++--------- CONTRIBUTING.md | 4 +- README.md | 8 +- app.json | 4 +- .../intersection_observer_article.js | 2 +- config/deploy.rb | 2 +- .../20200508212852_reset_unique_jobs_locks.rb | 2 +- lib/mastodon/version.rb | 2 +- lib/tasks/repo.rake | 4 +- package.json | 4 +- scalingo.json | 4 +- spec/fixtures/xml/mastodon.atom | 261 -- spec/presenters/instance_presenter_spec.rb | 4 +- spec/views/about/show.html.haml_spec.rb | 2 +- 16 files changed, 1349 insertions(+), 1610 deletions(-) delete mode 100644 spec/fixtures/xml/mastodon.atom diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 32919bd50c5fc3..fd6f74689e7139 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,4 +1,4 @@ -# CODEOWNERS for tootsuite/mastodon +# CODEOWNERS for mastodon/mastodon # Translators # To add translator, copy these lines, replace `fr` with appropriate language code and replace `@żelipapą` with user's GitHub nickname preceded by `@` sign or e-mail address. diff --git a/AUTHORS.md b/AUTHORS.md index 96412ff63719d5..596451737e903c 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -1,7 +1,7 @@ Authors ======= -Mastodon is available on [GitHub](https://github.com/tootsuite/mastodon) +Mastodon is available on [GitHub](https://github.com/mastodon/mastodon) and provided thanks to the work of the following contributors: * [Gargron](https://github.com/Gargron) @@ -719,7 +719,7 @@ and provided thanks to the work of the following contributors: * [西小倉宏信](mailto:nishiko@mindia.jp) * [雨宮美羽](mailto:k737566@gmail.com) -This document is provided for informational purposes only. Since it is only updated once per release, the version you are looking at may be currently out of date. To see the full list of contributors, consider looking at the [git history](https://github.com/tootsuite/mastodon/graphs/contributors) instead. +This document is provided for informational purposes only. Since it is only updated once per release, the version you are looking at may be currently out of date. To see the full list of contributors, consider looking at the [git history](https://github.com/mastodon/mastodon/graphs/contributors) instead. ## Translators diff --git a/CHANGELOG.md b/CHANGELOG.md index dbd9156c08e51f..b64da5b52f5ccf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,425 +6,425 @@ All notable changes to this project will be documented in this file. ## [3.4.1] - 2021-06-03 ### Added -- Add new emoji assets from Twemoji 13.1.0 ([Gargron](https://github.com/tootsuite/mastodon/pull/16345)) +- Add new emoji assets from Twemoji 13.1.0 ([Gargron](https://github.com/mastodon/mastodon/pull/16345)) ### Fixed -- Fix some ActivityPub identifiers in server actor outbox ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/16343)) -- Fix custom CSS path setting cookies and being uncacheable due to it ([tribela](https://github.com/tootsuite/mastodon/pull/16314)) -- Fix unread notification count when polling in web UI ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/16272)) -- Fix health check not being accessible through localhost ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/16270)) -- Fix some redis locks auto-releasing too fast ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/16276), [ClearlyClaire](https://github.com/tootsuite/mastodon/pull/16291)) -- Fix e-mail confirmations API not working correctly ([Gargron](https://github.com/tootsuite/mastodon/pull/16348)) -- Fix migration script not being able to run if it fails midway ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/16312)) -- Fix account deletion sometimes failing because of optimistic locks ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/16317)) -- Fix deprecated slash as division in SASS files ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/16347)) -- Fix `tootctl search deploy` compatibility error on Ruby 3 ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/16346)) -- Fix mailer jobs for deleted notifications erroring out ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/16294)) +- Fix some ActivityPub identifiers in server actor outbox ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16343)) +- Fix custom CSS path setting cookies and being uncacheable due to it ([tribela](https://github.com/mastodon/mastodon/pull/16314)) +- Fix unread notification count when polling in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16272)) +- Fix health check not being accessible through localhost ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16270)) +- Fix some redis locks auto-releasing too fast ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16276), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/16291)) +- Fix e-mail confirmations API not working correctly ([Gargron](https://github.com/mastodon/mastodon/pull/16348)) +- Fix migration script not being able to run if it fails midway ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16312)) +- Fix account deletion sometimes failing because of optimistic locks ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16317)) +- Fix deprecated slash as division in SASS files ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16347)) +- Fix `tootctl search deploy` compatibility error on Ruby 3 ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16346)) +- Fix mailer jobs for deleted notifications erroring out ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16294)) ## [3.4.0] - 2021-05-16 ### Added -- **Add follow recommendations for onboarding** ([Gargron](https://github.com/tootsuite/mastodon/pull/15945), [Gargron](https://github.com/tootsuite/mastodon/pull/16161), [Gargron](https://github.com/tootsuite/mastodon/pull/16060), [Gargron](https://github.com/tootsuite/mastodon/pull/16077), [Gargron](https://github.com/tootsuite/mastodon/pull/16078), [Gargron](https://github.com/tootsuite/mastodon/pull/16160), [Gargron](https://github.com/tootsuite/mastodon/pull/16079), [noellabo](https://github.com/tootsuite/mastodon/pull/16044), [noellabo](https://github.com/tootsuite/mastodon/pull/16045), [Gargron](https://github.com/tootsuite/mastodon/pull/16152), [Gargron](https://github.com/tootsuite/mastodon/pull/16153), [ClearlyClaire](https://github.com/tootsuite/mastodon/pull/16082), [ClearlyClaire](https://github.com/tootsuite/mastodon/pull/16173), [ClearlyClaire](https://github.com/tootsuite/mastodon/pull/16159), [ClearlyClaire](https://github.com/tootsuite/mastodon/pull/16189)) +- **Add follow recommendations for onboarding** ([Gargron](https://github.com/mastodon/mastodon/pull/15945), [Gargron](https://github.com/mastodon/mastodon/pull/16161), [Gargron](https://github.com/mastodon/mastodon/pull/16060), [Gargron](https://github.com/mastodon/mastodon/pull/16077), [Gargron](https://github.com/mastodon/mastodon/pull/16078), [Gargron](https://github.com/mastodon/mastodon/pull/16160), [Gargron](https://github.com/mastodon/mastodon/pull/16079), [noellabo](https://github.com/mastodon/mastodon/pull/16044), [noellabo](https://github.com/mastodon/mastodon/pull/16045), [Gargron](https://github.com/mastodon/mastodon/pull/16152), [Gargron](https://github.com/mastodon/mastodon/pull/16153), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/16082), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/16173), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/16159), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/16189)) - Tutorial on first web UI launch has been replaced with follow suggestions - Follow suggestions take user locale into account and are a mix of accounts most followed by currently active local users, and accounts that wrote the most shared/favourited posts in the last 30 days - Only accounts that have opted-in to being discoverable from their profile settings, and that do not require follow requests, will be suggested - Moderators can review suggestions for every supported locale and suppress specific suggestions from appearing and admins can ensure certain accounts always show up in suggestions from the settings area - New users no longer automatically follow admins -- **Add server rules** ([Gargron](https://github.com/tootsuite/mastodon/pull/15769), [ClearlyClaire](https://github.com/tootsuite/mastodon/pull/15778)) +- **Add server rules** ([Gargron](https://github.com/mastodon/mastodon/pull/15769), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15778)) - Admins can create and edit itemized server rules - They are available through the REST API and on the about page -- **Add canonical e-mail blocks for suspended accounts** ([Gargron](https://github.com/tootsuite/mastodon/pull/16049)) +- **Add canonical e-mail blocks for suspended accounts** ([Gargron](https://github.com/mastodon/mastodon/pull/16049)) - Normally, people can make multiple accounts using the same e-mail address using the `+` trick or by inserting or removing `.` characters from the first part of their address - Once an account is suspended, it will no longer be possible for the e-mail address used by that account to be used for new sign-ups in any of its forms -- Add management of delivery availability in admin UI ([noellabo](https://github.com/tootsuite/mastodon/pull/15771)) -- **Add system checks to dashboard in admin UI** ([Gargron](https://github.com/tootsuite/mastodon/pull/15989), [ClearlyClaire](https://github.com/tootsuite/mastodon/pull/15954), [ClearlyClaire](https://github.com/tootsuite/mastodon/pull/16002)) +- Add management of delivery availability in admin UI ([noellabo](https://github.com/mastodon/mastodon/pull/15771)) +- **Add system checks to dashboard in admin UI** ([Gargron](https://github.com/mastodon/mastodon/pull/15989), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15954), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/16002)) - The dashboard will now warn you if you some Sidekiq queues are not being processed, if you have not defined any server rules, or if you forgot to run database migrations from the latest Mastodon upgrade -- Add inline description of moderation actions in admin UI ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/15792)) -- Add "recommended" label to activity/peers API toggles in admin UI ([Gargron](https://github.com/tootsuite/mastodon/pull/16081)) -- Add joined date to profiles in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/16169), [rinsuki](https://github.com/tootsuite/mastodon/pull/16186)) -- Add transition to media modal background in web UI ([mkljczk](https://github.com/tootsuite/mastodon/pull/15843)) -- Add option to opt-out of unread notification markers in web UI ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/15842)) -- Add borders to 📱, 🚲, and 📲 emojis in web UI ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/15794), [ClearlyClaire](https://github.com/tootsuite/mastodon/pull/16035)) -- Add dropdown for boost privacy in boost confirmation modal in web UI ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/15704)) -- Add support for Ruby 3.0 ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/16046), [ClearlyClaire](https://github.com/tootsuite/mastodon/pull/16174)) -- Add `Message-ID` header to outgoing emails ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/16076)) +- Add inline description of moderation actions in admin UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15792)) +- Add "recommended" label to activity/peers API toggles in admin UI ([Gargron](https://github.com/mastodon/mastodon/pull/16081)) +- Add joined date to profiles in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/16169), [rinsuki](https://github.com/mastodon/mastodon/pull/16186)) +- Add transition to media modal background in web UI ([mkljczk](https://github.com/mastodon/mastodon/pull/15843)) +- Add option to opt-out of unread notification markers in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15842)) +- Add borders to 📱, 🚲, and 📲 emojis in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15794), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/16035)) +- Add dropdown for boost privacy in boost confirmation modal in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15704)) +- Add support for Ruby 3.0 ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16046), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/16174)) +- Add `Message-ID` header to outgoing emails ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16076)) - Some e-mail spam filters penalize e-mails that have a `Message-ID` header that uses a different domain name than the sending e-mail address. Now, the same domain will be used -- Add `af`, `gd` and `si` locales ([Gargron](https://github.com/tootsuite/mastodon/pull/16090)) -- Add guard against DNS rebinding attacks ([noellabo](https://github.com/tootsuite/mastodon/pull/16087), [noellabo](https://github.com/tootsuite/mastodon/pull/16095)) -- Add HTTP header to explicitly opt-out of FLoC by default ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/16036)) -- Add missing push notification title for polls and statuses ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/15929), [mkljczk](https://github.com/tootsuite/mastodon/pull/15564), [ClearlyClaire](https://github.com/tootsuite/mastodon/pull/15931)) -- Add `POST /api/v1/emails/confirmations` to REST API ([Gargron](https://github.com/tootsuite/mastodon/pull/15816), [Gargron](https://github.com/tootsuite/mastodon/pull/15949)) +- Add `af`, `gd` and `si` locales ([Gargron](https://github.com/mastodon/mastodon/pull/16090)) +- Add guard against DNS rebinding attacks ([noellabo](https://github.com/mastodon/mastodon/pull/16087), [noellabo](https://github.com/mastodon/mastodon/pull/16095)) +- Add HTTP header to explicitly opt-out of FLoC by default ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16036)) +- Add missing push notification title for polls and statuses ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15929), [mkljczk](https://github.com/mastodon/mastodon/pull/15564), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15931)) +- Add `POST /api/v1/emails/confirmations` to REST API ([Gargron](https://github.com/mastodon/mastodon/pull/15816), [Gargron](https://github.com/mastodon/mastodon/pull/15949)) - This method allows an app through which a user signed-up to request a new confirmation e-mail to be sent, or to change the e-mail of the account before it is confirmed -- Add `GET /api/v1/accounts/lookup` to REST API ([Gargron](https://github.com/tootsuite/mastodon/pull/15740), [ClearlyClaire](https://github.com/tootsuite/mastodon/pull/15750)) +- Add `GET /api/v1/accounts/lookup` to REST API ([Gargron](https://github.com/mastodon/mastodon/pull/15740), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15750)) - This method allows to quickly convert a username of a known account to an ID that can be used with the REST API, or to check if a username is available for sign-up -- Add `policy` param to `POST /api/v1/push/subscriptions` in REST API ([Gargron](https://github.com/tootsuite/mastodon/pull/16040)) +- Add `policy` param to `POST /api/v1/push/subscriptions` in REST API ([Gargron](https://github.com/mastodon/mastodon/pull/16040)) - This param allows an app to control from whom notifications should be delivered as push notifications to the app -- Add `details` to error response for `POST /api/v1/accounts` in REST API ([Gargron](https://github.com/tootsuite/mastodon/pull/15803)) +- Add `details` to error response for `POST /api/v1/accounts` in REST API ([Gargron](https://github.com/mastodon/mastodon/pull/15803)) - This attribute allows an app to display more helpful information to the user about why the sign-up did not succeed -- Add `SIDEKIQ_REDIS_URL` and related environment variables to optionally use a separate Redis server for Sidekiq ([noellabo](https://github.com/tootsuite/mastodon/pull/16188)) +- Add `SIDEKIQ_REDIS_URL` and related environment variables to optionally use a separate Redis server for Sidekiq ([noellabo](https://github.com/mastodon/mastodon/pull/16188)) ### Changed -- Change trending hashtags to be affected be reblogs ([Gargron](https://github.com/tootsuite/mastodon/pull/16164)) +- Change trending hashtags to be affected be reblogs ([Gargron](https://github.com/mastodon/mastodon/pull/16164)) - Previously, only original posts contributed to a hashtag's trending score - Now, reblogs of posts will also contribute to that hashtag's trending score -- Change e-mail confirmation link to always redirect to web UI ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/16151)) -- Change log level of worker lifecycle to WARN in streaming API ([Gargron](https://github.com/tootsuite/mastodon/pull/16110)) +- Change e-mail confirmation link to always redirect to web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16151)) +- Change log level of worker lifecycle to WARN in streaming API ([Gargron](https://github.com/mastodon/mastodon/pull/16110)) - Since running with INFO log level in production is not always desirable, it is easy to miss when a worker is shutdown and a new one is started -- Change the nouns "toot" and "status" to "post" in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/16080), [Gargron](https://github.com/tootsuite/mastodon/pull/16089)) +- Change the nouns "toot" and "status" to "post" in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/16080), [Gargron](https://github.com/mastodon/mastodon/pull/16089)) - To be clear, the button still says "Toot!" -- Change order of dropdown menu on posts to be more intuitive in web UI ([ariasuni](https://github.com/tootsuite/mastodon/pull/15647)) -- Change description of keyboard shortcuts in web UI ([ariasuni](https://github.com/tootsuite/mastodon/pull/16129)) -- Change option labels on edit profile page ([Gargron](https://github.com/tootsuite/mastodon/pull/16041)) +- Change order of dropdown menu on posts to be more intuitive in web UI ([ariasuni](https://github.com/mastodon/mastodon/pull/15647)) +- Change description of keyboard shortcuts in web UI ([ariasuni](https://github.com/mastodon/mastodon/pull/16129)) +- Change option labels on edit profile page ([Gargron](https://github.com/mastodon/mastodon/pull/16041)) - "Lock account" is now "Require follow requests" - "List this account on the directory" is now "Suggest account to others" - "Hide your network" is now "Hide your social graph" -- Change newly generated account IDs to not be enumerable ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/15844)) -- Change Web Push API deliveries to use request pooling ([Gargron](https://github.com/tootsuite/mastodon/pull/16014)) -- Change multiple mentions with same username to render with domain ([Gargron](https://github.com/tootsuite/mastodon/pull/15718), [noellabo](https://github.com/tootsuite/mastodon/pull/16038)) +- Change newly generated account IDs to not be enumerable ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15844)) +- Change Web Push API deliveries to use request pooling ([Gargron](https://github.com/mastodon/mastodon/pull/16014)) +- Change multiple mentions with same username to render with domain ([Gargron](https://github.com/mastodon/mastodon/pull/15718), [noellabo](https://github.com/mastodon/mastodon/pull/16038)) - When a post contains mentions of two or more users who have the same username, but on different domains, render their names with domain to help disambiguate them - Always render the domain of usernames used in profile metadata -- Change health check endpoint to reveal less information ([Gargron](https://github.com/tootsuite/mastodon/pull/15988)) -- Change account counters to use upsert (requires Postgres >= 9.5) ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/15913)) -- Change `mastodon:setup` to not call `assets:precompile` in Docker ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/13942)) -- **Change max. image dimensions to 1920x1080px (1080p)** ([Gargron](https://github.com/tootsuite/mastodon/pull/15690)) +- Change health check endpoint to reveal less information ([Gargron](https://github.com/mastodon/mastodon/pull/15988)) +- Change account counters to use upsert (requires Postgres >= 9.5) ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15913)) +- Change `mastodon:setup` to not call `assets:precompile` in Docker ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13942)) +- **Change max. image dimensions to 1920x1080px (1080p)** ([Gargron](https://github.com/mastodon/mastodon/pull/15690)) - Previously, this was 1280x1280px - This is the amount of pixels that original images get downsized to -- Change custom emoji to be animated when hovering container in web UI ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/15637)) -- Change streaming API from deprecated ClusterWS/cws to ws ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/15932)) -- Change systemd configuration to add sandboxing features ([Izorkin](https://github.com/tootsuite/mastodon/pull/15937), [Izorkin](https://github.com/tootsuite/mastodon/pull/16103), [Izorkin](https://github.com/tootsuite/mastodon/pull/16127)) -- Change nginx configuration to make running Onion service easier ([cohosh](https://github.com/tootsuite/mastodon/pull/15498)) -- Change Helm configuration ([dunn](https://github.com/tootsuite/mastodon/pull/15722), [dunn](https://github.com/tootsuite/mastodon/pull/15728), [dunn](https://github.com/tootsuite/mastodon/pull/15748), [dunn](https://github.com/tootsuite/mastodon/pull/15749), [dunn](https://github.com/tootsuite/mastodon/pull/15767)) -- Change Docker configuration ([SuperSandro2000](https://github.com/tootsuite/mastodon/pull/10823), [mashirozx](https://github.com/tootsuite/mastodon/pull/15978)) +- Change custom emoji to be animated when hovering container in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15637)) +- Change streaming API from deprecated ClusterWS/cws to ws ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15932)) +- Change systemd configuration to add sandboxing features ([Izorkin](https://github.com/mastodon/mastodon/pull/15937), [Izorkin](https://github.com/mastodon/mastodon/pull/16103), [Izorkin](https://github.com/mastodon/mastodon/pull/16127)) +- Change nginx configuration to make running Onion service easier ([cohosh](https://github.com/mastodon/mastodon/pull/15498)) +- Change Helm configuration ([dunn](https://github.com/mastodon/mastodon/pull/15722), [dunn](https://github.com/mastodon/mastodon/pull/15728), [dunn](https://github.com/mastodon/mastodon/pull/15748), [dunn](https://github.com/mastodon/mastodon/pull/15749), [dunn](https://github.com/mastodon/mastodon/pull/15767)) +- Change Docker configuration ([SuperSandro2000](https://github.com/mastodon/mastodon/pull/10823), [mashirozx](https://github.com/mastodon/mastodon/pull/15978)) ### Removed -- Remove PubSubHubbub-related columns from accounts table ([Gargron](https://github.com/tootsuite/mastodon/pull/16170), [ClearlyClaire](https://github.com/tootsuite/mastodon/pull/15857)) -- Remove dependency on @babel/plugin-proposal-class-properties ([ykzts](https://github.com/tootsuite/mastodon/pull/16155)) -- Remove dependency on pluck_each gem ([Gargron](https://github.com/tootsuite/mastodon/pull/16012)) -- Remove spam check and dependency on nilsimsa gem ([Gargron](https://github.com/tootsuite/mastodon/pull/16011)) -- Remove MySQL-specific code from Mastodon::MigrationHelpers ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/15924)) -- Remove IE11 from supported browsers target ([gol-cha](https://github.com/tootsuite/mastodon/pull/15779)) +- Remove PubSubHubbub-related columns from accounts table ([Gargron](https://github.com/mastodon/mastodon/pull/16170), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15857)) +- Remove dependency on @babel/plugin-proposal-class-properties ([ykzts](https://github.com/mastodon/mastodon/pull/16155)) +- Remove dependency on pluck_each gem ([Gargron](https://github.com/mastodon/mastodon/pull/16012)) +- Remove spam check and dependency on nilsimsa gem ([Gargron](https://github.com/mastodon/mastodon/pull/16011)) +- Remove MySQL-specific code from Mastodon::MigrationHelpers ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15924)) +- Remove IE11 from supported browsers target ([gol-cha](https://github.com/mastodon/mastodon/pull/15779)) ### Fixed -- Fix "You might be interested in" flashing while searching in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/16162)) -- Fix display of posts without text content in web UI ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/15665)) -- Fix Google Translate breaking web UI ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/15610), [ClearlyClaire](https://github.com/tootsuite/mastodon/pull/15611)) -- Fix web UI crashing when SVG support is disabled ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/15809)) -- Fix web UI crash when a status opened in the media modal is deleted ([kaias1jp](https://github.com/tootsuite/mastodon/pull/15701)) -- Fix OCR language data failing to load in web UI ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/15519)) -- Fix footer links not being clickable in Safari in web UI ([noellabo](https://github.com/tootsuite/mastodon/pull/15496)) -- Fix autofocus/autoselection not working on mobile in web UI ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/15555), [ClearlyClaire](https://github.com/tootsuite/mastodon/pull/15985)) -- Fix media redownload worker retrying on unexpected response codes ([Gargron](https://github.com/tootsuite/mastodon/pull/16111)) -- Fix thread resolve worker retrying when status no longer exists ([Gargron](https://github.com/tootsuite/mastodon/pull/16109)) -- Fix n+1 queries when rendering statuses in REST API ([abcang](https://github.com/tootsuite/mastodon/pull/15641)) -- Fix n+1 queries when rendering notifications in REST API ([abcang](https://github.com/tootsuite/mastodon/pull/15640)) -- Fix delete of local reply to local parent not being forwarded ([Gargron](https://github.com/tootsuite/mastodon/pull/16096)) -- Fix remote reporters not receiving suspend/unsuspend activities ([Gargron](https://github.com/tootsuite/mastodon/pull/16050)) -- Fix understanding (not fully qualified) `as:Public` and `Public` ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/15948)) -- Fix actor update not being distributed on profile picture deletion ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/15461)) -- Fix processing of incoming Delete activities ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/16084)) -- Fix processing of incoming Block activities ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/15546)) -- Fix processing of incoming Update activities of unknown accounts ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/15514)) -- Fix URIs of repeat follow requests not being recorded ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/15662)) -- Fix error on requests with no `Digest` header ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/15782)) -- Fix activity object not requiring signature in secure mode ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/15592)) -- Fix database serialization failure returning HTTP 500 ([Gargron](https://github.com/tootsuite/mastodon/pull/16101)) -- Fix media processing getting stuck on too much stdin/stderr ([Gargron](https://github.com/tootsuite/mastodon/pull/16136)) -- Fix some inefficient array manipulations ([007lva](https://github.com/tootsuite/mastodon/pull/15513), [007lva](https://github.com/tootsuite/mastodon/pull/15527)) -- Fix some inefficient regex matching ([007lva](https://github.com/tootsuite/mastodon/pull/15528)) -- Fix some inefficient SQL queries ([abcang](https://github.com/tootsuite/mastodon/pull/16104), [abcang](https://github.com/tootsuite/mastodon/pull/16106), [abcang](https://github.com/tootsuite/mastodon/pull/16105)) -- Fix trying to fetch key from empty URI when verifying HTTP signature ([Gargron](https://github.com/tootsuite/mastodon/pull/16100)) -- Fix `tootctl maintenance fix-duplicates` failures ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/15923), [ClearlyClaire](https://github.com/tootsuite/mastodon/pull/15515)) -- Fix error when removing status caused by race condition ([Gargron](https://github.com/tootsuite/mastodon/pull/16099)) -- Fix blocking someone not clearing up list feeds ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/16205)) -- Fix misspelled URLs character counting ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/15382)) -- Fix Sidekiq hanging forever due to a Resolv bug in Ruby 2.7.3 ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/16157)) -- Fix edge case where follow limit interferes with accepting a follow ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/16098)) -- Fix inconsistent lead text style in admin UI ([Gargron](https://github.com/tootsuite/mastodon/pull/16052), [ClearlyClaire](https://github.com/tootsuite/mastodon/pull/16086)) -- Fix reports of already suspended accounts being recorded ([Gargron](https://github.com/tootsuite/mastodon/pull/16047)) -- Fix sign-up restrictions based on IP addresses not being enforced ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/15607)) -- Fix YouTube embeds failing due to YouTube serving wrong OEmbed URLs ([Gargron](https://github.com/tootsuite/mastodon/pull/15716)) -- Fix error when rendering public pages with media without meta ([Gargron](https://github.com/tootsuite/mastodon/pull/16112)) -- Fix misaligned logo on follow button on public pages ([noellabo](https://github.com/tootsuite/mastodon/pull/15458)) -- Fix video modal not working on public pages ([noellabo](https://github.com/tootsuite/mastodon/pull/15469)) -- Fix race conditions on account migration creation ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/15597)) -- Fix not being able to change world filter expiration back to “Never” ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/15858)) -- Fix `.env.vagrant` not setting `RAILS_ENV` variable ([chandrn7](https://github.com/tootsuite/mastodon/pull/15709)) -- Fix error when muting users with `duration` in REST API ([Tak](https://github.com/tootsuite/mastodon/pull/15516)) -- Fix border padding on front page in light theme ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/15926)) -- Fix wrong URL to custom CSS when `CDN_HOST` is used ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/15927)) -- Fix `tootctl accounts unfollow` ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/15639)) -- Fix `tootctl emoji import` wasting time on MacOS shadow files ([cortices](https://github.com/tootsuite/mastodon/pull/15430)) -- Fix `tootctl emoji import` not treating shortcodes as case-insensitive ([angristan](https://github.com/tootsuite/mastodon/pull/15738)) -- Fix some issues with SAML account creation ([Gargron](https://github.com/tootsuite/mastodon/pull/15222), [kaiyou](https://github.com/tootsuite/mastodon/pull/15511)) -- Fix MX validation applying for explicitly allowed e-mail domains ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/15930)) -- Fix share page not using configured custom mascot ([tribela](https://github.com/tootsuite/mastodon/pull/15687)) -- Fix instance actor not being automatically created if it wasn't seeded properly ([ClearlyClaire](https://github.com/tootsuite/mastodon/pull/15693)) -- Fix HTTPS enforcement preventing Mastodon from being run as an Onion service ([cohosh](https://github.com/tootsuite/mastodon/pull/15560), [jtracey](https://github.com/tootsuite/mastodon/pull/15741), [ClearlyClaire](https://github.com/tootsuite/mastodon/pull/15712), [cohosh](https://github.com/tootsuite/mastodon/pull/15725)) -- Fix app name, website and redirect URIs not having a maximum length ([Gargron](https://github.com/tootsuite/mastodon/pull/16042)) +- Fix "You might be interested in" flashing while searching in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/16162)) +- Fix display of posts without text content in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15665)) +- Fix Google Translate breaking web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15610), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15611)) +- Fix web UI crashing when SVG support is disabled ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15809)) +- Fix web UI crash when a status opened in the media modal is deleted ([kaias1jp](https://github.com/mastodon/mastodon/pull/15701)) +- Fix OCR language data failing to load in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15519)) +- Fix footer links not being clickable in Safari in web UI ([noellabo](https://github.com/mastodon/mastodon/pull/15496)) +- Fix autofocus/autoselection not working on mobile in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15555), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15985)) +- Fix media redownload worker retrying on unexpected response codes ([Gargron](https://github.com/mastodon/mastodon/pull/16111)) +- Fix thread resolve worker retrying when status no longer exists ([Gargron](https://github.com/mastodon/mastodon/pull/16109)) +- Fix n+1 queries when rendering statuses in REST API ([abcang](https://github.com/mastodon/mastodon/pull/15641)) +- Fix n+1 queries when rendering notifications in REST API ([abcang](https://github.com/mastodon/mastodon/pull/15640)) +- Fix delete of local reply to local parent not being forwarded ([Gargron](https://github.com/mastodon/mastodon/pull/16096)) +- Fix remote reporters not receiving suspend/unsuspend activities ([Gargron](https://github.com/mastodon/mastodon/pull/16050)) +- Fix understanding (not fully qualified) `as:Public` and `Public` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15948)) +- Fix actor update not being distributed on profile picture deletion ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15461)) +- Fix processing of incoming Delete activities ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16084)) +- Fix processing of incoming Block activities ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15546)) +- Fix processing of incoming Update activities of unknown accounts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15514)) +- Fix URIs of repeat follow requests not being recorded ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15662)) +- Fix error on requests with no `Digest` header ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15782)) +- Fix activity object not requiring signature in secure mode ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15592)) +- Fix database serialization failure returning HTTP 500 ([Gargron](https://github.com/mastodon/mastodon/pull/16101)) +- Fix media processing getting stuck on too much stdin/stderr ([Gargron](https://github.com/mastodon/mastodon/pull/16136)) +- Fix some inefficient array manipulations ([007lva](https://github.com/mastodon/mastodon/pull/15513), [007lva](https://github.com/mastodon/mastodon/pull/15527)) +- Fix some inefficient regex matching ([007lva](https://github.com/mastodon/mastodon/pull/15528)) +- Fix some inefficient SQL queries ([abcang](https://github.com/mastodon/mastodon/pull/16104), [abcang](https://github.com/mastodon/mastodon/pull/16106), [abcang](https://github.com/mastodon/mastodon/pull/16105)) +- Fix trying to fetch key from empty URI when verifying HTTP signature ([Gargron](https://github.com/mastodon/mastodon/pull/16100)) +- Fix `tootctl maintenance fix-duplicates` failures ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15923), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15515)) +- Fix error when removing status caused by race condition ([Gargron](https://github.com/mastodon/mastodon/pull/16099)) +- Fix blocking someone not clearing up list feeds ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16205)) +- Fix misspelled URLs character counting ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15382)) +- Fix Sidekiq hanging forever due to a Resolv bug in Ruby 2.7.3 ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16157)) +- Fix edge case where follow limit interferes with accepting a follow ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16098)) +- Fix inconsistent lead text style in admin UI ([Gargron](https://github.com/mastodon/mastodon/pull/16052), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/16086)) +- Fix reports of already suspended accounts being recorded ([Gargron](https://github.com/mastodon/mastodon/pull/16047)) +- Fix sign-up restrictions based on IP addresses not being enforced ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15607)) +- Fix YouTube embeds failing due to YouTube serving wrong OEmbed URLs ([Gargron](https://github.com/mastodon/mastodon/pull/15716)) +- Fix error when rendering public pages with media without meta ([Gargron](https://github.com/mastodon/mastodon/pull/16112)) +- Fix misaligned logo on follow button on public pages ([noellabo](https://github.com/mastodon/mastodon/pull/15458)) +- Fix video modal not working on public pages ([noellabo](https://github.com/mastodon/mastodon/pull/15469)) +- Fix race conditions on account migration creation ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15597)) +- Fix not being able to change world filter expiration back to “Never” ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15858)) +- Fix `.env.vagrant` not setting `RAILS_ENV` variable ([chandrn7](https://github.com/mastodon/mastodon/pull/15709)) +- Fix error when muting users with `duration` in REST API ([Tak](https://github.com/mastodon/mastodon/pull/15516)) +- Fix border padding on front page in light theme ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15926)) +- Fix wrong URL to custom CSS when `CDN_HOST` is used ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15927)) +- Fix `tootctl accounts unfollow` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15639)) +- Fix `tootctl emoji import` wasting time on MacOS shadow files ([cortices](https://github.com/mastodon/mastodon/pull/15430)) +- Fix `tootctl emoji import` not treating shortcodes as case-insensitive ([angristan](https://github.com/mastodon/mastodon/pull/15738)) +- Fix some issues with SAML account creation ([Gargron](https://github.com/mastodon/mastodon/pull/15222), [kaiyou](https://github.com/mastodon/mastodon/pull/15511)) +- Fix MX validation applying for explicitly allowed e-mail domains ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15930)) +- Fix share page not using configured custom mascot ([tribela](https://github.com/mastodon/mastodon/pull/15687)) +- Fix instance actor not being automatically created if it wasn't seeded properly ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15693)) +- Fix HTTPS enforcement preventing Mastodon from being run as an Onion service ([cohosh](https://github.com/mastodon/mastodon/pull/15560), [jtracey](https://github.com/mastodon/mastodon/pull/15741), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15712), [cohosh](https://github.com/mastodon/mastodon/pull/15725)) +- Fix app name, website and redirect URIs not having a maximum length ([Gargron](https://github.com/mastodon/mastodon/pull/16042)) ## [3.3.0] - 2020-12-27 ### Added -- **Add hotkeys for audio/video control in web UI** ([Gargron](https://github.com/tootsuite/mastodon/pull/15158), [Gargron](https://github.com/tootsuite/mastodon/pull/15198)) +- **Add hotkeys for audio/video control in web UI** ([Gargron](https://github.com/mastodon/mastodon/pull/15158), [Gargron](https://github.com/mastodon/mastodon/pull/15198)) - `Space` and `k` to toggle playback - `m` to toggle mute - `f` to toggle fullscreen - `j` and `l` to go back and forward by 10 seconds - `.` and `,` to go back and forward by a frame (video only) -- Add expand/compress button on media modal in web UI ([mashirozx](https://github.com/tootsuite/mastodon/pull/15068), [mashirozx](https://github.com/tootsuite/mastodon/pull/15088), [mashirozx](https://github.com/tootsuite/mastodon/pull/15094)) -- Add border around 🕺 emoji in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14769)) -- Add border around 🐞 emoji in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14712)) -- Add home link to the getting started column when home isn't mounted ([ThibG](https://github.com/tootsuite/mastodon/pull/14707)) -- Add option to disable swiping motions across the web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13885)) -- **Add pop-out player for audio/video in web UI** ([Gargron](https://github.com/tootsuite/mastodon/pull/14870), [Gargron](https://github.com/tootsuite/mastodon/pull/15157), [Gargron](https://github.com/tootsuite/mastodon/pull/14915), [noellabo](https://github.com/tootsuite/mastodon/pull/15309)) +- Add expand/compress button on media modal in web UI ([mashirozx](https://github.com/mastodon/mastodon/pull/15068), [mashirozx](https://github.com/mastodon/mastodon/pull/15088), [mashirozx](https://github.com/mastodon/mastodon/pull/15094)) +- Add border around 🕺 emoji in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14769)) +- Add border around 🐞 emoji in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14712)) +- Add home link to the getting started column when home isn't mounted ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14707)) +- Add option to disable swiping motions across the web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13885)) +- **Add pop-out player for audio/video in web UI** ([Gargron](https://github.com/mastodon/mastodon/pull/14870), [Gargron](https://github.com/mastodon/mastodon/pull/15157), [Gargron](https://github.com/mastodon/mastodon/pull/14915), [noellabo](https://github.com/mastodon/mastodon/pull/15309)) - Continue watching/listening when you scroll away - Action bar to interact with/open toot from the pop-out player -- Add unread notification markers in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14818), [ThibG](https://github.com/tootsuite/mastodon/pull/14960), [ThibG](https://github.com/tootsuite/mastodon/pull/14954), [noellabo](https://github.com/tootsuite/mastodon/pull/14897), [noellabo](https://github.com/tootsuite/mastodon/pull/14907)) -- Add paragraph about browser add-ons when encountering errors in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14801)) -- Add import and export for bookmarks ([ThibG](https://github.com/tootsuite/mastodon/pull/14956)) -- Add cache buster feature for media files ([Gargron](https://github.com/tootsuite/mastodon/pull/15155)) +- Add unread notification markers in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14818), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/14960), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/14954), [noellabo](https://github.com/mastodon/mastodon/pull/14897), [noellabo](https://github.com/mastodon/mastodon/pull/14907)) +- Add paragraph about browser add-ons when encountering errors in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14801)) +- Add import and export for bookmarks ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14956)) +- Add cache buster feature for media files ([Gargron](https://github.com/mastodon/mastodon/pull/15155)) - If you have a proxy cache in front of object storage, deleted files will persist until the cache expires - If enabled, cache buster will make a special request to the proxy to signal a cache reset -- Add duration option to the mute function ([aquarla](https://github.com/tootsuite/mastodon/pull/13831)) -- Add replies policy option to the list function ([ThibG](https://github.com/tootsuite/mastodon/pull/9205), [trwnh](https://github.com/tootsuite/mastodon/pull/15304)) -- Add `og:published_time` OpenGraph tags on toots ([nornagon](https://github.com/tootsuite/mastodon/pull/14865)) -- **Add option to be notified when a followed user posts** ([Gargron](https://github.com/tootsuite/mastodon/pull/13546), [ThibG](https://github.com/tootsuite/mastodon/pull/14896), [Gargron](https://github.com/tootsuite/mastodon/pull/14822)) +- Add duration option to the mute function ([aquarla](https://github.com/mastodon/mastodon/pull/13831)) +- Add replies policy option to the list function ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9205), [trwnh](https://github.com/mastodon/mastodon/pull/15304)) +- Add `og:published_time` OpenGraph tags on toots ([nornagon](https://github.com/mastodon/mastodon/pull/14865)) +- **Add option to be notified when a followed user posts** ([Gargron](https://github.com/mastodon/mastodon/pull/13546), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/14896), [Gargron](https://github.com/mastodon/mastodon/pull/14822)) - If you don't want to miss a toot, click the bell button! -- Add client-side validation in password change forms ([ThibG](https://github.com/tootsuite/mastodon/pull/14564)) -- Add client-side validation in the registration form ([ThibG](https://github.com/tootsuite/mastodon/pull/14560), [ThibG](https://github.com/tootsuite/mastodon/pull/14599)) -- Add support for Gemini URLs ([joshleeb](https://github.com/tootsuite/mastodon/pull/15013)) -- Add app shortcuts to web app manifest ([mkljczk](https://github.com/tootsuite/mastodon/pull/15234)) -- Add WebAuthn as an alternative 2FA method ([santiagorodriguez96](https://github.com/tootsuite/mastodon/pull/14466), [jiikko](https://github.com/tootsuite/mastodon/pull/14806)) -- Add honeypot fields and minimum fill-out time for sign-up form ([ThibG](https://github.com/tootsuite/mastodon/pull/15276)) -- Add icon for mutual relationships in relationship manager ([noellabo](https://github.com/tootsuite/mastodon/pull/15149)) -- Add follow selected followers button in relationship manager ([noellabo](https://github.com/tootsuite/mastodon/pull/15148)) -- **Add subresource integrity for JS and CSS assets** ([Gargron](https://github.com/tootsuite/mastodon/pull/15096)) +- Add client-side validation in password change forms ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14564)) +- Add client-side validation in the registration form ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14560), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/14599)) +- Add support for Gemini URLs ([joshleeb](https://github.com/mastodon/mastodon/pull/15013)) +- Add app shortcuts to web app manifest ([mkljczk](https://github.com/mastodon/mastodon/pull/15234)) +- Add WebAuthn as an alternative 2FA method ([santiagorodriguez96](https://github.com/mastodon/mastodon/pull/14466), [jiikko](https://github.com/mastodon/mastodon/pull/14806)) +- Add honeypot fields and minimum fill-out time for sign-up form ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15276)) +- Add icon for mutual relationships in relationship manager ([noellabo](https://github.com/mastodon/mastodon/pull/15149)) +- Add follow selected followers button in relationship manager ([noellabo](https://github.com/mastodon/mastodon/pull/15148)) +- **Add subresource integrity for JS and CSS assets** ([Gargron](https://github.com/mastodon/mastodon/pull/15096)) - If you use a CDN for static assets (JavaScript, CSS, and so on), you have to trust that the CDN does not modify the assets maliciously - Subresource integrity compares server-generated asset digests with what's actually served from the CDN and prevents such attacks -- Add `ku`, `sa`, `sc`, `zgh` to available locales ([ykzts](https://github.com/tootsuite/mastodon/pull/15138)) -- Add ability to force an account to mark media as sensitive ([noellabo](https://github.com/tootsuite/mastodon/pull/14361)) -- **Add ability to block access or limit sign-ups from chosen IPs** ([Gargron](https://github.com/tootsuite/mastodon/pull/14963), [ThibG](https://github.com/tootsuite/mastodon/pull/15263)) +- Add `ku`, `sa`, `sc`, `zgh` to available locales ([ykzts](https://github.com/mastodon/mastodon/pull/15138)) +- Add ability to force an account to mark media as sensitive ([noellabo](https://github.com/mastodon/mastodon/pull/14361)) +- **Add ability to block access or limit sign-ups from chosen IPs** ([Gargron](https://github.com/mastodon/mastodon/pull/14963), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15263)) - Add rules for IPs or CIDR ranges that automatically expire after a configurable amount of time - Choose the severity of the rule, either blocking all access or merely limiting sign-ups -- **Add support for reversible suspensions through ActivityPub** ([Gargron](https://github.com/tootsuite/mastodon/pull/14989)) +- **Add support for reversible suspensions through ActivityPub** ([Gargron](https://github.com/mastodon/mastodon/pull/14989)) - Servers can signal that one of their accounts has been suspended - During suspension, the account can only delete its own content - A reversal of the suspension can be signalled the same way - A local suspension always overrides a remote one -- Add indication to admin UI of whether a report has been forwarded ([ThibG](https://github.com/tootsuite/mastodon/pull/13237)) -- Add display of reasons for joining of an account in admin UI ([mashirozx](https://github.com/tootsuite/mastodon/pull/15265)) -- Add option to obfuscate domain name in public list of domain blocks ([Gargron](https://github.com/tootsuite/mastodon/pull/15355)) -- Add option to make reasons for joining required on sign-up ([ThibG](https://github.com/tootsuite/mastodon/pull/15326), [ThibG](https://github.com/tootsuite/mastodon/pull/15358), [ThibG](https://github.com/tootsuite/mastodon/pull/15385), [ThibG](https://github.com/tootsuite/mastodon/pull/15405)) -- Add ActivityPub follower synchronization mechanism ([ThibG](https://github.com/tootsuite/mastodon/pull/14510), [ThibG](https://github.com/tootsuite/mastodon/pull/15026)) -- Add outbox attribute to instance actor ([ThibG](https://github.com/tootsuite/mastodon/pull/14721)) -- Add featured hashtags as an ActivityPub collection ([Gargron](https://github.com/tootsuite/mastodon/pull/11595), [noellabo](https://github.com/tootsuite/mastodon/pull/15277)) -- Add support for dereferencing objects through bearcaps ([Gargron](https://github.com/tootsuite/mastodon/pull/14683), [noellabo](https://github.com/tootsuite/mastodon/pull/14981)) -- Add `S3_READ_TIMEOUT` environment variable ([tateisu](https://github.com/tootsuite/mastodon/pull/14952)) -- Add `ALLOWED_PRIVATE_ADDRESSES` environment variable ([ThibG](https://github.com/tootsuite/mastodon/pull/14722)) -- Add `--fix-permissions` option to `tootctl media remove-orphans` ([Gargron](https://github.com/tootsuite/mastodon/pull/14383), [uist1idrju3i](https://github.com/tootsuite/mastodon/pull/14715)) -- Add `tootctl accounts merge` ([Gargron](https://github.com/tootsuite/mastodon/pull/15201), [ThibG](https://github.com/tootsuite/mastodon/pull/15264), [ThibG](https://github.com/tootsuite/mastodon/pull/15256)) +- Add indication to admin UI of whether a report has been forwarded ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13237)) +- Add display of reasons for joining of an account in admin UI ([mashirozx](https://github.com/mastodon/mastodon/pull/15265)) +- Add option to obfuscate domain name in public list of domain blocks ([Gargron](https://github.com/mastodon/mastodon/pull/15355)) +- Add option to make reasons for joining required on sign-up ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15326), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15358), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15385), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15405)) +- Add ActivityPub follower synchronization mechanism ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14510), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15026)) +- Add outbox attribute to instance actor ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14721)) +- Add featured hashtags as an ActivityPub collection ([Gargron](https://github.com/mastodon/mastodon/pull/11595), [noellabo](https://github.com/mastodon/mastodon/pull/15277)) +- Add support for dereferencing objects through bearcaps ([Gargron](https://github.com/mastodon/mastodon/pull/14683), [noellabo](https://github.com/mastodon/mastodon/pull/14981)) +- Add `S3_READ_TIMEOUT` environment variable ([tateisu](https://github.com/mastodon/mastodon/pull/14952)) +- Add `ALLOWED_PRIVATE_ADDRESSES` environment variable ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14722)) +- Add `--fix-permissions` option to `tootctl media remove-orphans` ([Gargron](https://github.com/mastodon/mastodon/pull/14383), [uist1idrju3i](https://github.com/mastodon/mastodon/pull/14715)) +- Add `tootctl accounts merge` ([Gargron](https://github.com/mastodon/mastodon/pull/15201), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15264), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15256)) - Has someone changed their domain or subdomain thereby creating two accounts where there should be one? - This command will fix it on your end -- Add `tootctl maintenance fix-duplicates` ([ThibG](https://github.com/tootsuite/mastodon/pull/14860), [Gargron](https://github.com/tootsuite/mastodon/pull/15223), [ThibG](https://github.com/tootsuite/mastodon/pull/15373)) +- Add `tootctl maintenance fix-duplicates` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14860), [Gargron](https://github.com/mastodon/mastodon/pull/15223), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15373)) - Index corruption in the database? - This command is for you -- **Add support for managing multiple stream subscriptions in a single connection** ([Gargron](https://github.com/tootsuite/mastodon/pull/14524), [Gargron](https://github.com/tootsuite/mastodon/pull/14566), [mfmfuyu](https://github.com/tootsuite/mastodon/pull/14859), [zunda](https://github.com/tootsuite/mastodon/pull/14608)) +- **Add support for managing multiple stream subscriptions in a single connection** ([Gargron](https://github.com/mastodon/mastodon/pull/14524), [Gargron](https://github.com/mastodon/mastodon/pull/14566), [mfmfuyu](https://github.com/mastodon/mastodon/pull/14859), [zunda](https://github.com/mastodon/mastodon/pull/14608)) - Previously, getting live updates for multiple timelines required opening a HTTP or WebSocket connection for each - More connections means more resource consumption on both ends, not to mention the (ever so slight) delay when establishing a new connection - Now, with just a single WebSocket connection you can subscribe and unsubscribe to and from multiple streams -- Add support for limiting results by both `min_id` and `max_id` at the same time in REST API ([tateisu](https://github.com/tootsuite/mastodon/pull/14776)) -- Add `GET /api/v1/accounts/:id/featured_tags` to REST API ([noellabo](https://github.com/tootsuite/mastodon/pull/11817), [noellabo](https://github.com/tootsuite/mastodon/pull/15270)) -- Add stoplight for object storage failures, return HTTP 503 in REST API ([Gargron](https://github.com/tootsuite/mastodon/pull/13043)) -- Add optional `tootctl remove media` cronjob in Helm chart ([dunn](https://github.com/tootsuite/mastodon/pull/14396)) -- Add clean error message when `RAILS_ENV` is unset ([ThibG](https://github.com/tootsuite/mastodon/pull/15381)) +- Add support for limiting results by both `min_id` and `max_id` at the same time in REST API ([tateisu](https://github.com/mastodon/mastodon/pull/14776)) +- Add `GET /api/v1/accounts/:id/featured_tags` to REST API ([noellabo](https://github.com/mastodon/mastodon/pull/11817), [noellabo](https://github.com/mastodon/mastodon/pull/15270)) +- Add stoplight for object storage failures, return HTTP 503 in REST API ([Gargron](https://github.com/mastodon/mastodon/pull/13043)) +- Add optional `tootctl remove media` cronjob in Helm chart ([dunn](https://github.com/mastodon/mastodon/pull/14396)) +- Add clean error message when `RAILS_ENV` is unset ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15381)) ### Changed -- **Change media modals look in web UI** ([Gargron](https://github.com/tootsuite/mastodon/pull/15217), [Gargron](https://github.com/tootsuite/mastodon/pull/15221), [Gargron](https://github.com/tootsuite/mastodon/pull/15284), [Gargron](https://github.com/tootsuite/mastodon/pull/15283), [Kjwon15](https://github.com/tootsuite/mastodon/pull/15308), [noellabo](https://github.com/tootsuite/mastodon/pull/15305), [ThibG](https://github.com/tootsuite/mastodon/pull/15417)) +- **Change media modals look in web UI** ([Gargron](https://github.com/mastodon/mastodon/pull/15217), [Gargron](https://github.com/mastodon/mastodon/pull/15221), [Gargron](https://github.com/mastodon/mastodon/pull/15284), [Gargron](https://github.com/mastodon/mastodon/pull/15283), [Kjwon15](https://github.com/mastodon/mastodon/pull/15308), [noellabo](https://github.com/mastodon/mastodon/pull/15305), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15417)) - Background of the overlay matches the color of the image - Action bar to interact with or open the toot from the modal -- Change order of announcements in admin UI to be newest-first ([ThibG](https://github.com/tootsuite/mastodon/pull/15091)) -- **Change account suspensions to be reversible by default** ([Gargron](https://github.com/tootsuite/mastodon/pull/14726), [ThibG](https://github.com/tootsuite/mastodon/pull/15152), [ThibG](https://github.com/tootsuite/mastodon/pull/15106), [ThibG](https://github.com/tootsuite/mastodon/pull/15100), [ThibG](https://github.com/tootsuite/mastodon/pull/15099), [noellabo](https://github.com/tootsuite/mastodon/pull/14855), [ThibG](https://github.com/tootsuite/mastodon/pull/15380), [Gargron](https://github.com/tootsuite/mastodon/pull/15420), [Gargron](https://github.com/tootsuite/mastodon/pull/15414)) +- Change order of announcements in admin UI to be newest-first ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15091)) +- **Change account suspensions to be reversible by default** ([Gargron](https://github.com/mastodon/mastodon/pull/14726), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15152), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15106), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15100), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15099), [noellabo](https://github.com/mastodon/mastodon/pull/14855), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15380), [Gargron](https://github.com/mastodon/mastodon/pull/15420), [Gargron](https://github.com/mastodon/mastodon/pull/15414)) - Suspensions no longer equal deletions - A suspended account can be unsuspended with minimal consequences for 30 days - Immediate deletion of data is still available as an explicit option - Suspended accounts can request an archive of their data through the UI - Change REST API to return empty data for suspended accounts (14765) -- Change web UI to show empty profile for suspended accounts ([Gargron](https://github.com/tootsuite/mastodon/pull/14766), [Gargron](https://github.com/tootsuite/mastodon/pull/15345)) -- Change featured hashtag suggestions to be recently used instead of most used ([abcang](https://github.com/tootsuite/mastodon/pull/14760)) -- Change direct toots to appear in the home feed again ([Gargron](https://github.com/tootsuite/mastodon/pull/14711), [ThibG](https://github.com/tootsuite/mastodon/pull/15182), [noellabo](https://github.com/tootsuite/mastodon/pull/14727)) +- Change web UI to show empty profile for suspended accounts ([Gargron](https://github.com/mastodon/mastodon/pull/14766), [Gargron](https://github.com/mastodon/mastodon/pull/15345)) +- Change featured hashtag suggestions to be recently used instead of most used ([abcang](https://github.com/mastodon/mastodon/pull/14760)) +- Change direct toots to appear in the home feed again ([Gargron](https://github.com/mastodon/mastodon/pull/14711), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15182), [noellabo](https://github.com/mastodon/mastodon/pull/14727)) - Return to treating all toots the same instead of trying to retrofit direct visibility into an instant messaging model -- Change email address validation to return more specific errors ([ThibG](https://github.com/tootsuite/mastodon/pull/14565)) -- Change HTTP signature requirements to include `Digest` header on `POST` requests ([ThibG](https://github.com/tootsuite/mastodon/pull/15069)) -- Change click area of video/audio player buttons to be bigger in web UI ([ariasuni](https://github.com/tootsuite/mastodon/pull/15049)) -- Change order of filters by alphabetic by "keyword or phrase" ([ariasuni](https://github.com/tootsuite/mastodon/pull/15050)) -- Change suspension of remote accounts to also undo outgoing follows ([ThibG](https://github.com/tootsuite/mastodon/pull/15188)) -- Change string "Home" to "Home and lists" in the filter creation screen ([ariasuni](https://github.com/tootsuite/mastodon/pull/15139)) -- Change string "Boost to original audience" to "Boost with original visibility" in web UI ([3n-k1](https://github.com/tootsuite/mastodon/pull/14598)) -- Change string "Show more" to "Show newer" and "Show older" on public pages ([ariasuni](https://github.com/tootsuite/mastodon/pull/15052)) -- Change order of announcements to be reverse chronological in web UI ([dariusk](https://github.com/tootsuite/mastodon/pull/15065), [dariusk](https://github.com/tootsuite/mastodon/pull/15070)) -- Change RTL detection to rely on unicode-bidi paragraph by paragraph in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/14573)) -- Change visibility icon next to timestamp to be clickable in web UI ([ariasuni](https://github.com/tootsuite/mastodon/pull/15053), [mayaeh](https://github.com/tootsuite/mastodon/pull/15055)) -- Change public thread view to hide "Show thread" link ([ThibG](https://github.com/tootsuite/mastodon/pull/15266)) -- Change number format on about page from full to shortened ([Gargron](https://github.com/tootsuite/mastodon/pull/15327)) -- Change how scheduled tasks run in multi-process environments ([noellabo](https://github.com/tootsuite/mastodon/pull/15314)) +- Change email address validation to return more specific errors ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14565)) +- Change HTTP signature requirements to include `Digest` header on `POST` requests ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15069)) +- Change click area of video/audio player buttons to be bigger in web UI ([ariasuni](https://github.com/mastodon/mastodon/pull/15049)) +- Change order of filters by alphabetic by "keyword or phrase" ([ariasuni](https://github.com/mastodon/mastodon/pull/15050)) +- Change suspension of remote accounts to also undo outgoing follows ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15188)) +- Change string "Home" to "Home and lists" in the filter creation screen ([ariasuni](https://github.com/mastodon/mastodon/pull/15139)) +- Change string "Boost to original audience" to "Boost with original visibility" in web UI ([3n-k1](https://github.com/mastodon/mastodon/pull/14598)) +- Change string "Show more" to "Show newer" and "Show older" on public pages ([ariasuni](https://github.com/mastodon/mastodon/pull/15052)) +- Change order of announcements to be reverse chronological in web UI ([dariusk](https://github.com/mastodon/mastodon/pull/15065), [dariusk](https://github.com/mastodon/mastodon/pull/15070)) +- Change RTL detection to rely on unicode-bidi paragraph by paragraph in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/14573)) +- Change visibility icon next to timestamp to be clickable in web UI ([ariasuni](https://github.com/mastodon/mastodon/pull/15053), [mayaeh](https://github.com/mastodon/mastodon/pull/15055)) +- Change public thread view to hide "Show thread" link ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15266)) +- Change number format on about page from full to shortened ([Gargron](https://github.com/mastodon/mastodon/pull/15327)) +- Change how scheduled tasks run in multi-process environments ([noellabo](https://github.com/mastodon/mastodon/pull/15314)) - New dedicated queue `scheduler` - Runs by default when Sidekiq is executed with no options - Has to be added manually in a multi-process environment ### Removed -- Remove fade-in animation from modals in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/15199)) -- Remove auto-redirect to direct messages in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/15142)) -- Remove obsolete IndexedDB operations from web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/14730)) -- Remove dependency on unused and unmaintained http_parser.rb gem ([ThibG](https://github.com/tootsuite/mastodon/pull/14574)) +- Remove fade-in animation from modals in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/15199)) +- Remove auto-redirect to direct messages in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/15142)) +- Remove obsolete IndexedDB operations from web UI ([Gargron](https://github.com/mastodon/mastodon/pull/14730)) +- Remove dependency on unused and unmaintained http_parser.rb gem ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14574)) ### Fixed -- Fix layout on about page when contact account has a long username ([ThibG](https://github.com/tootsuite/mastodon/pull/15357)) -- Fix follow limit preventing re-following of a moved account ([Gargron](https://github.com/tootsuite/mastodon/pull/14207), [ThibG](https://github.com/tootsuite/mastodon/pull/15384)) -- **Fix deletes not reaching every server that interacted with toot** ([Gargron](https://github.com/tootsuite/mastodon/pull/15200)) +- Fix layout on about page when contact account has a long username ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15357)) +- Fix follow limit preventing re-following of a moved account ([Gargron](https://github.com/mastodon/mastodon/pull/14207), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15384)) +- **Fix deletes not reaching every server that interacted with toot** ([Gargron](https://github.com/mastodon/mastodon/pull/15200)) - Previously, delete of a toot would be primarily sent to the followers of its author, people mentioned in the toot, and people who reblogged the toot - Now, additionally, it is ensured that it is sent to people who replied to it, favourited it, and to the person it replies to even if that person is not mentioned -- Fix resolving an account through its non-canonical form (i.e. alternate domain) ([ThibG](https://github.com/tootsuite/mastodon/pull/15187)) -- Fix sending redundant ActivityPub events when processing remote account deletion ([ThibG](https://github.com/tootsuite/mastodon/pull/15104)) -- Fix Move handler not being triggered when failing to fetch target account ([ThibG](https://github.com/tootsuite/mastodon/pull/15107)) -- Fix downloading remote media files when server returns empty filename ([ThibG](https://github.com/tootsuite/mastodon/pull/14867)) -- Fix account processing failing because of large collections ([ThibG](https://github.com/tootsuite/mastodon/pull/15027)) -- Fix not being able to unfavorite toots one has lost access to ([ThibG](https://github.com/tootsuite/mastodon/pull/15192)) -- Fix not being able to unbookmark toots one has lost access to ([ThibG](https://github.com/tootsuite/mastodon/pull/14604)) -- Fix possible casing inconsistencies in hashtag search ([ThibG](https://github.com/tootsuite/mastodon/pull/14906)) -- Fix updating account counters when association is not yet created ([Gargron](https://github.com/tootsuite/mastodon/pull/15108)) -- Fix cookies not having a SameSite attribute ([Gargron](https://github.com/tootsuite/mastodon/pull/15098)) -- Fix poll ending notifications being created for each vote ([ThibG](https://github.com/tootsuite/mastodon/pull/15071)) -- Fix multiple boosts of a same toot erroneously appearing in TL ([ThibG](https://github.com/tootsuite/mastodon/pull/14759)) -- Fix asset builds not picking up `CDN_HOST` change ([ThibG](https://github.com/tootsuite/mastodon/pull/14381)) -- Fix desktop notifications permission prompt in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/14985), [Gargron](https://github.com/tootsuite/mastodon/pull/15141), [ThibG](https://github.com/tootsuite/mastodon/pull/13543), [ThibG](https://github.com/tootsuite/mastodon/pull/15176)) +- Fix resolving an account through its non-canonical form (i.e. alternate domain) ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15187)) +- Fix sending redundant ActivityPub events when processing remote account deletion ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15104)) +- Fix Move handler not being triggered when failing to fetch target account ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15107)) +- Fix downloading remote media files when server returns empty filename ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14867)) +- Fix account processing failing because of large collections ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15027)) +- Fix not being able to unfavorite toots one has lost access to ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15192)) +- Fix not being able to unbookmark toots one has lost access to ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14604)) +- Fix possible casing inconsistencies in hashtag search ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14906)) +- Fix updating account counters when association is not yet created ([Gargron](https://github.com/mastodon/mastodon/pull/15108)) +- Fix cookies not having a SameSite attribute ([Gargron](https://github.com/mastodon/mastodon/pull/15098)) +- Fix poll ending notifications being created for each vote ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15071)) +- Fix multiple boosts of a same toot erroneously appearing in TL ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14759)) +- Fix asset builds not picking up `CDN_HOST` change ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14381)) +- Fix desktop notifications permission prompt in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/14985), [Gargron](https://github.com/mastodon/mastodon/pull/15141), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/13543), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15176)) - Some time ago, browsers added a requirement that desktop notification prompts could only be displayed in response to a user-generated event (such as a click) - This means that for some time, users who haven't already given the permission before were not getting a prompt and as such were not receiving desktop notifications -- Fix "Mark media as sensitive" string not supporting pluralizations in other languages in web UI ([ariasuni](https://github.com/tootsuite/mastodon/pull/15051)) -- Fix glitched image uploads when canvas read access is blocked in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/15180)) -- Fix some account gallery items having empty labels in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/15073)) -- Fix alt-key hotkeys activating while typing in a text field in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14942)) -- Fix wrong seek bar width on media player in web UI ([mfmfuyu](https://github.com/tootsuite/mastodon/pull/15060)) -- Fix logging out on mobile in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14901)) -- Fix wrong click area for GIFVs in media modal in web UI ([noellabo](https://github.com/tootsuite/mastodon/pull/14615)) -- Fix unreadable placeholder text color in high contrast theme in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/14803)) -- Fix scrolling issues when closing some dropdown menus in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14606)) -- Fix notification filter bar incorrectly filtering gaps in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14808)) -- Fix disabled boost icon being replaced by private boost icon on hover in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14456)) -- Fix hashtag detection in compose form being different to server-side in web UI ([kedamaDQ](https://github.com/tootsuite/mastodon/pull/14484), [ThibG](https://github.com/tootsuite/mastodon/pull/14513)) -- Fix home last read marker mishandling gaps in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14809)) -- Fix unnecessary re-rendering of various components when typing in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/15286)) -- Fix notifications being unnecessarily re-rendered in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/15312)) -- Fix column swiping animation logic in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/15301)) -- Fix inefficiency when fetching hashtag timeline ([noellabo](https://github.com/tootsuite/mastodon/pull/14861), [akihikodaki](https://github.com/tootsuite/mastodon/pull/14662)) -- Fix inefficiency when fetching bookmarks ([akihikodaki](https://github.com/tootsuite/mastodon/pull/14674)) -- Fix inefficiency when fetching favourites ([akihikodaki](https://github.com/tootsuite/mastodon/pull/14673)) -- Fix inefficiency when fetching media-only account timeline ([akihikodaki](https://github.com/tootsuite/mastodon/pull/14675)) -- Fix inefficieny when deleting accounts ([Gargron](https://github.com/tootsuite/mastodon/pull/15387), [ThibG](https://github.com/tootsuite/mastodon/pull/15409), [ThibG](https://github.com/tootsuite/mastodon/pull/15407), [ThibG](https://github.com/tootsuite/mastodon/pull/15408), [ThibG](https://github.com/tootsuite/mastodon/pull/15402), [ThibG](https://github.com/tootsuite/mastodon/pull/15416), [Gargron](https://github.com/tootsuite/mastodon/pull/15421)) -- Fix redundant query when processing batch actions on custom emojis ([niwatori24](https://github.com/tootsuite/mastodon/pull/14534)) -- Fix slow distinct queries where grouped queries are faster ([Gargron](https://github.com/tootsuite/mastodon/pull/15287)) -- Fix performance on instances list in admin UI ([Gargron](https://github.com/tootsuite/mastodon/pull/15282)) -- Fix server actor appearing in list of accounts in admin UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14567)) -- Fix "bootstrap timeline accounts" toggle in site settings in admin UI ([ThibG](https://github.com/tootsuite/mastodon/pull/15325)) -- Fix PostgreSQL secret name for cronjob in Helm chart ([metal3d](https://github.com/tootsuite/mastodon/pull/15072)) -- Fix Procfile not being compatible with herokuish ([acuteaura](https://github.com/tootsuite/mastodon/pull/12685)) -- Fix installation of tini being split into multiple steps in Dockerfile ([ryncsn](https://github.com/tootsuite/mastodon/pull/14686)) +- Fix "Mark media as sensitive" string not supporting pluralizations in other languages in web UI ([ariasuni](https://github.com/mastodon/mastodon/pull/15051)) +- Fix glitched image uploads when canvas read access is blocked in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15180)) +- Fix some account gallery items having empty labels in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15073)) +- Fix alt-key hotkeys activating while typing in a text field in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14942)) +- Fix wrong seek bar width on media player in web UI ([mfmfuyu](https://github.com/mastodon/mastodon/pull/15060)) +- Fix logging out on mobile in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14901)) +- Fix wrong click area for GIFVs in media modal in web UI ([noellabo](https://github.com/mastodon/mastodon/pull/14615)) +- Fix unreadable placeholder text color in high contrast theme in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/14803)) +- Fix scrolling issues when closing some dropdown menus in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14606)) +- Fix notification filter bar incorrectly filtering gaps in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14808)) +- Fix disabled boost icon being replaced by private boost icon on hover in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14456)) +- Fix hashtag detection in compose form being different to server-side in web UI ([kedamaDQ](https://github.com/mastodon/mastodon/pull/14484), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/14513)) +- Fix home last read marker mishandling gaps in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14809)) +- Fix unnecessary re-rendering of various components when typing in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/15286)) +- Fix notifications being unnecessarily re-rendered in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15312)) +- Fix column swiping animation logic in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15301)) +- Fix inefficiency when fetching hashtag timeline ([noellabo](https://github.com/mastodon/mastodon/pull/14861), [akihikodaki](https://github.com/mastodon/mastodon/pull/14662)) +- Fix inefficiency when fetching bookmarks ([akihikodaki](https://github.com/mastodon/mastodon/pull/14674)) +- Fix inefficiency when fetching favourites ([akihikodaki](https://github.com/mastodon/mastodon/pull/14673)) +- Fix inefficiency when fetching media-only account timeline ([akihikodaki](https://github.com/mastodon/mastodon/pull/14675)) +- Fix inefficieny when deleting accounts ([Gargron](https://github.com/mastodon/mastodon/pull/15387), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15409), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15407), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15408), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15402), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15416), [Gargron](https://github.com/mastodon/mastodon/pull/15421)) +- Fix redundant query when processing batch actions on custom emojis ([niwatori24](https://github.com/mastodon/mastodon/pull/14534)) +- Fix slow distinct queries where grouped queries are faster ([Gargron](https://github.com/mastodon/mastodon/pull/15287)) +- Fix performance on instances list in admin UI ([Gargron](https://github.com/mastodon/mastodon/pull/15282)) +- Fix server actor appearing in list of accounts in admin UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14567)) +- Fix "bootstrap timeline accounts" toggle in site settings in admin UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15325)) +- Fix PostgreSQL secret name for cronjob in Helm chart ([metal3d](https://github.com/mastodon/mastodon/pull/15072)) +- Fix Procfile not being compatible with herokuish ([acuteaura](https://github.com/mastodon/mastodon/pull/12685)) +- Fix installation of tini being split into multiple steps in Dockerfile ([ryncsn](https://github.com/mastodon/mastodon/pull/14686)) ### Security -- Fix streaming API allowing connections to persist after access token invalidation ([Gargron](https://github.com/tootsuite/mastodon/pull/15111)) -- Fix 2FA/sign-in token sessions being valid after password change ([Gargron](https://github.com/tootsuite/mastodon/pull/14802)) -- Fix resolving accounts sometimes creating duplicate records for a given ActivityPub identifier ([ThibG](https://github.com/tootsuite/mastodon/pull/15364)) +- Fix streaming API allowing connections to persist after access token invalidation ([Gargron](https://github.com/mastodon/mastodon/pull/15111)) +- Fix 2FA/sign-in token sessions being valid after password change ([Gargron](https://github.com/mastodon/mastodon/pull/14802)) +- Fix resolving accounts sometimes creating duplicate records for a given ActivityPub identifier ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15364)) ## [3.2.2] - 2020-12-19 ### Added -- Add `tootctl maintenance fix-duplicates` ([ThibG](https://github.com/tootsuite/mastodon/pull/14860), [Gargron](https://github.com/tootsuite/mastodon/pull/15223)) +- Add `tootctl maintenance fix-duplicates` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14860), [Gargron](https://github.com/mastodon/mastodon/pull/15223)) - Index corruption in the database? - This command is for you ### Removed -- Remove dependency on unused and unmaintained http_parser.rb gem ([ThibG](https://github.com/tootsuite/mastodon/pull/14574)) +- Remove dependency on unused and unmaintained http_parser.rb gem ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14574)) ### Fixed -- Fix Move handler not being triggered when failing to fetch target account ([ThibG](https://github.com/tootsuite/mastodon/pull/15107)) -- Fix downloading remote media files when server returns empty filename ([ThibG](https://github.com/tootsuite/mastodon/pull/14867)) -- Fix possible casing inconsistencies in hashtag search ([ThibG](https://github.com/tootsuite/mastodon/pull/14906)) -- Fix updating account counters when association is not yet created ([Gargron](https://github.com/tootsuite/mastodon/pull/15108)) -- Fix account processing failing because of large collections ([ThibG](https://github.com/tootsuite/mastodon/pull/15027)) -- Fix resolving an account through its non-canonical form (i.e. alternate domain) ([ThibG](https://github.com/tootsuite/mastodon/pull/15187)) -- Fix slow distinct queries where grouped queries are faster ([Gargron](https://github.com/tootsuite/mastodon/pull/15287)) +- Fix Move handler not being triggered when failing to fetch target account ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15107)) +- Fix downloading remote media files when server returns empty filename ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14867)) +- Fix possible casing inconsistencies in hashtag search ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14906)) +- Fix updating account counters when association is not yet created ([Gargron](https://github.com/mastodon/mastodon/pull/15108)) +- Fix account processing failing because of large collections ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15027)) +- Fix resolving an account through its non-canonical form (i.e. alternate domain) ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15187)) +- Fix slow distinct queries where grouped queries are faster ([Gargron](https://github.com/mastodon/mastodon/pull/15287)) ### Security -- Fix 2FA/sign-in token sessions being valid after password change ([Gargron](https://github.com/tootsuite/mastodon/pull/14802)) -- Fix resolving accounts sometimes creating duplicate records for a given ActivityPub identifier ([ThibG](https://github.com/tootsuite/mastodon/pull/15364)) +- Fix 2FA/sign-in token sessions being valid after password change ([Gargron](https://github.com/mastodon/mastodon/pull/14802)) +- Fix resolving accounts sometimes creating duplicate records for a given ActivityPub identifier ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15364)) ## [3.2.1] - 2020-10-19 ### Added -- Add support for latest HTTP Signatures spec draft ([ThibG](https://github.com/tootsuite/mastodon/pull/14556)) -- Add support for inlined objects in ActivityPub `to`/`cc` ([ThibG](https://github.com/tootsuite/mastodon/pull/14514)) +- Add support for latest HTTP Signatures spec draft ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14556)) +- Add support for inlined objects in ActivityPub `to`/`cc` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14514)) ### Changed -- Change actors to not be served at all without authentication in limited federation mode ([ThibG](https://github.com/tootsuite/mastodon/pull/14800)) +- Change actors to not be served at all without authentication in limited federation mode ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14800)) - Previously, a bare version of an actor was served when not authenticated, i.e. username and public key - Because all actor fetch requests are signed using a separate system actor, that is no longer required ### Fixed -- Fix `tootctl media` commands not recognizing very large IDs ([ThibG](https://github.com/tootsuite/mastodon/pull/14536)) -- Fix crash when failing to load emoji picker in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14525)) -- Fix contrast requirements in thumbnail color extraction ([ThibG](https://github.com/tootsuite/mastodon/pull/14464)) -- Fix audio/video player not using `CDN_HOST` on public pages ([ThibG](https://github.com/tootsuite/mastodon/pull/14486)) -- Fix private boost icon not being used on public pages ([OmmyZhang](https://github.com/tootsuite/mastodon/pull/14471)) -- Fix audio player on Safari in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14485), [ThibG](https://github.com/tootsuite/mastodon/pull/14465)) -- Fix dereferencing remote statuses not using the correct account for signature when receiving a targeted inbox delivery ([ThibG](https://github.com/tootsuite/mastodon/pull/14656)) -- Fix nil error in `tootctl media remove` ([noellabo](https://github.com/tootsuite/mastodon/pull/14657)) -- Fix videos with near-60 fps being rejected ([Gargron](https://github.com/tootsuite/mastodon/pull/14684)) -- Fix reported statuses not being included in warning e-mail ([Gargron](https://github.com/tootsuite/mastodon/pull/14778)) -- Fix `Reject` activities of `Follow` objects not correctly destroying a follow relationship ([ThibG](https://github.com/tootsuite/mastodon/pull/14479)) -- Fix inefficiencies in fan-out-on-write service ([Gargron](https://github.com/tootsuite/mastodon/pull/14682), [noellabo](https://github.com/tootsuite/mastodon/pull/14709)) -- Fix timeout errors when trying to webfinger some IPv6 configurations ([Gargron](https://github.com/tootsuite/mastodon/pull/14919)) -- Fix files served as `application/octet-stream` being rejected without attempting mime type detection ([ThibG](https://github.com/tootsuite/mastodon/pull/14452)) +- Fix `tootctl media` commands not recognizing very large IDs ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14536)) +- Fix crash when failing to load emoji picker in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14525)) +- Fix contrast requirements in thumbnail color extraction ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14464)) +- Fix audio/video player not using `CDN_HOST` on public pages ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14486)) +- Fix private boost icon not being used on public pages ([OmmyZhang](https://github.com/mastodon/mastodon/pull/14471)) +- Fix audio player on Safari in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14485), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/14465)) +- Fix dereferencing remote statuses not using the correct account for signature when receiving a targeted inbox delivery ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14656)) +- Fix nil error in `tootctl media remove` ([noellabo](https://github.com/mastodon/mastodon/pull/14657)) +- Fix videos with near-60 fps being rejected ([Gargron](https://github.com/mastodon/mastodon/pull/14684)) +- Fix reported statuses not being included in warning e-mail ([Gargron](https://github.com/mastodon/mastodon/pull/14778)) +- Fix `Reject` activities of `Follow` objects not correctly destroying a follow relationship ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14479)) +- Fix inefficiencies in fan-out-on-write service ([Gargron](https://github.com/mastodon/mastodon/pull/14682), [noellabo](https://github.com/mastodon/mastodon/pull/14709)) +- Fix timeout errors when trying to webfinger some IPv6 configurations ([Gargron](https://github.com/mastodon/mastodon/pull/14919)) +- Fix files served as `application/octet-stream` being rejected without attempting mime type detection ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14452)) ## [3.2.0] - 2020-07-27 ### Added -- Add `SMTP_SSL` environment variable ([OmmyZhang](https://github.com/tootsuite/mastodon/pull/14309)) -- Add hotkey for toggling content warning input in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13987)) -- **Add e-mail-based sign in challenge for users with disabled 2FA** ([Gargron](https://github.com/tootsuite/mastodon/pull/14013)) +- Add `SMTP_SSL` environment variable ([OmmyZhang](https://github.com/mastodon/mastodon/pull/14309)) +- Add hotkey for toggling content warning input in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13987)) +- **Add e-mail-based sign in challenge for users with disabled 2FA** ([Gargron](https://github.com/mastodon/mastodon/pull/14013)) - If user tries signing in after: - Being inactive for a while - With a previously unknown IP - Without 2FA being enabled - Require to enter a token sent via e-mail before sigining in -- Add `limit` param to RSS feeds ([noellabo](https://github.com/tootsuite/mastodon/pull/13743)) -- Add `visibility` param to share page ([noellabo](https://github.com/tootsuite/mastodon/pull/13023)) -- Add blurhash to link previews ([ThibG](https://github.com/tootsuite/mastodon/pull/13984), [ThibG](https://github.com/tootsuite/mastodon/pull/14143), [ThibG](https://github.com/tootsuite/mastodon/pull/13985), [Sasha-Sorokin](https://github.com/tootsuite/mastodon/pull/14267), [Sasha-Sorokin](https://github.com/tootsuite/mastodon/pull/14278), [ThibG](https://github.com/tootsuite/mastodon/pull/14126), [ThibG](https://github.com/tootsuite/mastodon/pull/14261), [ThibG](https://github.com/tootsuite/mastodon/pull/14260)) +- Add `limit` param to RSS feeds ([noellabo](https://github.com/mastodon/mastodon/pull/13743)) +- Add `visibility` param to share page ([noellabo](https://github.com/mastodon/mastodon/pull/13023)) +- Add blurhash to link previews ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13984), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/14143), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/13985), [Sasha-Sorokin](https://github.com/mastodon/mastodon/pull/14267), [Sasha-Sorokin](https://github.com/mastodon/mastodon/pull/14278), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/14126), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/14261), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/14260)) - In web UI, toots cannot be marked as sensitive unless there is media attached - However, it's possible to do via API or ActivityPub - Thumnails of link previews of such posts now use blurhash in web UI - The Card entity in REST API has a new `blurhash` attribute -- Add support for `summary` field for media description in ActivityPub ([ThibG](https://github.com/tootsuite/mastodon/pull/13763)) -- Add hints about incomplete remote content to web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/14031), [noellabo](https://github.com/tootsuite/mastodon/pull/14195)) -- **Add personal notes for accounts** ([ThibG](https://github.com/tootsuite/mastodon/pull/14148), [Gargron](https://github.com/tootsuite/mastodon/pull/14208), [Sasha-Sorokin](https://github.com/tootsuite/mastodon/pull/14251)) +- Add support for `summary` field for media description in ActivityPub ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13763)) +- Add hints about incomplete remote content to web UI ([Gargron](https://github.com/mastodon/mastodon/pull/14031), [noellabo](https://github.com/mastodon/mastodon/pull/14195)) +- **Add personal notes for accounts** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14148), [Gargron](https://github.com/mastodon/mastodon/pull/14208), [Sasha-Sorokin](https://github.com/mastodon/mastodon/pull/14251)) - To clarify, these are notes only you can see, to help you remember details - Notes can be viewed and edited from profiles in web UI - New REST API: `POST /api/v1/accounts/:id/note` with `comment` param - The Relationship entity in REST API has a new `note` attribute -- Add Helm chart ([dunn](https://github.com/tootsuite/mastodon/pull/14090), [dunn](https://github.com/tootsuite/mastodon/pull/14256), [dunn](https://github.com/tootsuite/mastodon/pull/14245)) -- **Add customizable thumbnails for audio and video attachments** ([Gargron](https://github.com/tootsuite/mastodon/pull/14145), [Gargron](https://github.com/tootsuite/mastodon/pull/14244), [Gargron](https://github.com/tootsuite/mastodon/pull/14273), [Gargron](https://github.com/tootsuite/mastodon/pull/14203), [ThibG](https://github.com/tootsuite/mastodon/pull/14255), [ThibG](https://github.com/tootsuite/mastodon/pull/14306), [noellabo](https://github.com/tootsuite/mastodon/pull/14358), [noellabo](https://github.com/tootsuite/mastodon/pull/14357)) +- Add Helm chart ([dunn](https://github.com/mastodon/mastodon/pull/14090), [dunn](https://github.com/mastodon/mastodon/pull/14256), [dunn](https://github.com/mastodon/mastodon/pull/14245)) +- **Add customizable thumbnails for audio and video attachments** ([Gargron](https://github.com/mastodon/mastodon/pull/14145), [Gargron](https://github.com/mastodon/mastodon/pull/14244), [Gargron](https://github.com/mastodon/mastodon/pull/14273), [Gargron](https://github.com/mastodon/mastodon/pull/14203), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/14255), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/14306), [noellabo](https://github.com/mastodon/mastodon/pull/14358), [noellabo](https://github.com/mastodon/mastodon/pull/14357)) - Metadata (album, artist, etc) is no longer stripped from audio files - Album art is automatically extracted from audio files - Thumbnail can be manually uploaded for both audio and video attachments @@ -433,120 +433,120 @@ All notable changes to this project will be documented in this file. - And on `PUT /api/v1/media/:id` - ActivityPub representation of media attachments represents custom thumbnails with an `icon` attribute - The Media Attachment entity in REST API now has a `preview_remote_url` to its `preview_url`, equivalent to `remote_url` to its `url` -- **Add color extraction for thumbnails** ([Gargron](https://github.com/tootsuite/mastodon/pull/14209), [ThibG](https://github.com/tootsuite/mastodon/pull/14264)) +- **Add color extraction for thumbnails** ([Gargron](https://github.com/mastodon/mastodon/pull/14209), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/14264)) - The `meta` attribute on the Media Attachment entity in REST API can now have a `colors` attribute which in turn contains three hex colors: `background`, `foreground`, and `accent` - The background color is chosen from the most dominant color around the edges of the thumbnail - The foreground and accent colors are chosen from the colors that are the most different from the background color using the CIEDE2000 algorithm - The most satured color of the two is designated as the accent color - The one with the highest W3C contrast is designated as the foreground color - If there are not enough colors in the thumbnail, new ones are generated using a monochrome pattern -- Add a visibility indicator to toots in web UI ([noellabo](https://github.com/tootsuite/mastodon/pull/14123), [highemerly](https://github.com/tootsuite/mastodon/pull/14292)) -- Add `tootctl email_domain_blocks` ([tateisu](https://github.com/tootsuite/mastodon/pull/13589), [Gargron](https://github.com/tootsuite/mastodon/pull/14147)) -- Add "Add new domain block" to header of federation page in admin UI ([ariasuni](https://github.com/tootsuite/mastodon/pull/13934)) -- Add ability to keep emoji picker open with ctrl+click in web UI ([bclindner](https://github.com/tootsuite/mastodon/pull/13896), [noellabo](https://github.com/tootsuite/mastodon/pull/14096)) -- Add custom icon for private boosts in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14380)) -- Add support for Create and Update activities that don't inline objects in ActivityPub ([ThibG](https://github.com/tootsuite/mastodon/pull/14359)) -- Add support for Undo activities that don't inline activities in ActivityPub ([ThibG](https://github.com/tootsuite/mastodon/pull/14346)) +- Add a visibility indicator to toots in web UI ([noellabo](https://github.com/mastodon/mastodon/pull/14123), [highemerly](https://github.com/mastodon/mastodon/pull/14292)) +- Add `tootctl email_domain_blocks` ([tateisu](https://github.com/mastodon/mastodon/pull/13589), [Gargron](https://github.com/mastodon/mastodon/pull/14147)) +- Add "Add new domain block" to header of federation page in admin UI ([ariasuni](https://github.com/mastodon/mastodon/pull/13934)) +- Add ability to keep emoji picker open with ctrl+click in web UI ([bclindner](https://github.com/mastodon/mastodon/pull/13896), [noellabo](https://github.com/mastodon/mastodon/pull/14096)) +- Add custom icon for private boosts in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14380)) +- Add support for Create and Update activities that don't inline objects in ActivityPub ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14359)) +- Add support for Undo activities that don't inline activities in ActivityPub ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14346)) ### Changed -- Change `.env.production.sample` to be leaner and cleaner ([Gargron](https://github.com/tootsuite/mastodon/pull/14206)) +- Change `.env.production.sample` to be leaner and cleaner ([Gargron](https://github.com/mastodon/mastodon/pull/14206)) - It was overloaded as de-facto documentation and getting quite crowded - Defer to the actual documentation while still giving a minimal example -- Change `tootctl search deploy` to work faster and display progress ([Gargron](https://github.com/tootsuite/mastodon/pull/14300)) -- Change User-Agent of link preview fetching service to include "Bot" ([Gargron](https://github.com/tootsuite/mastodon/pull/14248)) +- Change `tootctl search deploy` to work faster and display progress ([Gargron](https://github.com/mastodon/mastodon/pull/14300)) +- Change User-Agent of link preview fetching service to include "Bot" ([Gargron](https://github.com/mastodon/mastodon/pull/14248)) - Some websites may not render OpenGraph tags into HTML if that's not the case -- Change behaviour to carry blocks over when someone migrates their followers ([ThibG](https://github.com/tootsuite/mastodon/pull/14144)) -- Change volume control and download buttons in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/14122)) -- **Change design of audio players in web UI** ([Gargron](https://github.com/tootsuite/mastodon/pull/14095), [ThibG](https://github.com/tootsuite/mastodon/pull/14281), [Gargron](https://github.com/tootsuite/mastodon/pull/14282), [ThibG](https://github.com/tootsuite/mastodon/pull/14118), [Gargron](https://github.com/tootsuite/mastodon/pull/14199), [ThibG](https://github.com/tootsuite/mastodon/pull/14338)) -- Change reply filter to never filter own toots in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14128)) -- Change boost button to no longer serve as visibility indicator in web UI ([noellabo](https://github.com/tootsuite/mastodon/pull/14132), [ThibG](https://github.com/tootsuite/mastodon/pull/14373)) -- Change contrast of flash messages ([cchoi12](https://github.com/tootsuite/mastodon/pull/13892)) -- Change wording from "Hide media" to "Hide image/images" in web UI ([ariasuni](https://github.com/tootsuite/mastodon/pull/13834)) -- Change appearence of settings pages to be more consistent ([ariasuni](https://github.com/tootsuite/mastodon/pull/13938)) -- Change "Add media" tooltip to not include long list of formats in web UI ([ariasuni](https://github.com/tootsuite/mastodon/pull/13954)) -- Change how badly contrasting emoji are rendered in web UI ([leo60228](https://github.com/tootsuite/mastodon/pull/13773), [ThibG](https://github.com/tootsuite/mastodon/pull/13772), [mfmfuyu](https://github.com/tootsuite/mastodon/pull/14020), [ThibG](https://github.com/tootsuite/mastodon/pull/14015)) -- Change structure of unavailable content section on about page ([ariasuni](https://github.com/tootsuite/mastodon/pull/13930)) -- Change behaviour to accept ActivityPub activities relayed through group actor ([noellabo](https://github.com/tootsuite/mastodon/pull/14279)) -- Change amount of processing retries for ActivityPub activities ([noellabo](https://github.com/tootsuite/mastodon/pull/14355)) +- Change behaviour to carry blocks over when someone migrates their followers ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14144)) +- Change volume control and download buttons in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/14122)) +- **Change design of audio players in web UI** ([Gargron](https://github.com/mastodon/mastodon/pull/14095), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/14281), [Gargron](https://github.com/mastodon/mastodon/pull/14282), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/14118), [Gargron](https://github.com/mastodon/mastodon/pull/14199), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/14338)) +- Change reply filter to never filter own toots in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14128)) +- Change boost button to no longer serve as visibility indicator in web UI ([noellabo](https://github.com/mastodon/mastodon/pull/14132), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/14373)) +- Change contrast of flash messages ([cchoi12](https://github.com/mastodon/mastodon/pull/13892)) +- Change wording from "Hide media" to "Hide image/images" in web UI ([ariasuni](https://github.com/mastodon/mastodon/pull/13834)) +- Change appearence of settings pages to be more consistent ([ariasuni](https://github.com/mastodon/mastodon/pull/13938)) +- Change "Add media" tooltip to not include long list of formats in web UI ([ariasuni](https://github.com/mastodon/mastodon/pull/13954)) +- Change how badly contrasting emoji are rendered in web UI ([leo60228](https://github.com/mastodon/mastodon/pull/13773), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/13772), [mfmfuyu](https://github.com/mastodon/mastodon/pull/14020), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/14015)) +- Change structure of unavailable content section on about page ([ariasuni](https://github.com/mastodon/mastodon/pull/13930)) +- Change behaviour to accept ActivityPub activities relayed through group actor ([noellabo](https://github.com/mastodon/mastodon/pull/14279)) +- Change amount of processing retries for ActivityPub activities ([noellabo](https://github.com/mastodon/mastodon/pull/14355)) ### Removed -- Remove the terms "blacklist" and "whitelist" from UX ([Gargron](https://github.com/tootsuite/mastodon/pull/14149), [mayaeh](https://github.com/tootsuite/mastodon/pull/14192)) +- Remove the terms "blacklist" and "whitelist" from UX ([Gargron](https://github.com/mastodon/mastodon/pull/14149), [mayaeh](https://github.com/mastodon/mastodon/pull/14192)) - Environment variables changed (old versions continue to work): - `WHITELIST_MODE` → `LIMITED_FEDERATION_MODE` - `EMAIL_DOMAIN_BLACKLIST` → `EMAIL_DOMAIN_DENYLIST` - `EMAIL_DOMAIN_WHITELIST` → `EMAIL_DOMAIN_ALLOWLIST` - CLI option changed: - `tootctl domains purge --whitelist-mode` → `tootctl domains purge --limited-federation-mode` -- Remove some unnecessary database indices ([lfuelling](https://github.com/tootsuite/mastodon/pull/13695), [noellabo](https://github.com/tootsuite/mastodon/pull/14259)) -- Remove unnecessary Node.js version upper bound ([ykzts](https://github.com/tootsuite/mastodon/pull/14139)) +- Remove some unnecessary database indices ([lfuelling](https://github.com/mastodon/mastodon/pull/13695), [noellabo](https://github.com/mastodon/mastodon/pull/14259)) +- Remove unnecessary Node.js version upper bound ([ykzts](https://github.com/mastodon/mastodon/pull/14139)) ### Fixed -- Fix `following` param not working when exact match is found in account search ([noellabo](https://github.com/tootsuite/mastodon/pull/14394)) -- Fix sometimes occuring duplicate mention notifications ([noellabo](https://github.com/tootsuite/mastodon/pull/14378)) -- Fix RSS feeds not being cachable ([ThibG](https://github.com/tootsuite/mastodon/pull/14368)) -- Fix lack of locking around processing of Announce activities in ActivityPub ([noellabo](https://github.com/tootsuite/mastodon/pull/14365)) -- Fix boosted toots from blocked account not being retroactively removed from TL ([ThibG](https://github.com/tootsuite/mastodon/pull/14339)) -- Fix large shortened numbers (like 1.2K) using incorrect pluralization ([Sasha-Sorokin](https://github.com/tootsuite/mastodon/pull/14061)) -- Fix streaming server trying to use empty password to connect to Redis when `REDIS_PASSWORD` is given but blank ([ThibG](https://github.com/tootsuite/mastodon/pull/14135)) -- Fix being unable to unboost posts when blocked by their author ([ThibG](https://github.com/tootsuite/mastodon/pull/14308)) -- Fix account domain block not properly unfollowing accounts from domain ([Gargron](https://github.com/tootsuite/mastodon/pull/14304)) -- Fix removing a domain allow wiping known accounts in open federation mode ([ThibG](https://github.com/tootsuite/mastodon/pull/14298)) -- Fix blocks and mutes pagination in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14275)) -- Fix new posts pushing down origin of opened dropdown in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14271), [ThibG](https://github.com/tootsuite/mastodon/pull/14348)) -- Fix timeline markers not being saved sometimes ([ThibG](https://github.com/tootsuite/mastodon/pull/13887), [ThibG](https://github.com/tootsuite/mastodon/pull/13889), [ThibG](https://github.com/tootsuite/mastodon/pull/14155)) -- Fix CSV uploads being rejected ([noellabo](https://github.com/tootsuite/mastodon/pull/13835)) -- Fix incompatibility with ElasticSearch 7.x ([noellabo](https://github.com/tootsuite/mastodon/pull/13828)) -- Fix being able to search posts where you're in the target audience but not actively mentioned ([noellabo](https://github.com/tootsuite/mastodon/pull/13829)) -- Fix non-local posts appearing on local-only hashtag timelines in web UI ([noellabo](https://github.com/tootsuite/mastodon/pull/13827)) -- Fix `tootctl media remove-orphans` choking on unknown files in storage ([Gargron](https://github.com/tootsuite/mastodon/pull/13765)) -- Fix `tootctl upgrade storage-schema` misbehaving ([Gargron](https://github.com/tootsuite/mastodon/pull/13761), [angristan](https://github.com/tootsuite/mastodon/pull/13768)) +- Fix `following` param not working when exact match is found in account search ([noellabo](https://github.com/mastodon/mastodon/pull/14394)) +- Fix sometimes occuring duplicate mention notifications ([noellabo](https://github.com/mastodon/mastodon/pull/14378)) +- Fix RSS feeds not being cachable ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14368)) +- Fix lack of locking around processing of Announce activities in ActivityPub ([noellabo](https://github.com/mastodon/mastodon/pull/14365)) +- Fix boosted toots from blocked account not being retroactively removed from TL ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14339)) +- Fix large shortened numbers (like 1.2K) using incorrect pluralization ([Sasha-Sorokin](https://github.com/mastodon/mastodon/pull/14061)) +- Fix streaming server trying to use empty password to connect to Redis when `REDIS_PASSWORD` is given but blank ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14135)) +- Fix being unable to unboost posts when blocked by their author ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14308)) +- Fix account domain block not properly unfollowing accounts from domain ([Gargron](https://github.com/mastodon/mastodon/pull/14304)) +- Fix removing a domain allow wiping known accounts in open federation mode ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14298)) +- Fix blocks and mutes pagination in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14275)) +- Fix new posts pushing down origin of opened dropdown in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14271), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/14348)) +- Fix timeline markers not being saved sometimes ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13887), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/13889), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/14155)) +- Fix CSV uploads being rejected ([noellabo](https://github.com/mastodon/mastodon/pull/13835)) +- Fix incompatibility with ElasticSearch 7.x ([noellabo](https://github.com/mastodon/mastodon/pull/13828)) +- Fix being able to search posts where you're in the target audience but not actively mentioned ([noellabo](https://github.com/mastodon/mastodon/pull/13829)) +- Fix non-local posts appearing on local-only hashtag timelines in web UI ([noellabo](https://github.com/mastodon/mastodon/pull/13827)) +- Fix `tootctl media remove-orphans` choking on unknown files in storage ([Gargron](https://github.com/mastodon/mastodon/pull/13765)) +- Fix `tootctl upgrade storage-schema` misbehaving ([Gargron](https://github.com/mastodon/mastodon/pull/13761), [angristan](https://github.com/mastodon/mastodon/pull/13768)) - Fix it marking records as upgraded even though no files were moved - Fix it not working with S3 storage - Fix it not working with custom emojis -- Fix GIF reader raising incorrect exceptions ([ThibG](https://github.com/tootsuite/mastodon/pull/13760)) -- Fix hashtag search performing account search as well ([ThibG](https://github.com/tootsuite/mastodon/pull/13758)) -- Fix Webfinger returning wrong status code on malformed or missing param ([ThibG](https://github.com/tootsuite/mastodon/pull/13759)) -- Fix `rake mastodon:setup` error when some environment variables are set ([ThibG](https://github.com/tootsuite/mastodon/pull/13928)) -- Fix admin page crashing when trying to block an invalid domain name in admin UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13884)) -- Fix unsent toot confirmation dialog not popping up in single column mode in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13888)) -- Fix performance of follow import ([noellabo](https://github.com/tootsuite/mastodon/pull/13836)) +- Fix GIF reader raising incorrect exceptions ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13760)) +- Fix hashtag search performing account search as well ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13758)) +- Fix Webfinger returning wrong status code on malformed or missing param ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13759)) +- Fix `rake mastodon:setup` error when some environment variables are set ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13928)) +- Fix admin page crashing when trying to block an invalid domain name in admin UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13884)) +- Fix unsent toot confirmation dialog not popping up in single column mode in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13888)) +- Fix performance of follow import ([noellabo](https://github.com/mastodon/mastodon/pull/13836)) - Reduce timeout of Webfinger requests to that of other requests - Use circuit breakers to stop hitting unresponsive servers - Avoid hitting servers that are already known to be generally unavailable -- Fix filters ignoring media descriptions ([BenLubar](https://github.com/tootsuite/mastodon/pull/13837)) -- Fix some actions on custom emojis leading to cryptic errors in admin UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13951)) -- Fix ActivityPub serialization of replies when some of them are URIs ([ThibG](https://github.com/tootsuite/mastodon/pull/13957)) -- Fix `rake mastodon:setup` choking on environment variables containing `%` ([ThibG](https://github.com/tootsuite/mastodon/pull/13940)) -- Fix account redirect confirmation message talking about moved followers ([ThibG](https://github.com/tootsuite/mastodon/pull/13950)) -- Fix avatars having the wrong size on public detailed status pages ([ThibG](https://github.com/tootsuite/mastodon/pull/14140)) -- Fix various issues around OpenGraph representation of media ([Gargron](https://github.com/tootsuite/mastodon/pull/14133)) +- Fix filters ignoring media descriptions ([BenLubar](https://github.com/mastodon/mastodon/pull/13837)) +- Fix some actions on custom emojis leading to cryptic errors in admin UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13951)) +- Fix ActivityPub serialization of replies when some of them are URIs ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13957)) +- Fix `rake mastodon:setup` choking on environment variables containing `%` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13940)) +- Fix account redirect confirmation message talking about moved followers ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13950)) +- Fix avatars having the wrong size on public detailed status pages ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14140)) +- Fix various issues around OpenGraph representation of media ([Gargron](https://github.com/mastodon/mastodon/pull/14133)) - Pages containing audio no longer say "Attached: 1 image" in description - Audio attachments now represented as OpenGraph `og:audio` - The `twitter:player` page now uses Mastodon's proper audio/video player - Audio/video buffered bars now display correctly in audio/video player - Volume and progress bars now respond to movement/move smoother -- Fix audio/video/images/cards not reacting to window resizes in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/14130)) -- Fix very wide media attachments resulting in too thin a thumbnail in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14127)) -- Fix crash when merging posts into home feed after following someone ([ThibG](https://github.com/tootsuite/mastodon/pull/14129)) -- Fix unique username constraint for local users not being enforced in database ([ThibG](https://github.com/tootsuite/mastodon/pull/14099)) -- Fix unnecessary gap under video modal in web UI ([mfmfuyu](https://github.com/tootsuite/mastodon/pull/14098)) -- Fix 2FA and sign in token pages not respecting user locale ([mfmfuyu](https://github.com/tootsuite/mastodon/pull/14087)) -- Fix unapproved users being able to view profiles when in limited-federation mode *and* requiring approval for sign-ups ([ThibG](https://github.com/tootsuite/mastodon/pull/14093)) -- Fix initial audio volume not corresponding to what's displayed in audio player in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14057)) -- Fix timelines sometimes jumping when closing modals in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14019)) -- Fix memory usage of downloading remote files ([Gargron](https://github.com/tootsuite/mastodon/pull/14184), [Gargron](https://github.com/tootsuite/mastodon/pull/14181), [noellabo](https://github.com/tootsuite/mastodon/pull/14356)) +- Fix audio/video/images/cards not reacting to window resizes in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/14130)) +- Fix very wide media attachments resulting in too thin a thumbnail in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14127)) +- Fix crash when merging posts into home feed after following someone ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14129)) +- Fix unique username constraint for local users not being enforced in database ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14099)) +- Fix unnecessary gap under video modal in web UI ([mfmfuyu](https://github.com/mastodon/mastodon/pull/14098)) +- Fix 2FA and sign in token pages not respecting user locale ([mfmfuyu](https://github.com/mastodon/mastodon/pull/14087)) +- Fix unapproved users being able to view profiles when in limited-federation mode *and* requiring approval for sign-ups ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14093)) +- Fix initial audio volume not corresponding to what's displayed in audio player in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14057)) +- Fix timelines sometimes jumping when closing modals in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14019)) +- Fix memory usage of downloading remote files ([Gargron](https://github.com/mastodon/mastodon/pull/14184), [Gargron](https://github.com/mastodon/mastodon/pull/14181), [noellabo](https://github.com/mastodon/mastodon/pull/14356)) - Don't read entire file (up to 40 MB) into memory - Read and write it to temp file in small chunks -- Fix inconsistent account header padding in web UI ([trwnh](https://github.com/tootsuite/mastodon/pull/14179)) -- Fix Thai being skipped from language detection ([Sasha-Sorokin](https://github.com/tootsuite/mastodon/pull/13989)) +- Fix inconsistent account header padding in web UI ([trwnh](https://github.com/mastodon/mastodon/pull/14179)) +- Fix Thai being skipped from language detection ([Sasha-Sorokin](https://github.com/mastodon/mastodon/pull/13989)) - Since Thai has its own alphabet, it can be detected more reliably -- Fix broken hashtag column options styling in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14247)) -- Fix pointer cursor being shown on toots that are not clickable in web UI ([arielrodrigues](https://github.com/tootsuite/mastodon/pull/14185)) -- Fix lock icon not being shown when locking account in profile settings ([ThibG](https://github.com/tootsuite/mastodon/pull/14190)) -- Fix domain blocks doing work the wrong way around ([ThibG](https://github.com/tootsuite/mastodon/pull/13424)) +- Fix broken hashtag column options styling in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14247)) +- Fix pointer cursor being shown on toots that are not clickable in web UI ([arielrodrigues](https://github.com/mastodon/mastodon/pull/14185)) +- Fix lock icon not being shown when locking account in profile settings ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14190)) +- Fix domain blocks doing work the wrong way around ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13424)) - Instead of suspending accounts one by one, mark all as suspended first (quick) - Only then proceed to start removing their data (slow) - Clear out media attachments in a separate worker (slow) @@ -554,1301 +554,1301 @@ All notable changes to this project will be documented in this file. ## [3.1.5] - 2020-07-07 ### Security -- Fix media attachment enumeration ([ThibG](https://github.com/tootsuite/mastodon/pull/14254)) -- Change rate limits for various paths ([Gargron](https://github.com/tootsuite/mastodon/pull/14253)) -- Fix other sessions not being logged out on password change ([Gargron](https://github.com/tootsuite/mastodon/pull/14252)) +- Fix media attachment enumeration ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14254)) +- Change rate limits for various paths ([Gargron](https://github.com/mastodon/mastodon/pull/14253)) +- Fix other sessions not being logged out on password change ([Gargron](https://github.com/mastodon/mastodon/pull/14252)) ## [3.1.4] - 2020-05-14 ### Added -- Add `vi` to available locales ([taicv](https://github.com/tootsuite/mastodon/pull/13542)) -- Add ability to remove identity proofs from account ([Gargron](https://github.com/tootsuite/mastodon/pull/13682)) -- Add ability to exclude local content from federated timeline ([noellabo](https://github.com/tootsuite/mastodon/pull/13504), [noellabo](https://github.com/tootsuite/mastodon/pull/13745)) +- Add `vi` to available locales ([taicv](https://github.com/mastodon/mastodon/pull/13542)) +- Add ability to remove identity proofs from account ([Gargron](https://github.com/mastodon/mastodon/pull/13682)) +- Add ability to exclude local content from federated timeline ([noellabo](https://github.com/mastodon/mastodon/pull/13504), [noellabo](https://github.com/mastodon/mastodon/pull/13745)) - Add `remote` param to `GET /api/v1/timelines/public` REST API - Add `public/remote` / `public:remote` variants to streaming API - "Remote only" option in federated timeline column settings in web UI -- Add ability to exclude remote content from hashtag timelines in web UI ([noellabo](https://github.com/tootsuite/mastodon/pull/13502)) +- Add ability to exclude remote content from hashtag timelines in web UI ([noellabo](https://github.com/mastodon/mastodon/pull/13502)) - No changes to REST API - "Local only" option in hashtag column settings in web UI -- Add Capistrano tasks that reload the services after deploying ([berkes](https://github.com/tootsuite/mastodon/pull/12642)) -- Add `invites_enabled` attribute to `GET /api/v1/instance` in REST API ([ThibG](https://github.com/tootsuite/mastodon/pull/13501)) -- Add `tootctl emoji export` command ([lfuelling](https://github.com/tootsuite/mastodon/pull/13534)) -- Add separate cache directory for non-local uploads ([Gargron](https://github.com/tootsuite/mastodon/pull/12821), [Hanage999](https://github.com/tootsuite/mastodon/pull/13593), [mayaeh](https://github.com/tootsuite/mastodon/pull/13551)) +- Add Capistrano tasks that reload the services after deploying ([berkes](https://github.com/mastodon/mastodon/pull/12642)) +- Add `invites_enabled` attribute to `GET /api/v1/instance` in REST API ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13501)) +- Add `tootctl emoji export` command ([lfuelling](https://github.com/mastodon/mastodon/pull/13534)) +- Add separate cache directory for non-local uploads ([Gargron](https://github.com/mastodon/mastodon/pull/12821), [Hanage999](https://github.com/mastodon/mastodon/pull/13593), [mayaeh](https://github.com/mastodon/mastodon/pull/13551)) - Add `tootctl upgrade storage-schema` command to move old non-local uploads to the cache directory -- Add buttons to delete header and avatar from profile settings ([sternenseemann](https://github.com/tootsuite/mastodon/pull/13234)) -- Add emoji graphics and shortcodes from Twemoji 12.1.5 ([DeeUnderscore](https://github.com/tootsuite/mastodon/pull/13021)) +- Add buttons to delete header and avatar from profile settings ([sternenseemann](https://github.com/mastodon/mastodon/pull/13234)) +- Add emoji graphics and shortcodes from Twemoji 12.1.5 ([DeeUnderscore](https://github.com/mastodon/mastodon/pull/13021)) ### Changed -- Change error message when trying to migrate to an account that does not have current account set as an alias to be more clear ([TheEvilSkeleton](https://github.com/tootsuite/mastodon/pull/13746)) -- Change delivery failure tracking to work with hostnames instead of URLs ([Gargron](https://github.com/tootsuite/mastodon/pull/13437), [noellabo](https://github.com/tootsuite/mastodon/pull/13481), [noellabo](https://github.com/tootsuite/mastodon/pull/13482), [noellabo](https://github.com/tootsuite/mastodon/pull/13535)) -- Change Content-Security-Policy to not need unsafe-inline style-src ([ThibG](https://github.com/tootsuite/mastodon/pull/13679), [ThibG](https://github.com/tootsuite/mastodon/pull/13692), [ThibG](https://github.com/tootsuite/mastodon/pull/13576), [ThibG](https://github.com/tootsuite/mastodon/pull/13575), [ThibG](https://github.com/tootsuite/mastodon/pull/13438)) -- Change how RSS items are titled and formatted ([ThibG](https://github.com/tootsuite/mastodon/pull/13592), [ykzts](https://github.com/tootsuite/mastodon/pull/13591)) +- Change error message when trying to migrate to an account that does not have current account set as an alias to be more clear ([TheEvilSkeleton](https://github.com/mastodon/mastodon/pull/13746)) +- Change delivery failure tracking to work with hostnames instead of URLs ([Gargron](https://github.com/mastodon/mastodon/pull/13437), [noellabo](https://github.com/mastodon/mastodon/pull/13481), [noellabo](https://github.com/mastodon/mastodon/pull/13482), [noellabo](https://github.com/mastodon/mastodon/pull/13535)) +- Change Content-Security-Policy to not need unsafe-inline style-src ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13679), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/13692), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/13576), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/13575), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/13438)) +- Change how RSS items are titled and formatted ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13592), [ykzts](https://github.com/mastodon/mastodon/pull/13591)) ### Fixed -- Fix dropdown of muted and followed accounts offering option to hide boosts in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13748)) -- Fix "You are already signed in" alert being shown at wrong times ([ThibG](https://github.com/tootsuite/mastodon/pull/13547)) -- Fix retrying of failed-to-download media files not actually working ([noellabo](https://github.com/tootsuite/mastodon/pull/13741)) -- Fix first poll option not being focused when adding a poll in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13740)) -- Fix `sr` locale being selected over `sr-Latn` ([ThibG](https://github.com/tootsuite/mastodon/pull/13693)) -- Fix error within error when limiting backtrace to 3 lines ([Gargron](https://github.com/tootsuite/mastodon/pull/13120)) -- Fix `tootctl media remove-orphans` crashing on "Import" files ([ThibG](https://github.com/tootsuite/mastodon/pull/13685)) -- Fix regression in `tootctl media remove-orphans` ([Gargron](https://github.com/tootsuite/mastodon/pull/13405)) -- Fix old unique jobs digests not having been cleaned up ([Gargron](https://github.com/tootsuite/mastodon/pull/13683)) -- Fix own following/followers not showing muted users ([ThibG](https://github.com/tootsuite/mastodon/pull/13614)) -- Fix list of followed people ignoring sorting on Follows & Followers page ([taras2358](https://github.com/tootsuite/mastodon/pull/13676)) -- Fix wrong pgHero Content-Security-Policy when `CDN_HOST` is set ([ThibG](https://github.com/tootsuite/mastodon/pull/13595)) -- Fix needlessly deduplicating usernames on collisions with remote accounts when signing-up through SAML/CAS ([kaiyou](https://github.com/tootsuite/mastodon/pull/13581)) -- Fix page incorrectly scrolling when bringing up dropdown menus in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13574)) -- Fix messed up z-index when NoScript blocks media/previews in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13449)) -- Fix "See what's happening" page showing public instead of local timeline for logged-in users ([ThibG](https://github.com/tootsuite/mastodon/pull/13499)) -- Fix not being able to resolve public resources in development environment ([Gargron](https://github.com/tootsuite/mastodon/pull/13505)) -- Fix uninformative error message when uploading unsupported image files ([ThibG](https://github.com/tootsuite/mastodon/pull/13540)) -- Fix expanded video player issues in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13541), [eai04191](https://github.com/tootsuite/mastodon/pull/13533)) -- Fix and refactor keyboard navigation in dropdown menus in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13528)) -- Fix uploaded image orientation being messed up in some browsers in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13493)) -- Fix actions log crash when displaying updates of deleted announcements in admin UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13489)) -- Fix search not working due to proxy settings when using hidden services ([Gargron](https://github.com/tootsuite/mastodon/pull/13488)) -- Fix poll refresh button not being debounced in web UI ([rasjonell](https://github.com/tootsuite/mastodon/pull/13485), [ThibG](https://github.com/tootsuite/mastodon/pull/13490)) -- Fix confusing error when failing to add an alias to an unknown account ([ThibG](https://github.com/tootsuite/mastodon/pull/13480)) -- Fix "Email changed" notification sometimes having wrong e-mail ([ThibG](https://github.com/tootsuite/mastodon/pull/13475)) -- Fix varioues issues on the account aliases page ([ThibG](https://github.com/tootsuite/mastodon/pull/13452)) -- Fix API footer link in web UI ([bubblineyuri](https://github.com/tootsuite/mastodon/pull/13441)) -- Fix pagination of following, followers, follow requests, blocks and mutes lists in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13445)) -- Fix styling of polls in JS-less fallback on public pages ([ThibG](https://github.com/tootsuite/mastodon/pull/13436)) -- Fix trying to delete already deleted file when post-processing ([Gargron](https://github.com/tootsuite/mastodon/pull/13406)) +- Fix dropdown of muted and followed accounts offering option to hide boosts in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13748)) +- Fix "You are already signed in" alert being shown at wrong times ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13547)) +- Fix retrying of failed-to-download media files not actually working ([noellabo](https://github.com/mastodon/mastodon/pull/13741)) +- Fix first poll option not being focused when adding a poll in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13740)) +- Fix `sr` locale being selected over `sr-Latn` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13693)) +- Fix error within error when limiting backtrace to 3 lines ([Gargron](https://github.com/mastodon/mastodon/pull/13120)) +- Fix `tootctl media remove-orphans` crashing on "Import" files ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13685)) +- Fix regression in `tootctl media remove-orphans` ([Gargron](https://github.com/mastodon/mastodon/pull/13405)) +- Fix old unique jobs digests not having been cleaned up ([Gargron](https://github.com/mastodon/mastodon/pull/13683)) +- Fix own following/followers not showing muted users ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13614)) +- Fix list of followed people ignoring sorting on Follows & Followers page ([taras2358](https://github.com/mastodon/mastodon/pull/13676)) +- Fix wrong pgHero Content-Security-Policy when `CDN_HOST` is set ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13595)) +- Fix needlessly deduplicating usernames on collisions with remote accounts when signing-up through SAML/CAS ([kaiyou](https://github.com/mastodon/mastodon/pull/13581)) +- Fix page incorrectly scrolling when bringing up dropdown menus in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13574)) +- Fix messed up z-index when NoScript blocks media/previews in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13449)) +- Fix "See what's happening" page showing public instead of local timeline for logged-in users ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13499)) +- Fix not being able to resolve public resources in development environment ([Gargron](https://github.com/mastodon/mastodon/pull/13505)) +- Fix uninformative error message when uploading unsupported image files ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13540)) +- Fix expanded video player issues in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13541), [eai04191](https://github.com/mastodon/mastodon/pull/13533)) +- Fix and refactor keyboard navigation in dropdown menus in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13528)) +- Fix uploaded image orientation being messed up in some browsers in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13493)) +- Fix actions log crash when displaying updates of deleted announcements in admin UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13489)) +- Fix search not working due to proxy settings when using hidden services ([Gargron](https://github.com/mastodon/mastodon/pull/13488)) +- Fix poll refresh button not being debounced in web UI ([rasjonell](https://github.com/mastodon/mastodon/pull/13485), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/13490)) +- Fix confusing error when failing to add an alias to an unknown account ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13480)) +- Fix "Email changed" notification sometimes having wrong e-mail ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13475)) +- Fix varioues issues on the account aliases page ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13452)) +- Fix API footer link in web UI ([bubblineyuri](https://github.com/mastodon/mastodon/pull/13441)) +- Fix pagination of following, followers, follow requests, blocks and mutes lists in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13445)) +- Fix styling of polls in JS-less fallback on public pages ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13436)) +- Fix trying to delete already deleted file when post-processing ([Gargron](https://github.com/mastodon/mastodon/pull/13406)) ### Security -- Fix Doorkeeper vulnerability that exposed app secret to users who authorized the app and reset secret of the web UI that could have been exposed ([dependabot-preview[bot]](https://github.com/tootsuite/mastodon/pull/13613), [Gargron](https://github.com/tootsuite/mastodon/pull/13688)) +- Fix Doorkeeper vulnerability that exposed app secret to users who authorized the app and reset secret of the web UI that could have been exposed ([dependabot-preview[bot]](https://github.com/mastodon/mastodon/pull/13613), [Gargron](https://github.com/mastodon/mastodon/pull/13688)) - For apps that self-register on behalf of every individual user (such as most mobile apps), this is a non-issue - The issue only affects developers of apps who are shared between multiple users, such as server-side apps like cross-posters ## [3.1.3] - 2020-04-05 ### Added -- Add ability to filter audit log in admin UI ([Gargron](https://github.com/tootsuite/mastodon/pull/13381)) -- Add titles to warning presets in admin UI ([Gargron](https://github.com/tootsuite/mastodon/pull/13252)) -- Add option to include resolved DNS records when blacklisting e-mail domains in admin UI ([Gargron](https://github.com/tootsuite/mastodon/pull/13254)) -- Add ability to delete files uploaded for settings in admin UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13192)) -- Add sorting by username, creation and last activity in admin UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13076)) -- Add explanation as to why unlocked accounts may have follow requests in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13385)) -- Add link to bookmarks to dropdown in web UI ([mayaeh](https://github.com/tootsuite/mastodon/pull/13273)) -- Add support for links to statuses in announcements to be opened in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13212), [ThibG](https://github.com/tootsuite/mastodon/pull/13250)) -- Add tooltips to audio/video player buttons in web UI ([ariasuni](https://github.com/tootsuite/mastodon/pull/13203)) -- Add submit button to the top of preferences pages ([guigeekz](https://github.com/tootsuite/mastodon/pull/13068)) -- Add specific rate limits for posting, following and reporting ([Gargron](https://github.com/tootsuite/mastodon/pull/13172), [Gargron](https://github.com/tootsuite/mastodon/pull/13390)) +- Add ability to filter audit log in admin UI ([Gargron](https://github.com/mastodon/mastodon/pull/13381)) +- Add titles to warning presets in admin UI ([Gargron](https://github.com/mastodon/mastodon/pull/13252)) +- Add option to include resolved DNS records when blacklisting e-mail domains in admin UI ([Gargron](https://github.com/mastodon/mastodon/pull/13254)) +- Add ability to delete files uploaded for settings in admin UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13192)) +- Add sorting by username, creation and last activity in admin UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13076)) +- Add explanation as to why unlocked accounts may have follow requests in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13385)) +- Add link to bookmarks to dropdown in web UI ([mayaeh](https://github.com/mastodon/mastodon/pull/13273)) +- Add support for links to statuses in announcements to be opened in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13212), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/13250)) +- Add tooltips to audio/video player buttons in web UI ([ariasuni](https://github.com/mastodon/mastodon/pull/13203)) +- Add submit button to the top of preferences pages ([guigeekz](https://github.com/mastodon/mastodon/pull/13068)) +- Add specific rate limits for posting, following and reporting ([Gargron](https://github.com/mastodon/mastodon/pull/13172), [Gargron](https://github.com/mastodon/mastodon/pull/13390)) - 300 posts every 3 hours - 400 follows or follow requests every 24 hours - 400 reports every 24 hours -- Add federation support for the "hide network" preference ([ThibG](https://github.com/tootsuite/mastodon/pull/11673)) -- Add `--skip-media-remove` option to `tootctl statuses remove` ([tateisu](https://github.com/tootsuite/mastodon/pull/13080)) +- Add federation support for the "hide network" preference ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11673)) +- Add `--skip-media-remove` option to `tootctl statuses remove` ([tateisu](https://github.com/mastodon/mastodon/pull/13080)) ### Changed -- **Change design of polls in web UI** ([Sasha-Sorokin](https://github.com/tootsuite/mastodon/pull/13257), [ThibG](https://github.com/tootsuite/mastodon/pull/13313)) -- Change status click areas in web UI to be bigger ([ariasuni](https://github.com/tootsuite/mastodon/pull/13327)) -- **Change `tootctl media remove-orphans` to work for all classes** ([Gargron](https://github.com/tootsuite/mastodon/pull/13316)) -- **Change local media attachments to perform heavy processing asynchronously** ([Gargron](https://github.com/tootsuite/mastodon/pull/13210)) -- Change video uploads to always be converted to H264/MP4 ([Gargron](https://github.com/tootsuite/mastodon/pull/13220), [ThibG](https://github.com/tootsuite/mastodon/pull/13239), [ThibG](https://github.com/tootsuite/mastodon/pull/13242)) -- Change video uploads to enforce certain limits ([Gargron](https://github.com/tootsuite/mastodon/pull/13218)) +- **Change design of polls in web UI** ([Sasha-Sorokin](https://github.com/mastodon/mastodon/pull/13257), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/13313)) +- Change status click areas in web UI to be bigger ([ariasuni](https://github.com/mastodon/mastodon/pull/13327)) +- **Change `tootctl media remove-orphans` to work for all classes** ([Gargron](https://github.com/mastodon/mastodon/pull/13316)) +- **Change local media attachments to perform heavy processing asynchronously** ([Gargron](https://github.com/mastodon/mastodon/pull/13210)) +- Change video uploads to always be converted to H264/MP4 ([Gargron](https://github.com/mastodon/mastodon/pull/13220), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/13239), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/13242)) +- Change video uploads to enforce certain limits ([Gargron](https://github.com/mastodon/mastodon/pull/13218)) - Dimensions smaller than 1920x1200px - Frame rate at most 60fps -- Change the tooltip "Toggle visibility" to "Hide media" in web UI ([ariasuni](https://github.com/tootsuite/mastodon/pull/13199)) -- Change description of privacy levels to be more intuitive in web UI ([ariasuni](https://github.com/tootsuite/mastodon/pull/13197)) -- Change GIF label to be displayed even when autoplay is enabled in web UI ([koyuawsmbrtn](https://github.com/tootsuite/mastodon/pull/13209)) -- Change the string "Hide everything from …" to "Block domain …" in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13178), [mayaeh](https://github.com/tootsuite/mastodon/pull/13221)) -- Change wording of media display preferences to be more intuitive ([ariasuni](https://github.com/tootsuite/mastodon/pull/13198)) +- Change the tooltip "Toggle visibility" to "Hide media" in web UI ([ariasuni](https://github.com/mastodon/mastodon/pull/13199)) +- Change description of privacy levels to be more intuitive in web UI ([ariasuni](https://github.com/mastodon/mastodon/pull/13197)) +- Change GIF label to be displayed even when autoplay is enabled in web UI ([koyuawsmbrtn](https://github.com/mastodon/mastodon/pull/13209)) +- Change the string "Hide everything from …" to "Block domain …" in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13178), [mayaeh](https://github.com/mastodon/mastodon/pull/13221)) +- Change wording of media display preferences to be more intuitive ([ariasuni](https://github.com/mastodon/mastodon/pull/13198)) ### Deprecated -- `POST /api/v1/media` → `POST /api/v2/media` ([Gargron](https://github.com/tootsuite/mastodon/pull/13210)) +- `POST /api/v1/media` → `POST /api/v2/media` ([Gargron](https://github.com/mastodon/mastodon/pull/13210)) ### Fixed -- Fix `tootctl media remove-orphans` ignoring `PAPERCLIP_ROOT_PATH` ([Gargron](https://github.com/tootsuite/mastodon/pull/13375)) -- Fix returning results when searching for URL with non-zero offset ([Gargron](https://github.com/tootsuite/mastodon/pull/13377)) -- Fix pinning a column in web UI sometimes redirecting out of web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/13376)) -- Fix background jobs not using locks like they are supposed to ([Gargron](https://github.com/tootsuite/mastodon/pull/13361)) -- Fix content warning being unnecessarily cleared when hiding content warning input in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13348)) -- Fix "Show more" not switching to "Show less" on public pages ([ThibG](https://github.com/tootsuite/mastodon/pull/13174)) -- Fix import overwrite option not being selectable ([noellabo](https://github.com/tootsuite/mastodon/pull/13347)) -- Fix wrong color for ellipsis in boost confirmation dialog in web UI ([ariasuni](https://github.com/tootsuite/mastodon/pull/13355)) -- Fix unnecessary unfollowing when importing follows with overwrite option ([noellabo](https://github.com/tootsuite/mastodon/pull/13350)) -- Fix 404 and 410 API errors being silently discarded in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13279)) -- Fix OCR not working on Safari because of unsupported worker-src CSP ([ThibG](https://github.com/tootsuite/mastodon/pull/13323)) -- Fix media not being marked sensitive when a content warning is set with no text ([ThibG](https://github.com/tootsuite/mastodon/pull/13277)) -- Fix crash after deleting announcements in web UI ([codesections](https://github.com/tootsuite/mastodon/pull/13283), [ThibG](https://github.com/tootsuite/mastodon/pull/13312)) -- Fix bookmarks not being searchable ([Kjwon15](https://github.com/tootsuite/mastodon/pull/13271), [noellabo](https://github.com/tootsuite/mastodon/pull/13293)) -- Fix reported accounts not being whitelisted from further spam checks when resolving a spam check report ([ThibG](https://github.com/tootsuite/mastodon/pull/13289)) -- Fix web UI crash in single-column mode on prehistoric browsers ([ThibG](https://github.com/tootsuite/mastodon/pull/13267)) -- Fix some timeouts when searching for URLs ([ThibG](https://github.com/tootsuite/mastodon/pull/13253)) -- Fix detailed view of direct messages displaying a 0 boost count in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13244)) -- Fix regression in “Edit media” modal in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13243)) -- Fix public posts from silenced accounts not being changed to unlisted visibility ([ThibG](https://github.com/tootsuite/mastodon/pull/13096)) -- Fix error when searching for URLs that contain the mention syntax ([ThibG](https://github.com/tootsuite/mastodon/pull/13151)) -- Fix text area above/right of emoji picker being accidentally clickable in web UI ([ariasuni](https://github.com/tootsuite/mastodon/pull/13148)) -- Fix too large announcements not being scrollable in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13211)) -- Fix `tootctl media remove-orphans` crashing when encountering invalid media ([ThibG](https://github.com/tootsuite/mastodon/pull/13170)) -- Fix installation failing when Redis password contains special characters ([ThibG](https://github.com/tootsuite/mastodon/pull/13156)) -- Fix announcements with fully-qualified mentions to local users crashing web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13164)) +- Fix `tootctl media remove-orphans` ignoring `PAPERCLIP_ROOT_PATH` ([Gargron](https://github.com/mastodon/mastodon/pull/13375)) +- Fix returning results when searching for URL with non-zero offset ([Gargron](https://github.com/mastodon/mastodon/pull/13377)) +- Fix pinning a column in web UI sometimes redirecting out of web UI ([Gargron](https://github.com/mastodon/mastodon/pull/13376)) +- Fix background jobs not using locks like they are supposed to ([Gargron](https://github.com/mastodon/mastodon/pull/13361)) +- Fix content warning being unnecessarily cleared when hiding content warning input in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13348)) +- Fix "Show more" not switching to "Show less" on public pages ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13174)) +- Fix import overwrite option not being selectable ([noellabo](https://github.com/mastodon/mastodon/pull/13347)) +- Fix wrong color for ellipsis in boost confirmation dialog in web UI ([ariasuni](https://github.com/mastodon/mastodon/pull/13355)) +- Fix unnecessary unfollowing when importing follows with overwrite option ([noellabo](https://github.com/mastodon/mastodon/pull/13350)) +- Fix 404 and 410 API errors being silently discarded in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13279)) +- Fix OCR not working on Safari because of unsupported worker-src CSP ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13323)) +- Fix media not being marked sensitive when a content warning is set with no text ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13277)) +- Fix crash after deleting announcements in web UI ([codesections](https://github.com/mastodon/mastodon/pull/13283), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/13312)) +- Fix bookmarks not being searchable ([Kjwon15](https://github.com/mastodon/mastodon/pull/13271), [noellabo](https://github.com/mastodon/mastodon/pull/13293)) +- Fix reported accounts not being whitelisted from further spam checks when resolving a spam check report ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13289)) +- Fix web UI crash in single-column mode on prehistoric browsers ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13267)) +- Fix some timeouts when searching for URLs ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13253)) +- Fix detailed view of direct messages displaying a 0 boost count in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13244)) +- Fix regression in “Edit media” modal in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13243)) +- Fix public posts from silenced accounts not being changed to unlisted visibility ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13096)) +- Fix error when searching for URLs that contain the mention syntax ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13151)) +- Fix text area above/right of emoji picker being accidentally clickable in web UI ([ariasuni](https://github.com/mastodon/mastodon/pull/13148)) +- Fix too large announcements not being scrollable in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13211)) +- Fix `tootctl media remove-orphans` crashing when encountering invalid media ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13170)) +- Fix installation failing when Redis password contains special characters ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13156)) +- Fix announcements with fully-qualified mentions to local users crashing web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13164)) ### Security -- Fix re-sending of e-mail confirmation not being rate limited ([Gargron](https://github.com/tootsuite/mastodon/pull/13360)) +- Fix re-sending of e-mail confirmation not being rate limited ([Gargron](https://github.com/mastodon/mastodon/pull/13360)) ## [v3.1.2] - 2020-02-27 ### Added -- Add `--reset-password` option to `tootctl accounts modify` ([ThibG](https://github.com/tootsuite/mastodon/pull/13126)) -- Add source-mapped stacktrace to error message in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13082)) +- Add `--reset-password` option to `tootctl accounts modify` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13126)) +- Add source-mapped stacktrace to error message in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13082)) ### Fixed -- Fix dismissing an announcement twice raising an obscure error ([ThibG](https://github.com/tootsuite/mastodon/pull/13124)) -- Fix misleading error when attempting to re-send a pending follow request ([ThibG](https://github.com/tootsuite/mastodon/pull/13133)) -- Fix backups failing when files are missing from media attachments ([ThibG](https://github.com/tootsuite/mastodon/pull/13146)) -- Fix duplicate accounts being created when fetching an account for its key only ([ThibG](https://github.com/tootsuite/mastodon/pull/13147)) -- Fix `/web` redirecting to `/web/web` in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13128)) -- Fix previously OStatus-based accounts not being detected as ActivityPub ([ThibG](https://github.com/tootsuite/mastodon/pull/13129)) -- Fix account JSON/RSS not being cacheable due to wrong mime type comparison ([ThibG](https://github.com/tootsuite/mastodon/pull/13116)) -- Fix old browsers crashing because of missing `finally` polyfill in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13115)) -- Fix account's bio not being shown if there are no proofs/fields in admin UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13075)) -- Fix sign-ups without checked user agreement being accepted through the web form ([ThibG](https://github.com/tootsuite/mastodon/pull/13088)) -- Fix non-x64 architectures not being able to build Docker image because of hardcoded Node.js architecture ([SaraSmiseth](https://github.com/tootsuite/mastodon/pull/13081)) -- Fix invite request input not being shown on sign-up error if left empty ([ThibG](https://github.com/tootsuite/mastodon/pull/13089)) -- Fix some migration hints mentioning GitLab instead of Mastodon ([saper](https://github.com/tootsuite/mastodon/pull/13084)) +- Fix dismissing an announcement twice raising an obscure error ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13124)) +- Fix misleading error when attempting to re-send a pending follow request ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13133)) +- Fix backups failing when files are missing from media attachments ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13146)) +- Fix duplicate accounts being created when fetching an account for its key only ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13147)) +- Fix `/web` redirecting to `/web/web` in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13128)) +- Fix previously OStatus-based accounts not being detected as ActivityPub ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13129)) +- Fix account JSON/RSS not being cacheable due to wrong mime type comparison ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13116)) +- Fix old browsers crashing because of missing `finally` polyfill in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13115)) +- Fix account's bio not being shown if there are no proofs/fields in admin UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13075)) +- Fix sign-ups without checked user agreement being accepted through the web form ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13088)) +- Fix non-x64 architectures not being able to build Docker image because of hardcoded Node.js architecture ([SaraSmiseth](https://github.com/mastodon/mastodon/pull/13081)) +- Fix invite request input not being shown on sign-up error if left empty ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13089)) +- Fix some migration hints mentioning GitLab instead of Mastodon ([saper](https://github.com/mastodon/mastodon/pull/13084)) ### Security -- Fix leak of arbitrary statuses through unfavourite action in REST API ([Gargron](https://github.com/tootsuite/mastodon/pull/13161)) +- Fix leak of arbitrary statuses through unfavourite action in REST API ([Gargron](https://github.com/mastodon/mastodon/pull/13161)) ## [3.1.1] - 2020-02-10 ### Fixed -- Fix yanked dependency preventing installation ([mayaeh](https://github.com/tootsuite/mastodon/pull/13059)) +- Fix yanked dependency preventing installation ([mayaeh](https://github.com/mastodon/mastodon/pull/13059)) ## [3.1.0] - 2020-02-09 ### Added -- Add bookmarks ([ThibG](https://github.com/tootsuite/mastodon/pull/7107), [Gargron](https://github.com/tootsuite/mastodon/pull/12494), [Gomasy](https://github.com/tootsuite/mastodon/pull/12381)) -- Add announcements ([Gargron](https://github.com/tootsuite/mastodon/pull/12662), [Gargron](https://github.com/tootsuite/mastodon/pull/12967), [Gargron](https://github.com/tootsuite/mastodon/pull/12970), [Gargron](https://github.com/tootsuite/mastodon/pull/12963), [Gargron](https://github.com/tootsuite/mastodon/pull/12950), [Gargron](https://github.com/tootsuite/mastodon/pull/12990), [Gargron](https://github.com/tootsuite/mastodon/pull/12949), [Gargron](https://github.com/tootsuite/mastodon/pull/12989), [Gargron](https://github.com/tootsuite/mastodon/pull/12964), [Gargron](https://github.com/tootsuite/mastodon/pull/12965), [ThibG](https://github.com/tootsuite/mastodon/pull/12958), [ThibG](https://github.com/tootsuite/mastodon/pull/12957), [Gargron](https://github.com/tootsuite/mastodon/pull/12955), [ThibG](https://github.com/tootsuite/mastodon/pull/12946), [ThibG](https://github.com/tootsuite/mastodon/pull/12954)) -- Add number animations in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/12948), [Gargron](https://github.com/tootsuite/mastodon/pull/12971)) -- Add `kab`, `is`, `kn`, `mr`, `ur` to available locales ([Gargron](https://github.com/tootsuite/mastodon/pull/12882), [BoFFire](https://github.com/tootsuite/mastodon/pull/12962), [Gargron](https://github.com/tootsuite/mastodon/pull/12379)) -- Add profile filter category ([ThibG](https://github.com/tootsuite/mastodon/pull/12918)) -- Add ability to add oneself to lists ([ThibG](https://github.com/tootsuite/mastodon/pull/12271)) -- Add hint how to contribute translations to preferences page ([Sasha-Sorokin](https://github.com/tootsuite/mastodon/pull/12736)) -- Add signatures to statuses in archive takeout ([noellabo](https://github.com/tootsuite/mastodon/pull/12649)) -- Add support for `magnet:` and `xmpp` links ([ThibG](https://github.com/tootsuite/mastodon/pull/12905), [ThibG](https://github.com/tootsuite/mastodon/pull/12709)) -- Add `follow_request` notification type ([ThibG](https://github.com/tootsuite/mastodon/pull/12198)) -- Add ability to filter reports by account domain in admin UI ([ThibG](https://github.com/tootsuite/mastodon/pull/12154)) -- Add link to search for users connected from the same IP address to admin UI ([ThibG](https://github.com/tootsuite/mastodon/pull/12157)) -- Add link to reports targeting a specific domain in admin view ([ThibG](https://github.com/tootsuite/mastodon/pull/12513)) -- Add support for EventSource streaming in web UI ([BenLubar](https://github.com/tootsuite/mastodon/pull/12887)) -- Add hotkey for opening media attachments in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/12498), [Kjwon15](https://github.com/tootsuite/mastodon/pull/12546)) -- Add relationship-based options to status dropdowns in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/12377), [ThibG](https://github.com/tootsuite/mastodon/pull/12535), [Gargron](https://github.com/tootsuite/mastodon/pull/12430)) -- Add support for submitting media description with `ctrl`+`enter` in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/12272)) -- Add download button to audio and video players in web UI ([NimaBoscarino](https://github.com/tootsuite/mastodon/pull/12179)) -- Add setting for whether to crop images in timelines in web UI ([duxovni](https://github.com/tootsuite/mastodon/pull/12126)) -- Add support for `Event` activities ([tcitworld](https://github.com/tootsuite/mastodon/pull/12637)) -- Add basic support for `Group` actors ([noellabo](https://github.com/tootsuite/mastodon/pull/12071)) -- Add `S3_OVERRIDE_PATH_STYLE` environment variable ([Gargron](https://github.com/tootsuite/mastodon/pull/12594)) -- Add `S3_OPEN_TIMEOUT` environment variable ([tateisu](https://github.com/tootsuite/mastodon/pull/12459)) -- Add `LDAP_MAIL` environment variable ([madmath03](https://github.com/tootsuite/mastodon/pull/12053)) -- Add `LDAP_UID_CONVERSION_ENABLED` environment variable ([madmath03](https://github.com/tootsuite/mastodon/pull/12461)) -- Add `--remote-only` option to `tootctl emoji purge` ([ThibG](https://github.com/tootsuite/mastodon/pull/12810)) -- Add `tootctl media remove-orphans` ([Gargron](https://github.com/tootsuite/mastodon/pull/12568), [Gargron](https://github.com/tootsuite/mastodon/pull/12571)) -- Add `tootctl media lookup` command ([irlcatgirl](https://github.com/tootsuite/mastodon/pull/12283)) -- Add cache for OEmbed endpoints to avoid extra HTTP requests ([Gargron](https://github.com/tootsuite/mastodon/pull/12403)) -- Add support for KaiOS arrow navigation to public pages ([nolanlawson](https://github.com/tootsuite/mastodon/pull/12251)) -- Add `discoverable` to accounts in REST API ([trwnh](https://github.com/tootsuite/mastodon/pull/12508)) -- Add admin setting to disable default follows ([ArisuOngaku](https://github.com/tootsuite/mastodon/pull/12566)) -- Add support for LDAP and PAM in the OAuth password grant strategy ([ntl-purism](https://github.com/tootsuite/mastodon/pull/12390), [Gargron](https://github.com/tootsuite/mastodon/pull/12743)) -- Allow support for `Accept`/`Reject` activities with a non-embedded object ([puckipedia](https://github.com/tootsuite/mastodon/pull/12199)) -- Add "Show thread" button to public profiles ([Sasha-Sorokin](https://github.com/tootsuite/mastodon/pull/13000)) +- Add bookmarks ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/7107), [Gargron](https://github.com/mastodon/mastodon/pull/12494), [Gomasy](https://github.com/mastodon/mastodon/pull/12381)) +- Add announcements ([Gargron](https://github.com/mastodon/mastodon/pull/12662), [Gargron](https://github.com/mastodon/mastodon/pull/12967), [Gargron](https://github.com/mastodon/mastodon/pull/12970), [Gargron](https://github.com/mastodon/mastodon/pull/12963), [Gargron](https://github.com/mastodon/mastodon/pull/12950), [Gargron](https://github.com/mastodon/mastodon/pull/12990), [Gargron](https://github.com/mastodon/mastodon/pull/12949), [Gargron](https://github.com/mastodon/mastodon/pull/12989), [Gargron](https://github.com/mastodon/mastodon/pull/12964), [Gargron](https://github.com/mastodon/mastodon/pull/12965), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/12958), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/12957), [Gargron](https://github.com/mastodon/mastodon/pull/12955), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/12946), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/12954)) +- Add number animations in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/12948), [Gargron](https://github.com/mastodon/mastodon/pull/12971)) +- Add `kab`, `is`, `kn`, `mr`, `ur` to available locales ([Gargron](https://github.com/mastodon/mastodon/pull/12882), [BoFFire](https://github.com/mastodon/mastodon/pull/12962), [Gargron](https://github.com/mastodon/mastodon/pull/12379)) +- Add profile filter category ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12918)) +- Add ability to add oneself to lists ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12271)) +- Add hint how to contribute translations to preferences page ([Sasha-Sorokin](https://github.com/mastodon/mastodon/pull/12736)) +- Add signatures to statuses in archive takeout ([noellabo](https://github.com/mastodon/mastodon/pull/12649)) +- Add support for `magnet:` and `xmpp` links ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12905), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/12709)) +- Add `follow_request` notification type ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12198)) +- Add ability to filter reports by account domain in admin UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12154)) +- Add link to search for users connected from the same IP address to admin UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12157)) +- Add link to reports targeting a specific domain in admin view ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12513)) +- Add support for EventSource streaming in web UI ([BenLubar](https://github.com/mastodon/mastodon/pull/12887)) +- Add hotkey for opening media attachments in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12498), [Kjwon15](https://github.com/mastodon/mastodon/pull/12546)) +- Add relationship-based options to status dropdowns in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/12377), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/12535), [Gargron](https://github.com/mastodon/mastodon/pull/12430)) +- Add support for submitting media description with `ctrl`+`enter` in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12272)) +- Add download button to audio and video players in web UI ([NimaBoscarino](https://github.com/mastodon/mastodon/pull/12179)) +- Add setting for whether to crop images in timelines in web UI ([duxovni](https://github.com/mastodon/mastodon/pull/12126)) +- Add support for `Event` activities ([tcitworld](https://github.com/mastodon/mastodon/pull/12637)) +- Add basic support for `Group` actors ([noellabo](https://github.com/mastodon/mastodon/pull/12071)) +- Add `S3_OVERRIDE_PATH_STYLE` environment variable ([Gargron](https://github.com/mastodon/mastodon/pull/12594)) +- Add `S3_OPEN_TIMEOUT` environment variable ([tateisu](https://github.com/mastodon/mastodon/pull/12459)) +- Add `LDAP_MAIL` environment variable ([madmath03](https://github.com/mastodon/mastodon/pull/12053)) +- Add `LDAP_UID_CONVERSION_ENABLED` environment variable ([madmath03](https://github.com/mastodon/mastodon/pull/12461)) +- Add `--remote-only` option to `tootctl emoji purge` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12810)) +- Add `tootctl media remove-orphans` ([Gargron](https://github.com/mastodon/mastodon/pull/12568), [Gargron](https://github.com/mastodon/mastodon/pull/12571)) +- Add `tootctl media lookup` command ([irlcatgirl](https://github.com/mastodon/mastodon/pull/12283)) +- Add cache for OEmbed endpoints to avoid extra HTTP requests ([Gargron](https://github.com/mastodon/mastodon/pull/12403)) +- Add support for KaiOS arrow navigation to public pages ([nolanlawson](https://github.com/mastodon/mastodon/pull/12251)) +- Add `discoverable` to accounts in REST API ([trwnh](https://github.com/mastodon/mastodon/pull/12508)) +- Add admin setting to disable default follows ([ArisuOngaku](https://github.com/mastodon/mastodon/pull/12566)) +- Add support for LDAP and PAM in the OAuth password grant strategy ([ntl-purism](https://github.com/mastodon/mastodon/pull/12390), [Gargron](https://github.com/mastodon/mastodon/pull/12743)) +- Allow support for `Accept`/`Reject` activities with a non-embedded object ([puckipedia](https://github.com/mastodon/mastodon/pull/12199)) +- Add "Show thread" button to public profiles ([Sasha-Sorokin](https://github.com/mastodon/mastodon/pull/13000)) ### Changed -- Change `last_status_at` to be a date, not datetime in REST API ([ThibG](https://github.com/tootsuite/mastodon/pull/12966)) -- Change followers page to relationships page in admin UI ([Gargron](https://github.com/tootsuite/mastodon/pull/12927), [Gargron](https://github.com/tootsuite/mastodon/pull/12934)) -- Change reported media attachments to always be hidden in admin UI ([Gargron](https://github.com/tootsuite/mastodon/pull/12879), [ThibG](https://github.com/tootsuite/mastodon/pull/12907)) -- Change string from "Disable" to "Disable login" in admin UI ([nileshkumar](https://github.com/tootsuite/mastodon/pull/12201)) -- Change report page structure in admin UI ([Sasha-Sorokin](https://github.com/tootsuite/mastodon/pull/12615)) -- Change swipe sensitivity to be lower on small screens in web UI ([umonaca](https://github.com/tootsuite/mastodon/pull/12168)) -- Change audio/video playback to stop playback when out of view in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/12486)) -- Change media description label based on upload type in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/12270)) -- Change large numbers to render without decimal units in web UI ([noellabo](https://github.com/tootsuite/mastodon/pull/12706)) -- Change "Add a choice" button to be disabled rather than hidden when poll limit reached in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/12319), [hinaloe](https://github.com/tootsuite/mastodon/pull/12544)) -- Change `tootctl statuses remove` to keep statuses favourited or bookmarked by local users ([ThibG](https://github.com/tootsuite/mastodon/pull/11267), [Gomasy](https://github.com/tootsuite/mastodon/pull/12818)) -- Change domain block behavior to update user records (fast) before deleting data (slower) ([ThibG](https://github.com/tootsuite/mastodon/pull/12247)) -- Change behaviour to strip audio metadata on uploads ([hugogameiro](https://github.com/tootsuite/mastodon/pull/12171)) -- Change accepted length of remote media descriptions from 420 to 1,500 characters ([ThibG](https://github.com/tootsuite/mastodon/pull/12262)) -- Change preferences pages structure ([Sasha-Sorokin](https://github.com/tootsuite/mastodon/pull/12497), [mayaeh](https://github.com/tootsuite/mastodon/pull/12517), [Sasha-Sorokin](https://github.com/tootsuite/mastodon/pull/12801), [Sasha-Sorokin](https://github.com/tootsuite/mastodon/pull/12797), [Sasha-Sorokin](https://github.com/tootsuite/mastodon/pull/12799), [Sasha-Sorokin](https://github.com/tootsuite/mastodon/pull/12793)) -- Change format of titles in RSS ([devkral](https://github.com/tootsuite/mastodon/pull/8596)) -- Change favourite icon animation from spring-based motion to CSS animation in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/12175)) -- Change minimum required Node.js version to 10, and default to 12 ([Shleeble](https://github.com/tootsuite/mastodon/pull/12791), [mkody](https://github.com/tootsuite/mastodon/pull/12906), [Shleeble](https://github.com/tootsuite/mastodon/pull/12703)) -- Change spam check to exempt server staff ([ThibG](https://github.com/tootsuite/mastodon/pull/12874)) -- Change to fallback to to `Create` audience when `object` has no defined audience ([ThibG](https://github.com/tootsuite/mastodon/pull/12249)) -- Change Twemoji library to 12.1.3 in web UI ([koyuawsmbrtn](https://github.com/tootsuite/mastodon/pull/12342)) -- Change blocked users to be hidden from following/followers lists ([ThibG](https://github.com/tootsuite/mastodon/pull/12733)) -- Change signature verification to ignore signatures with invalid host ([Gargron](https://github.com/tootsuite/mastodon/pull/13033)) +- Change `last_status_at` to be a date, not datetime in REST API ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12966)) +- Change followers page to relationships page in admin UI ([Gargron](https://github.com/mastodon/mastodon/pull/12927), [Gargron](https://github.com/mastodon/mastodon/pull/12934)) +- Change reported media attachments to always be hidden in admin UI ([Gargron](https://github.com/mastodon/mastodon/pull/12879), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/12907)) +- Change string from "Disable" to "Disable login" in admin UI ([nileshkumar](https://github.com/mastodon/mastodon/pull/12201)) +- Change report page structure in admin UI ([Sasha-Sorokin](https://github.com/mastodon/mastodon/pull/12615)) +- Change swipe sensitivity to be lower on small screens in web UI ([umonaca](https://github.com/mastodon/mastodon/pull/12168)) +- Change audio/video playback to stop playback when out of view in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/12486)) +- Change media description label based on upload type in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12270)) +- Change large numbers to render without decimal units in web UI ([noellabo](https://github.com/mastodon/mastodon/pull/12706)) +- Change "Add a choice" button to be disabled rather than hidden when poll limit reached in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12319), [hinaloe](https://github.com/mastodon/mastodon/pull/12544)) +- Change `tootctl statuses remove` to keep statuses favourited or bookmarked by local users ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11267), [Gomasy](https://github.com/mastodon/mastodon/pull/12818)) +- Change domain block behavior to update user records (fast) before deleting data (slower) ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12247)) +- Change behaviour to strip audio metadata on uploads ([hugogameiro](https://github.com/mastodon/mastodon/pull/12171)) +- Change accepted length of remote media descriptions from 420 to 1,500 characters ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12262)) +- Change preferences pages structure ([Sasha-Sorokin](https://github.com/mastodon/mastodon/pull/12497), [mayaeh](https://github.com/mastodon/mastodon/pull/12517), [Sasha-Sorokin](https://github.com/mastodon/mastodon/pull/12801), [Sasha-Sorokin](https://github.com/mastodon/mastodon/pull/12797), [Sasha-Sorokin](https://github.com/mastodon/mastodon/pull/12799), [Sasha-Sorokin](https://github.com/mastodon/mastodon/pull/12793)) +- Change format of titles in RSS ([devkral](https://github.com/mastodon/mastodon/pull/8596)) +- Change favourite icon animation from spring-based motion to CSS animation in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12175)) +- Change minimum required Node.js version to 10, and default to 12 ([Shleeble](https://github.com/mastodon/mastodon/pull/12791), [mkody](https://github.com/mastodon/mastodon/pull/12906), [Shleeble](https://github.com/mastodon/mastodon/pull/12703)) +- Change spam check to exempt server staff ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12874)) +- Change to fallback to to `Create` audience when `object` has no defined audience ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12249)) +- Change Twemoji library to 12.1.3 in web UI ([koyuawsmbrtn](https://github.com/mastodon/mastodon/pull/12342)) +- Change blocked users to be hidden from following/followers lists ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12733)) +- Change signature verification to ignore signatures with invalid host ([Gargron](https://github.com/mastodon/mastodon/pull/13033)) ### Removed -- Remove unused dependencies ([ykzts](https://github.com/tootsuite/mastodon/pull/12861), [mayaeh](https://github.com/tootsuite/mastodon/pull/12826), [ThibG](https://github.com/tootsuite/mastodon/pull/12822), [ykzts](https://github.com/tootsuite/mastodon/pull/12533)) +- Remove unused dependencies ([ykzts](https://github.com/mastodon/mastodon/pull/12861), [mayaeh](https://github.com/mastodon/mastodon/pull/12826), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/12822), [ykzts](https://github.com/mastodon/mastodon/pull/12533)) ### Fixed -- Fix some translatable strings being used wrongly ([Sasha-Sorokin](https://github.com/tootsuite/mastodon/pull/12569), [Sasha-Sorokin](https://github.com/tootsuite/mastodon/pull/12589), [Sasha-Sorokin](https://github.com/tootsuite/mastodon/pull/12502), [mayaeh](https://github.com/tootsuite/mastodon/pull/12231)) -- Fix headline of public timeline page when set to local-only ([ykzts](https://github.com/tootsuite/mastodon/pull/12224)) -- Fix space between tabs not being spread evenly in web UI ([Sasha-Sorokin](https://github.com/tootsuite/mastodon/pull/12944), [Sasha-Sorokin](https://github.com/tootsuite/mastodon/pull/12961), [Sasha-Sorokin](https://github.com/tootsuite/mastodon/pull/12446)) -- Fix interactive delays in database migrations with no TTY ([Gargron](https://github.com/tootsuite/mastodon/pull/12969)) -- Fix status overflowing in report dialog in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/12959)) -- Fix unlocalized dropdown button title in web UI ([Sasha-Sorokin](https://github.com/tootsuite/mastodon/pull/12947)) -- Fix media attachments without file being uploadable ([Gargron](https://github.com/tootsuite/mastodon/pull/12562)) -- Fix unfollow confirmations in profile directory in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/12922)) -- Fix duplicate `description` meta tag on accounts public pages ([ThibG](https://github.com/tootsuite/mastodon/pull/12923)) -- Fix slow query of federated timeline ([notozeki](https://github.com/tootsuite/mastodon/pull/12886)) -- Fix not all of account's active IPs showing up in admin UI ([Gargron](https://github.com/tootsuite/mastodon/pull/12909), [Gargron](https://github.com/tootsuite/mastodon/pull/12943)) -- Fix search by IP not using alternative browser sessions in admin UI ([Gargron](https://github.com/tootsuite/mastodon/pull/12904)) -- Fix “X new items” not showing up for slow mode on empty timelines in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/12875)) -- Fix OEmbed endpoint being inaccessible in secure mode ([Gargron](https://github.com/tootsuite/mastodon/pull/12864)) -- Fix proofs API being inaccessible in secure mode ([Gargron](https://github.com/tootsuite/mastodon/pull/12495)) -- Fix Ruby 2.7 incompatibilities ([ThibG](https://github.com/tootsuite/mastodon/pull/12831), [ThibG](https://github.com/tootsuite/mastodon/pull/12824), [Shleeble](https://github.com/tootsuite/mastodon/pull/12759), [zunda](https://github.com/tootsuite/mastodon/pull/12769)) -- Fix invalid poll votes being accepted in REST API ([ThibG](https://github.com/tootsuite/mastodon/pull/12601)) -- Fix old migrations failing because of strong migrations update ([ThibG](https://github.com/tootsuite/mastodon/pull/12787), [ThibG](https://github.com/tootsuite/mastodon/pull/12692)) -- Fix reuse of detailed status components in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/12792)) -- Fix base64-encoded file uploads not being possible in REST API ([Gargron](https://github.com/tootsuite/mastodon/pull/12748), [Gargron](https://github.com/tootsuite/mastodon/pull/12857)) -- Fix error due to missing authentication call in filters controller ([Gargron](https://github.com/tootsuite/mastodon/pull/12746)) -- Fix uncaught unknown format error in host meta controller ([Gargron](https://github.com/tootsuite/mastodon/pull/12747)) -- Fix URL search not returning private toots user has access to ([ThibG](https://github.com/tootsuite/mastodon/pull/12742), [ThibG](https://github.com/tootsuite/mastodon/pull/12336)) -- Fix cache digesting log noise on status embeds ([Gargron](https://github.com/tootsuite/mastodon/pull/12750)) -- Fix slowness due to layout thrashing when reloading a large set of statuses in web UI ([panarom](https://github.com/tootsuite/mastodon/pull/12661), [panarom](https://github.com/tootsuite/mastodon/pull/12744), [Gargron](https://github.com/tootsuite/mastodon/pull/12712)) -- Fix error when fetching followers/following from REST API when user has network hidden ([Gargron](https://github.com/tootsuite/mastodon/pull/12716)) -- Fix IDN mentions not being processed, IDN domains not being rendered ([Gargron](https://github.com/tootsuite/mastodon/pull/12715), [Gargron](https://github.com/tootsuite/mastodon/pull/13035), [Gargron](https://github.com/tootsuite/mastodon/pull/13030)) -- Fix error when searching for empty phrase ([Gargron](https://github.com/tootsuite/mastodon/pull/12711)) -- Fix backups stopping due to read timeouts ([chr-1x](https://github.com/tootsuite/mastodon/pull/12281)) -- Fix batch actions on non-pending tags in admin UI ([ThibG](https://github.com/tootsuite/mastodon/pull/12537)) -- Fix sample `SAML_ACS_URL`, `SAML_ISSUER` ([orlea](https://github.com/tootsuite/mastodon/pull/12669)) -- Fix manual scrolling issue on Firefox/Windows in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/12648)) -- Fix archive takeout failing if total dump size exceeds 2GB ([scd31](https://github.com/tootsuite/mastodon/pull/12602), [Gargron](https://github.com/tootsuite/mastodon/pull/12653)) -- Fix custom emoji category creation silently erroring out on duplicate category ([ThibG](https://github.com/tootsuite/mastodon/pull/12647)) -- Fix link crawler not specifying preferred content type ([ThibG](https://github.com/tootsuite/mastodon/pull/12646)) -- Fix featured hashtag setting page erroring out instead of rejecting invalid tags ([ThibG](https://github.com/tootsuite/mastodon/pull/12436)) -- Fix tooltip messages of single/multiple-choice polls switcher being reversed in web UI ([acid-chicken](https://github.com/tootsuite/mastodon/pull/12616)) -- Fix typo in help text of `tootctl statuses remove` ([trwnh](https://github.com/tootsuite/mastodon/pull/12603)) -- Fix generic HTTP 500 error on duplicate records ([Gargron](https://github.com/tootsuite/mastodon/pull/12563)) -- Fix old migration failing with new status default scope ([ThibG](https://github.com/tootsuite/mastodon/pull/12493)) -- Fix errors when using search API with no query ([Gargron](https://github.com/tootsuite/mastodon/pull/12541), [trwnh](https://github.com/tootsuite/mastodon/pull/12549)) -- Fix poll options not being selectable via keyboard in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/12538)) -- Fix conversations not having an unread indicator in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/12506)) -- Fix lost focus when modals open/close in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/12437)) -- Fix pending upload count not being decremented on error in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/12499)) -- Fix empty poll options not being removed on remote poll update ([ThibG](https://github.com/tootsuite/mastodon/pull/12484)) -- Fix OCR with delete & redraft in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/12465)) -- Fix blur behind closed registration message ([ThibG](https://github.com/tootsuite/mastodon/pull/12442)) -- Fix OEmbed discovery not handling different URL variants in query ([Gargron](https://github.com/tootsuite/mastodon/pull/12439)) -- Fix link crawler crashing on `` tags without `href` ([ThibG](https://github.com/tootsuite/mastodon/pull/12159)) -- Fix whitelisted subdomains being ignored in whitelist mode ([noiob](https://github.com/tootsuite/mastodon/pull/12435)) -- Fix broken audit log in whitelist mode in admin UI ([ThibG](https://github.com/tootsuite/mastodon/pull/12303)) -- Fix unread indicator not honoring "Only media" option in local and federated timelines in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/12330)) -- Fix error when rebuilding home feeds ([dariusk](https://github.com/tootsuite/mastodon/pull/12324)) -- Fix relationship caches being broken as result of a follow request ([ThibG](https://github.com/tootsuite/mastodon/pull/12299)) -- Fix more items than the limit being uploadable in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/12300)) -- Fix various issues with account migration ([ThibG](https://github.com/tootsuite/mastodon/pull/12301)) -- Fix filtered out items being counted as pending items in slow mode in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/12266)) -- Fix notification filters not applying to poll options ([ThibG](https://github.com/tootsuite/mastodon/pull/12269)) -- Fix notification message for user's own poll saying it's a poll they voted on in web UI ([ykzts](https://github.com/tootsuite/mastodon/pull/12219)) -- Fix polls with an expiration not showing up as expired in web UI ([noellabo](https://github.com/tootsuite/mastodon/pull/12222)) -- Fix volume slider having an offset between cursor and slider in Chromium in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/12158)) -- Fix Vagrant image not accepting connections ([shrft](https://github.com/tootsuite/mastodon/pull/12180)) -- Fix batch actions being hidden on small screens in admin UI ([ThibG](https://github.com/tootsuite/mastodon/pull/12183)) -- Fix incoming federation not working in whitelist mode ([ThibG](https://github.com/tootsuite/mastodon/pull/12185)) -- Fix error when passing empty `source` param to `PUT /api/v1/accounts/update_credentials` ([jglauche](https://github.com/tootsuite/mastodon/pull/12259)) -- Fix HTTP-based streaming API being cacheable by proxies ([BenLubar](https://github.com/tootsuite/mastodon/pull/12945)) -- Fix users being able to register while `tootctl self-destruct` is in progress ([Kjwon15](https://github.com/tootsuite/mastodon/pull/12877)) -- Fix microformats detection in link crawler not ignoring `h-card` links ([nightpool](https://github.com/tootsuite/mastodon/pull/12189)) -- Fix outline on full-screen video in web UI ([hinaloe](https://github.com/tootsuite/mastodon/pull/12176)) -- Fix TLD domain blocks not being editable ([ThibG](https://github.com/tootsuite/mastodon/pull/12805)) -- Fix Nanobox deploy hooks ([danhunsaker](https://github.com/tootsuite/mastodon/pull/12663)) -- Fix needlessly complicated SQL query when performing account search amongst followings ([ThibG](https://github.com/tootsuite/mastodon/pull/12302)) -- Fix favourites count not updating when unfavouriting in web UI ([NimaBoscarino](https://github.com/tootsuite/mastodon/pull/12140)) -- Fix occasional crash on scroll in Chromium in web UI ([hinaloe](https://github.com/tootsuite/mastodon/pull/12274)) -- Fix intersection observer not working in single-column mode web UI ([panarom](https://github.com/tootsuite/mastodon/pull/12735)) -- Fix voting issue with remote polls that contain trailing spaces ([ThibG](https://github.com/tootsuite/mastodon/pull/12515)) -- Fix dynamic elements not working in pgHero due to CSP rules ([ykzts](https://github.com/tootsuite/mastodon/pull/12489)) -- Fix overly verbose backtraces when delivering ActivityPub payloads ([zunda](https://github.com/tootsuite/mastodon/pull/12798)) -- Fix rendering `` without `href` when scheme unsupported ([Gargron](https://github.com/tootsuite/mastodon/pull/13040)) -- Fix unfiltered params error when generating ActivityPub tag pagination ([Gargron](https://github.com/tootsuite/mastodon/pull/13049)) -- Fix malformed HTML causing uncaught error ([Gargron](https://github.com/tootsuite/mastodon/pull/13042)) -- Fix native share button not being displayed for unlisted toots ([ThibG](https://github.com/tootsuite/mastodon/pull/13045)) -- Fix remote convertible media attachments (e.g. GIFs) not being saved ([Gargron](https://github.com/tootsuite/mastodon/pull/13032)) -- Fix account query not using faster index ([abcang](https://github.com/tootsuite/mastodon/pull/13016)) -- Fix error when sending moderation notification ([renatolond](https://github.com/tootsuite/mastodon/pull/13014)) +- Fix some translatable strings being used wrongly ([Sasha-Sorokin](https://github.com/mastodon/mastodon/pull/12569), [Sasha-Sorokin](https://github.com/mastodon/mastodon/pull/12589), [Sasha-Sorokin](https://github.com/mastodon/mastodon/pull/12502), [mayaeh](https://github.com/mastodon/mastodon/pull/12231)) +- Fix headline of public timeline page when set to local-only ([ykzts](https://github.com/mastodon/mastodon/pull/12224)) +- Fix space between tabs not being spread evenly in web UI ([Sasha-Sorokin](https://github.com/mastodon/mastodon/pull/12944), [Sasha-Sorokin](https://github.com/mastodon/mastodon/pull/12961), [Sasha-Sorokin](https://github.com/mastodon/mastodon/pull/12446)) +- Fix interactive delays in database migrations with no TTY ([Gargron](https://github.com/mastodon/mastodon/pull/12969)) +- Fix status overflowing in report dialog in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12959)) +- Fix unlocalized dropdown button title in web UI ([Sasha-Sorokin](https://github.com/mastodon/mastodon/pull/12947)) +- Fix media attachments without file being uploadable ([Gargron](https://github.com/mastodon/mastodon/pull/12562)) +- Fix unfollow confirmations in profile directory in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12922)) +- Fix duplicate `description` meta tag on accounts public pages ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12923)) +- Fix slow query of federated timeline ([notozeki](https://github.com/mastodon/mastodon/pull/12886)) +- Fix not all of account's active IPs showing up in admin UI ([Gargron](https://github.com/mastodon/mastodon/pull/12909), [Gargron](https://github.com/mastodon/mastodon/pull/12943)) +- Fix search by IP not using alternative browser sessions in admin UI ([Gargron](https://github.com/mastodon/mastodon/pull/12904)) +- Fix “X new items” not showing up for slow mode on empty timelines in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12875)) +- Fix OEmbed endpoint being inaccessible in secure mode ([Gargron](https://github.com/mastodon/mastodon/pull/12864)) +- Fix proofs API being inaccessible in secure mode ([Gargron](https://github.com/mastodon/mastodon/pull/12495)) +- Fix Ruby 2.7 incompatibilities ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12831), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/12824), [Shleeble](https://github.com/mastodon/mastodon/pull/12759), [zunda](https://github.com/mastodon/mastodon/pull/12769)) +- Fix invalid poll votes being accepted in REST API ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12601)) +- Fix old migrations failing because of strong migrations update ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12787), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/12692)) +- Fix reuse of detailed status components in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12792)) +- Fix base64-encoded file uploads not being possible in REST API ([Gargron](https://github.com/mastodon/mastodon/pull/12748), [Gargron](https://github.com/mastodon/mastodon/pull/12857)) +- Fix error due to missing authentication call in filters controller ([Gargron](https://github.com/mastodon/mastodon/pull/12746)) +- Fix uncaught unknown format error in host meta controller ([Gargron](https://github.com/mastodon/mastodon/pull/12747)) +- Fix URL search not returning private toots user has access to ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12742), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/12336)) +- Fix cache digesting log noise on status embeds ([Gargron](https://github.com/mastodon/mastodon/pull/12750)) +- Fix slowness due to layout thrashing when reloading a large set of statuses in web UI ([panarom](https://github.com/mastodon/mastodon/pull/12661), [panarom](https://github.com/mastodon/mastodon/pull/12744), [Gargron](https://github.com/mastodon/mastodon/pull/12712)) +- Fix error when fetching followers/following from REST API when user has network hidden ([Gargron](https://github.com/mastodon/mastodon/pull/12716)) +- Fix IDN mentions not being processed, IDN domains not being rendered ([Gargron](https://github.com/mastodon/mastodon/pull/12715), [Gargron](https://github.com/mastodon/mastodon/pull/13035), [Gargron](https://github.com/mastodon/mastodon/pull/13030)) +- Fix error when searching for empty phrase ([Gargron](https://github.com/mastodon/mastodon/pull/12711)) +- Fix backups stopping due to read timeouts ([chr-1x](https://github.com/mastodon/mastodon/pull/12281)) +- Fix batch actions on non-pending tags in admin UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12537)) +- Fix sample `SAML_ACS_URL`, `SAML_ISSUER` ([orlea](https://github.com/mastodon/mastodon/pull/12669)) +- Fix manual scrolling issue on Firefox/Windows in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12648)) +- Fix archive takeout failing if total dump size exceeds 2GB ([scd31](https://github.com/mastodon/mastodon/pull/12602), [Gargron](https://github.com/mastodon/mastodon/pull/12653)) +- Fix custom emoji category creation silently erroring out on duplicate category ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12647)) +- Fix link crawler not specifying preferred content type ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12646)) +- Fix featured hashtag setting page erroring out instead of rejecting invalid tags ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12436)) +- Fix tooltip messages of single/multiple-choice polls switcher being reversed in web UI ([acid-chicken](https://github.com/mastodon/mastodon/pull/12616)) +- Fix typo in help text of `tootctl statuses remove` ([trwnh](https://github.com/mastodon/mastodon/pull/12603)) +- Fix generic HTTP 500 error on duplicate records ([Gargron](https://github.com/mastodon/mastodon/pull/12563)) +- Fix old migration failing with new status default scope ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12493)) +- Fix errors when using search API with no query ([Gargron](https://github.com/mastodon/mastodon/pull/12541), [trwnh](https://github.com/mastodon/mastodon/pull/12549)) +- Fix poll options not being selectable via keyboard in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12538)) +- Fix conversations not having an unread indicator in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/12506)) +- Fix lost focus when modals open/close in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12437)) +- Fix pending upload count not being decremented on error in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12499)) +- Fix empty poll options not being removed on remote poll update ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12484)) +- Fix OCR with delete & redraft in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12465)) +- Fix blur behind closed registration message ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12442)) +- Fix OEmbed discovery not handling different URL variants in query ([Gargron](https://github.com/mastodon/mastodon/pull/12439)) +- Fix link crawler crashing on `` tags without `href` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12159)) +- Fix whitelisted subdomains being ignored in whitelist mode ([noiob](https://github.com/mastodon/mastodon/pull/12435)) +- Fix broken audit log in whitelist mode in admin UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12303)) +- Fix unread indicator not honoring "Only media" option in local and federated timelines in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12330)) +- Fix error when rebuilding home feeds ([dariusk](https://github.com/mastodon/mastodon/pull/12324)) +- Fix relationship caches being broken as result of a follow request ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12299)) +- Fix more items than the limit being uploadable in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12300)) +- Fix various issues with account migration ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12301)) +- Fix filtered out items being counted as pending items in slow mode in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12266)) +- Fix notification filters not applying to poll options ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12269)) +- Fix notification message for user's own poll saying it's a poll they voted on in web UI ([ykzts](https://github.com/mastodon/mastodon/pull/12219)) +- Fix polls with an expiration not showing up as expired in web UI ([noellabo](https://github.com/mastodon/mastodon/pull/12222)) +- Fix volume slider having an offset between cursor and slider in Chromium in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12158)) +- Fix Vagrant image not accepting connections ([shrft](https://github.com/mastodon/mastodon/pull/12180)) +- Fix batch actions being hidden on small screens in admin UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12183)) +- Fix incoming federation not working in whitelist mode ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12185)) +- Fix error when passing empty `source` param to `PUT /api/v1/accounts/update_credentials` ([jglauche](https://github.com/mastodon/mastodon/pull/12259)) +- Fix HTTP-based streaming API being cacheable by proxies ([BenLubar](https://github.com/mastodon/mastodon/pull/12945)) +- Fix users being able to register while `tootctl self-destruct` is in progress ([Kjwon15](https://github.com/mastodon/mastodon/pull/12877)) +- Fix microformats detection in link crawler not ignoring `h-card` links ([nightpool](https://github.com/mastodon/mastodon/pull/12189)) +- Fix outline on full-screen video in web UI ([hinaloe](https://github.com/mastodon/mastodon/pull/12176)) +- Fix TLD domain blocks not being editable ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12805)) +- Fix Nanobox deploy hooks ([danhunsaker](https://github.com/mastodon/mastodon/pull/12663)) +- Fix needlessly complicated SQL query when performing account search amongst followings ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12302)) +- Fix favourites count not updating when unfavouriting in web UI ([NimaBoscarino](https://github.com/mastodon/mastodon/pull/12140)) +- Fix occasional crash on scroll in Chromium in web UI ([hinaloe](https://github.com/mastodon/mastodon/pull/12274)) +- Fix intersection observer not working in single-column mode web UI ([panarom](https://github.com/mastodon/mastodon/pull/12735)) +- Fix voting issue with remote polls that contain trailing spaces ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12515)) +- Fix dynamic elements not working in pgHero due to CSP rules ([ykzts](https://github.com/mastodon/mastodon/pull/12489)) +- Fix overly verbose backtraces when delivering ActivityPub payloads ([zunda](https://github.com/mastodon/mastodon/pull/12798)) +- Fix rendering `` without `href` when scheme unsupported ([Gargron](https://github.com/mastodon/mastodon/pull/13040)) +- Fix unfiltered params error when generating ActivityPub tag pagination ([Gargron](https://github.com/mastodon/mastodon/pull/13049)) +- Fix malformed HTML causing uncaught error ([Gargron](https://github.com/mastodon/mastodon/pull/13042)) +- Fix native share button not being displayed for unlisted toots ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13045)) +- Fix remote convertible media attachments (e.g. GIFs) not being saved ([Gargron](https://github.com/mastodon/mastodon/pull/13032)) +- Fix account query not using faster index ([abcang](https://github.com/mastodon/mastodon/pull/13016)) +- Fix error when sending moderation notification ([renatolond](https://github.com/mastodon/mastodon/pull/13014)) ### Security -- Fix OEmbed leaking information about existence of non-public statuses ([Gargron](https://github.com/tootsuite/mastodon/pull/12930)) -- Fix password change/reset not immediately invalidating other sessions ([Gargron](https://github.com/tootsuite/mastodon/pull/12928)) -- Fix settings pages being cacheable by the browser ([Gargron](https://github.com/tootsuite/mastodon/pull/12714)) +- Fix OEmbed leaking information about existence of non-public statuses ([Gargron](https://github.com/mastodon/mastodon/pull/12930)) +- Fix password change/reset not immediately invalidating other sessions ([Gargron](https://github.com/mastodon/mastodon/pull/12928)) +- Fix settings pages being cacheable by the browser ([Gargron](https://github.com/mastodon/mastodon/pull/12714)) ## [3.0.1] - 2019-10-10 ### Added -- Add `tootctl media usage` command ([Gargron](https://github.com/tootsuite/mastodon/pull/12115)) -- Add admin setting to auto-approve trending hashtags ([Gargron](https://github.com/tootsuite/mastodon/pull/12122), [Gargron](https://github.com/tootsuite/mastodon/pull/12130)) +- Add `tootctl media usage` command ([Gargron](https://github.com/mastodon/mastodon/pull/12115)) +- Add admin setting to auto-approve trending hashtags ([Gargron](https://github.com/mastodon/mastodon/pull/12122), [Gargron](https://github.com/mastodon/mastodon/pull/12130)) ### Changed -- Change `tootctl media refresh` to skip already downloaded attachments ([Gargron](https://github.com/tootsuite/mastodon/pull/12118)) +- Change `tootctl media refresh` to skip already downloaded attachments ([Gargron](https://github.com/mastodon/mastodon/pull/12118)) ### Removed -- Remove auto-silence behaviour from spam check ([Gargron](https://github.com/tootsuite/mastodon/pull/12117)) -- Remove HTML `lang` attribute from individual statuses in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/12124)) -- Remove fallback to long description on sidebar and meta description ([Gargron](https://github.com/tootsuite/mastodon/pull/12119)) +- Remove auto-silence behaviour from spam check ([Gargron](https://github.com/mastodon/mastodon/pull/12117)) +- Remove HTML `lang` attribute from individual statuses in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/12124)) +- Remove fallback to long description on sidebar and meta description ([Gargron](https://github.com/mastodon/mastodon/pull/12119)) ### Fixed -- Fix preloaded JSON-LD context for identity not being used ([Gargron](https://github.com/tootsuite/mastodon/pull/12138)) -- Fix media editing modal changing dimensions once the image loads ([Gargron](https://github.com/tootsuite/mastodon/pull/12131)) -- Fix not showing whether a custom emoji has a local counterpart in admin UI ([Gargron](https://github.com/tootsuite/mastodon/pull/12135)) -- Fix attachment not being re-downloaded even if file is not stored ([Gargron](https://github.com/tootsuite/mastodon/pull/12125)) -- Fix old migration trying to use new column due to default status scope ([Gargron](https://github.com/tootsuite/mastodon/pull/12095)) -- Fix column back button missing for not found accounts ([trwnh](https://github.com/tootsuite/mastodon/pull/12094)) -- Fix issues with tootctl's parallelization and progress reporting ([Gargron](https://github.com/tootsuite/mastodon/pull/12093), [Gargron](https://github.com/tootsuite/mastodon/pull/12097)) -- Fix existing user records with now-renamed `pt` locale ([Gargron](https://github.com/tootsuite/mastodon/pull/12092)) -- Fix hashtag timeline REST API accepting too many hashtags ([Gargron](https://github.com/tootsuite/mastodon/pull/12091)) -- Fix `GET /api/v1/instance` REST APIs being unavailable in secure mode ([Gargron](https://github.com/tootsuite/mastodon/pull/12089)) -- Fix performance of home feed regeneration and merging ([Gargron](https://github.com/tootsuite/mastodon/pull/12084)) -- Fix ffmpeg performance issues due to stdout buffer overflow ([hugogameiro](https://github.com/tootsuite/mastodon/pull/12088)) -- Fix S3 adapter retrying failing uploads with exponential backoff ([Gargron](https://github.com/tootsuite/mastodon/pull/12085)) -- Fix `tootctl accounts cull` advertising unused option flag ([Kjwon15](https://github.com/tootsuite/mastodon/pull/12074)) +- Fix preloaded JSON-LD context for identity not being used ([Gargron](https://github.com/mastodon/mastodon/pull/12138)) +- Fix media editing modal changing dimensions once the image loads ([Gargron](https://github.com/mastodon/mastodon/pull/12131)) +- Fix not showing whether a custom emoji has a local counterpart in admin UI ([Gargron](https://github.com/mastodon/mastodon/pull/12135)) +- Fix attachment not being re-downloaded even if file is not stored ([Gargron](https://github.com/mastodon/mastodon/pull/12125)) +- Fix old migration trying to use new column due to default status scope ([Gargron](https://github.com/mastodon/mastodon/pull/12095)) +- Fix column back button missing for not found accounts ([trwnh](https://github.com/mastodon/mastodon/pull/12094)) +- Fix issues with tootctl's parallelization and progress reporting ([Gargron](https://github.com/mastodon/mastodon/pull/12093), [Gargron](https://github.com/mastodon/mastodon/pull/12097)) +- Fix existing user records with now-renamed `pt` locale ([Gargron](https://github.com/mastodon/mastodon/pull/12092)) +- Fix hashtag timeline REST API accepting too many hashtags ([Gargron](https://github.com/mastodon/mastodon/pull/12091)) +- Fix `GET /api/v1/instance` REST APIs being unavailable in secure mode ([Gargron](https://github.com/mastodon/mastodon/pull/12089)) +- Fix performance of home feed regeneration and merging ([Gargron](https://github.com/mastodon/mastodon/pull/12084)) +- Fix ffmpeg performance issues due to stdout buffer overflow ([hugogameiro](https://github.com/mastodon/mastodon/pull/12088)) +- Fix S3 adapter retrying failing uploads with exponential backoff ([Gargron](https://github.com/mastodon/mastodon/pull/12085)) +- Fix `tootctl accounts cull` advertising unused option flag ([Kjwon15](https://github.com/mastodon/mastodon/pull/12074)) ## [3.0.0] - 2019-10-03 ### Added -- Add "not available" label to unloaded media attachments in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/11715), [Gargron](https://github.com/tootsuite/mastodon/pull/11745)) -- **Add profile directory to web UI** ([Gargron](https://github.com/tootsuite/mastodon/pull/11688), [mayaeh](https://github.com/tootsuite/mastodon/pull/11872)) +- Add "not available" label to unloaded media attachments in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/11715), [Gargron](https://github.com/mastodon/mastodon/pull/11745)) +- **Add profile directory to web UI** ([Gargron](https://github.com/mastodon/mastodon/pull/11688), [mayaeh](https://github.com/mastodon/mastodon/pull/11872)) - Add profile directory opt-in federation - Add profile directory REST API -- Add special alert for throttled requests in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/11677)) -- Add confirmation modal when logging out from the web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/11671)) -- **Add audio player in web UI** ([Gargron](https://github.com/tootsuite/mastodon/pull/11644), [Gargron](https://github.com/tootsuite/mastodon/pull/11652), [Gargron](https://github.com/tootsuite/mastodon/pull/11654), [ThibG](https://github.com/tootsuite/mastodon/pull/11629), [Gargron](https://github.com/tootsuite/mastodon/pull/12056)) -- **Add autosuggestions for hashtags in web UI** ([Gargron](https://github.com/tootsuite/mastodon/pull/11422), [ThibG](https://github.com/tootsuite/mastodon/pull/11632), [Gargron](https://github.com/tootsuite/mastodon/pull/11764), [Gargron](https://github.com/tootsuite/mastodon/pull/11588), [Gargron](https://github.com/tootsuite/mastodon/pull/11442)) -- **Add media editing modal with OCR tool in web UI** ([Gargron](https://github.com/tootsuite/mastodon/pull/11563), [Gargron](https://github.com/tootsuite/mastodon/pull/11566), [ThibG](https://github.com/tootsuite/mastodon/pull/11575), [ThibG](https://github.com/tootsuite/mastodon/pull/11576), [Gargron](https://github.com/tootsuite/mastodon/pull/11577), [Gargron](https://github.com/tootsuite/mastodon/pull/11573), [Gargron](https://github.com/tootsuite/mastodon/pull/11571)) -- Add indicator of unread notifications to window title when web UI is out of focus ([Gargron](https://github.com/tootsuite/mastodon/pull/11560), [Gargron](https://github.com/tootsuite/mastodon/pull/11572)) -- Add indicator for which options you voted for in a poll in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/11195)) -- **Add search results pagination to web UI** ([Gargron](https://github.com/tootsuite/mastodon/pull/11409), [ThibG](https://github.com/tootsuite/mastodon/pull/11447)) -- **Add option to disable real-time updates in web UI ("slow mode")** ([Gargron](https://github.com/tootsuite/mastodon/pull/9984), [ykzts](https://github.com/tootsuite/mastodon/pull/11880), [ThibG](https://github.com/tootsuite/mastodon/pull/11883), [Gargron](https://github.com/tootsuite/mastodon/pull/11898), [ThibG](https://github.com/tootsuite/mastodon/pull/11859)) -- Add option to disable blurhash previews in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/11188)) -- Add native smooth scrolling when supported in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/11207)) -- Add scrolling to the search bar on focus in web UI ([Kjwon15](https://github.com/tootsuite/mastodon/pull/12032)) -- Add refresh button to list of rebloggers/favouriters in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/12031)) -- Add error description and button to copy stack trace to web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/12033)) -- Add search and sort functions to hashtag admin UI ([mayaeh](https://github.com/tootsuite/mastodon/pull/11829), [Gargron](https://github.com/tootsuite/mastodon/pull/11897), [mayaeh](https://github.com/tootsuite/mastodon/pull/11875)) -- Add setting for default search engine indexing in admin UI ([brortao](https://github.com/tootsuite/mastodon/pull/11804)) -- Add account bio to account view in admin UI ([ThibG](https://github.com/tootsuite/mastodon/pull/11473)) -- **Add option to include reported statuses in warning e-mail from admin UI** ([Gargron](https://github.com/tootsuite/mastodon/pull/11639), [Gargron](https://github.com/tootsuite/mastodon/pull/11812), [Gargron](https://github.com/tootsuite/mastodon/pull/11741), [Gargron](https://github.com/tootsuite/mastodon/pull/11698), [mayaeh](https://github.com/tootsuite/mastodon/pull/11765)) -- Add number of pending accounts and pending hashtags to dashboard in admin UI ([Gargron](https://github.com/tootsuite/mastodon/pull/11514)) -- **Add account migration UI** ([Gargron](https://github.com/tootsuite/mastodon/pull/11846), [noellabo](https://github.com/tootsuite/mastodon/pull/11905), [noellabo](https://github.com/tootsuite/mastodon/pull/11907), [noellabo](https://github.com/tootsuite/mastodon/pull/11906), [noellabo](https://github.com/tootsuite/mastodon/pull/11902)) -- **Add table of contents to about page** ([Gargron](https://github.com/tootsuite/mastodon/pull/11885), [ykzts](https://github.com/tootsuite/mastodon/pull/11941), [ykzts](https://github.com/tootsuite/mastodon/pull/11895), [Kjwon15](https://github.com/tootsuite/mastodon/pull/11916)) -- **Add password challenge to 2FA settings, e-mail notifications** ([Gargron](https://github.com/tootsuite/mastodon/pull/11878)) -- **Add optional public list of domain blocks with comments** ([ThibG](https://github.com/tootsuite/mastodon/pull/11298), [ThibG](https://github.com/tootsuite/mastodon/pull/11515), [Gargron](https://github.com/tootsuite/mastodon/pull/11908)) -- Add an RSS feed for featured hashtags ([noellabo](https://github.com/tootsuite/mastodon/pull/10502)) -- Add explanations to featured hashtags UI and profile ([Gargron](https://github.com/tootsuite/mastodon/pull/11586)) -- **Add hashtag trends with admin and user settings** ([Gargron](https://github.com/tootsuite/mastodon/pull/11490), [Gargron](https://github.com/tootsuite/mastodon/pull/11502), [Gargron](https://github.com/tootsuite/mastodon/pull/11641), [Gargron](https://github.com/tootsuite/mastodon/pull/11594), [Gargron](https://github.com/tootsuite/mastodon/pull/11517), [mayaeh](https://github.com/tootsuite/mastodon/pull/11845), [Gargron](https://github.com/tootsuite/mastodon/pull/11774), [Gargron](https://github.com/tootsuite/mastodon/pull/11712), [Gargron](https://github.com/tootsuite/mastodon/pull/11791), [Gargron](https://github.com/tootsuite/mastodon/pull/11743), [Gargron](https://github.com/tootsuite/mastodon/pull/11740), [Gargron](https://github.com/tootsuite/mastodon/pull/11714), [ThibG](https://github.com/tootsuite/mastodon/pull/11631), [Sasha-Sorokin](https://github.com/tootsuite/mastodon/pull/11569), [Gargron](https://github.com/tootsuite/mastodon/pull/11524), [Gargron](https://github.com/tootsuite/mastodon/pull/11513)) +- Add special alert for throttled requests in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11677)) +- Add confirmation modal when logging out from the web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11671)) +- **Add audio player in web UI** ([Gargron](https://github.com/mastodon/mastodon/pull/11644), [Gargron](https://github.com/mastodon/mastodon/pull/11652), [Gargron](https://github.com/mastodon/mastodon/pull/11654), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/11629), [Gargron](https://github.com/mastodon/mastodon/pull/12056)) +- **Add autosuggestions for hashtags in web UI** ([Gargron](https://github.com/mastodon/mastodon/pull/11422), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/11632), [Gargron](https://github.com/mastodon/mastodon/pull/11764), [Gargron](https://github.com/mastodon/mastodon/pull/11588), [Gargron](https://github.com/mastodon/mastodon/pull/11442)) +- **Add media editing modal with OCR tool in web UI** ([Gargron](https://github.com/mastodon/mastodon/pull/11563), [Gargron](https://github.com/mastodon/mastodon/pull/11566), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/11575), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/11576), [Gargron](https://github.com/mastodon/mastodon/pull/11577), [Gargron](https://github.com/mastodon/mastodon/pull/11573), [Gargron](https://github.com/mastodon/mastodon/pull/11571)) +- Add indicator of unread notifications to window title when web UI is out of focus ([Gargron](https://github.com/mastodon/mastodon/pull/11560), [Gargron](https://github.com/mastodon/mastodon/pull/11572)) +- Add indicator for which options you voted for in a poll in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11195)) +- **Add search results pagination to web UI** ([Gargron](https://github.com/mastodon/mastodon/pull/11409), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/11447)) +- **Add option to disable real-time updates in web UI ("slow mode")** ([Gargron](https://github.com/mastodon/mastodon/pull/9984), [ykzts](https://github.com/mastodon/mastodon/pull/11880), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/11883), [Gargron](https://github.com/mastodon/mastodon/pull/11898), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/11859)) +- Add option to disable blurhash previews in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11188)) +- Add native smooth scrolling when supported in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11207)) +- Add scrolling to the search bar on focus in web UI ([Kjwon15](https://github.com/mastodon/mastodon/pull/12032)) +- Add refresh button to list of rebloggers/favouriters in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/12031)) +- Add error description and button to copy stack trace to web UI ([Gargron](https://github.com/mastodon/mastodon/pull/12033)) +- Add search and sort functions to hashtag admin UI ([mayaeh](https://github.com/mastodon/mastodon/pull/11829), [Gargron](https://github.com/mastodon/mastodon/pull/11897), [mayaeh](https://github.com/mastodon/mastodon/pull/11875)) +- Add setting for default search engine indexing in admin UI ([brortao](https://github.com/mastodon/mastodon/pull/11804)) +- Add account bio to account view in admin UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11473)) +- **Add option to include reported statuses in warning e-mail from admin UI** ([Gargron](https://github.com/mastodon/mastodon/pull/11639), [Gargron](https://github.com/mastodon/mastodon/pull/11812), [Gargron](https://github.com/mastodon/mastodon/pull/11741), [Gargron](https://github.com/mastodon/mastodon/pull/11698), [mayaeh](https://github.com/mastodon/mastodon/pull/11765)) +- Add number of pending accounts and pending hashtags to dashboard in admin UI ([Gargron](https://github.com/mastodon/mastodon/pull/11514)) +- **Add account migration UI** ([Gargron](https://github.com/mastodon/mastodon/pull/11846), [noellabo](https://github.com/mastodon/mastodon/pull/11905), [noellabo](https://github.com/mastodon/mastodon/pull/11907), [noellabo](https://github.com/mastodon/mastodon/pull/11906), [noellabo](https://github.com/mastodon/mastodon/pull/11902)) +- **Add table of contents to about page** ([Gargron](https://github.com/mastodon/mastodon/pull/11885), [ykzts](https://github.com/mastodon/mastodon/pull/11941), [ykzts](https://github.com/mastodon/mastodon/pull/11895), [Kjwon15](https://github.com/mastodon/mastodon/pull/11916)) +- **Add password challenge to 2FA settings, e-mail notifications** ([Gargron](https://github.com/mastodon/mastodon/pull/11878)) +- **Add optional public list of domain blocks with comments** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11298), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/11515), [Gargron](https://github.com/mastodon/mastodon/pull/11908)) +- Add an RSS feed for featured hashtags ([noellabo](https://github.com/mastodon/mastodon/pull/10502)) +- Add explanations to featured hashtags UI and profile ([Gargron](https://github.com/mastodon/mastodon/pull/11586)) +- **Add hashtag trends with admin and user settings** ([Gargron](https://github.com/mastodon/mastodon/pull/11490), [Gargron](https://github.com/mastodon/mastodon/pull/11502), [Gargron](https://github.com/mastodon/mastodon/pull/11641), [Gargron](https://github.com/mastodon/mastodon/pull/11594), [Gargron](https://github.com/mastodon/mastodon/pull/11517), [mayaeh](https://github.com/mastodon/mastodon/pull/11845), [Gargron](https://github.com/mastodon/mastodon/pull/11774), [Gargron](https://github.com/mastodon/mastodon/pull/11712), [Gargron](https://github.com/mastodon/mastodon/pull/11791), [Gargron](https://github.com/mastodon/mastodon/pull/11743), [Gargron](https://github.com/mastodon/mastodon/pull/11740), [Gargron](https://github.com/mastodon/mastodon/pull/11714), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/11631), [Sasha-Sorokin](https://github.com/mastodon/mastodon/pull/11569), [Gargron](https://github.com/mastodon/mastodon/pull/11524), [Gargron](https://github.com/mastodon/mastodon/pull/11513)) - Add hashtag usage breakdown to admin UI - Add batch actions for hashtags to admin UI - Add trends to web UI - Add trends to public pages - Add user preference to hide trends - Add admin setting to disable trends -- **Add categories for custom emojis** ([Gargron](https://github.com/tootsuite/mastodon/pull/11196), [Gargron](https://github.com/tootsuite/mastodon/pull/11793), [Gargron](https://github.com/tootsuite/mastodon/pull/11920), [highemerly](https://github.com/tootsuite/mastodon/pull/11876)) +- **Add categories for custom emojis** ([Gargron](https://github.com/mastodon/mastodon/pull/11196), [Gargron](https://github.com/mastodon/mastodon/pull/11793), [Gargron](https://github.com/mastodon/mastodon/pull/11920), [highemerly](https://github.com/mastodon/mastodon/pull/11876)) - Add custom emoji categories to emoji picker in web UI - Add `category` to custom emojis in REST API - Add batch actions for custom emojis in admin UI -- Add max image dimensions to error message ([raboof](https://github.com/tootsuite/mastodon/pull/11552)) -- Add aac, m4a, 3gp, amr, wma to allowed audio formats ([Gargron](https://github.com/tootsuite/mastodon/pull/11342), [umonaca](https://github.com/tootsuite/mastodon/pull/11687)) -- **Add search syntax for operators and phrases** ([Gargron](https://github.com/tootsuite/mastodon/pull/11411)) -- **Add REST API for managing featured hashtags** ([noellabo](https://github.com/tootsuite/mastodon/pull/11778)) -- **Add REST API for managing timeline read markers** ([Gargron](https://github.com/tootsuite/mastodon/pull/11762)) -- Add `exclude_unreviewed` param to `GET /api/v2/search` REST API ([Gargron](https://github.com/tootsuite/mastodon/pull/11977)) -- Add `reason` param to `POST /api/v1/accounts` REST API ([Gargron](https://github.com/tootsuite/mastodon/pull/12064)) -- **Add ActivityPub secure mode** ([Gargron](https://github.com/tootsuite/mastodon/pull/11269), [ThibG](https://github.com/tootsuite/mastodon/pull/11332), [ThibG](https://github.com/tootsuite/mastodon/pull/11295)) -- Add HTTP signatures to all outgoing ActivityPub GET requests ([Gargron](https://github.com/tootsuite/mastodon/pull/11284), [ThibG](https://github.com/tootsuite/mastodon/pull/11300)) -- Add support for ActivityPub Audio activities ([ThibG](https://github.com/tootsuite/mastodon/pull/11189)) -- Add ActivityPub actor representing the entire server ([ThibG](https://github.com/tootsuite/mastodon/pull/11321), [rtucker](https://github.com/tootsuite/mastodon/pull/11400), [ThibG](https://github.com/tootsuite/mastodon/pull/11561), [Gargron](https://github.com/tootsuite/mastodon/pull/11798)) -- **Add whitelist mode** ([Gargron](https://github.com/tootsuite/mastodon/pull/11291), [mayaeh](https://github.com/tootsuite/mastodon/pull/11634)) -- Add config of multipart threshold for S3 ([ykzts](https://github.com/tootsuite/mastodon/pull/11924), [ykzts](https://github.com/tootsuite/mastodon/pull/11944)) -- Add health check endpoint for web ([ykzts](https://github.com/tootsuite/mastodon/pull/11770), [ykzts](https://github.com/tootsuite/mastodon/pull/11947)) -- Add HTTP signature keyId to request log ([Gargron](https://github.com/tootsuite/mastodon/pull/11591)) -- Add `SMTP_REPLY_TO` environment variable ([hugogameiro](https://github.com/tootsuite/mastodon/pull/11718)) -- Add `tootctl preview_cards remove` command ([mayaeh](https://github.com/tootsuite/mastodon/pull/11320)) -- Add `tootctl media refresh` command ([Gargron](https://github.com/tootsuite/mastodon/pull/11775)) -- Add `tootctl cache recount` command ([Gargron](https://github.com/tootsuite/mastodon/pull/11597)) -- Add option to exclude suspended domains from `tootctl domains crawl` ([dariusk](https://github.com/tootsuite/mastodon/pull/11454)) -- Add parallelization to `tootctl search deploy` ([noellabo](https://github.com/tootsuite/mastodon/pull/12051)) -- Add soft delete for statuses for instant deletes through API ([Gargron](https://github.com/tootsuite/mastodon/pull/11623), [Gargron](https://github.com/tootsuite/mastodon/pull/11648)) -- Add rails-level JSON caching ([Gargron](https://github.com/tootsuite/mastodon/pull/11333), [Gargron](https://github.com/tootsuite/mastodon/pull/11271)) -- **Add request pool to improve delivery performance** ([Gargron](https://github.com/tootsuite/mastodon/pull/10353), [ykzts](https://github.com/tootsuite/mastodon/pull/11756)) -- Add concurrent connection attempts to resolved IP addresses ([ThibG](https://github.com/tootsuite/mastodon/pull/11757)) -- Add index for remember_token to improve login performance ([abcang](https://github.com/tootsuite/mastodon/pull/11881)) -- **Add more accurate hashtag search** ([Gargron](https://github.com/tootsuite/mastodon/pull/11579), [Gargron](https://github.com/tootsuite/mastodon/pull/11427), [Gargron](https://github.com/tootsuite/mastodon/pull/11448)) -- **Add more accurate account search** ([Gargron](https://github.com/tootsuite/mastodon/pull/11537), [Gargron](https://github.com/tootsuite/mastodon/pull/11580)) -- **Add a spam check** ([Gargron](https://github.com/tootsuite/mastodon/pull/11217), [Gargron](https://github.com/tootsuite/mastodon/pull/11806), [ThibG](https://github.com/tootsuite/mastodon/pull/11296)) -- Add new languages ([Gargron](https://github.com/tootsuite/mastodon/pull/12062)) +- Add max image dimensions to error message ([raboof](https://github.com/mastodon/mastodon/pull/11552)) +- Add aac, m4a, 3gp, amr, wma to allowed audio formats ([Gargron](https://github.com/mastodon/mastodon/pull/11342), [umonaca](https://github.com/mastodon/mastodon/pull/11687)) +- **Add search syntax for operators and phrases** ([Gargron](https://github.com/mastodon/mastodon/pull/11411)) +- **Add REST API for managing featured hashtags** ([noellabo](https://github.com/mastodon/mastodon/pull/11778)) +- **Add REST API for managing timeline read markers** ([Gargron](https://github.com/mastodon/mastodon/pull/11762)) +- Add `exclude_unreviewed` param to `GET /api/v2/search` REST API ([Gargron](https://github.com/mastodon/mastodon/pull/11977)) +- Add `reason` param to `POST /api/v1/accounts` REST API ([Gargron](https://github.com/mastodon/mastodon/pull/12064)) +- **Add ActivityPub secure mode** ([Gargron](https://github.com/mastodon/mastodon/pull/11269), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/11332), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/11295)) +- Add HTTP signatures to all outgoing ActivityPub GET requests ([Gargron](https://github.com/mastodon/mastodon/pull/11284), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/11300)) +- Add support for ActivityPub Audio activities ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11189)) +- Add ActivityPub actor representing the entire server ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11321), [rtucker](https://github.com/mastodon/mastodon/pull/11400), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/11561), [Gargron](https://github.com/mastodon/mastodon/pull/11798)) +- **Add whitelist mode** ([Gargron](https://github.com/mastodon/mastodon/pull/11291), [mayaeh](https://github.com/mastodon/mastodon/pull/11634)) +- Add config of multipart threshold for S3 ([ykzts](https://github.com/mastodon/mastodon/pull/11924), [ykzts](https://github.com/mastodon/mastodon/pull/11944)) +- Add health check endpoint for web ([ykzts](https://github.com/mastodon/mastodon/pull/11770), [ykzts](https://github.com/mastodon/mastodon/pull/11947)) +- Add HTTP signature keyId to request log ([Gargron](https://github.com/mastodon/mastodon/pull/11591)) +- Add `SMTP_REPLY_TO` environment variable ([hugogameiro](https://github.com/mastodon/mastodon/pull/11718)) +- Add `tootctl preview_cards remove` command ([mayaeh](https://github.com/mastodon/mastodon/pull/11320)) +- Add `tootctl media refresh` command ([Gargron](https://github.com/mastodon/mastodon/pull/11775)) +- Add `tootctl cache recount` command ([Gargron](https://github.com/mastodon/mastodon/pull/11597)) +- Add option to exclude suspended domains from `tootctl domains crawl` ([dariusk](https://github.com/mastodon/mastodon/pull/11454)) +- Add parallelization to `tootctl search deploy` ([noellabo](https://github.com/mastodon/mastodon/pull/12051)) +- Add soft delete for statuses for instant deletes through API ([Gargron](https://github.com/mastodon/mastodon/pull/11623), [Gargron](https://github.com/mastodon/mastodon/pull/11648)) +- Add rails-level JSON caching ([Gargron](https://github.com/mastodon/mastodon/pull/11333), [Gargron](https://github.com/mastodon/mastodon/pull/11271)) +- **Add request pool to improve delivery performance** ([Gargron](https://github.com/mastodon/mastodon/pull/10353), [ykzts](https://github.com/mastodon/mastodon/pull/11756)) +- Add concurrent connection attempts to resolved IP addresses ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11757)) +- Add index for remember_token to improve login performance ([abcang](https://github.com/mastodon/mastodon/pull/11881)) +- **Add more accurate hashtag search** ([Gargron](https://github.com/mastodon/mastodon/pull/11579), [Gargron](https://github.com/mastodon/mastodon/pull/11427), [Gargron](https://github.com/mastodon/mastodon/pull/11448)) +- **Add more accurate account search** ([Gargron](https://github.com/mastodon/mastodon/pull/11537), [Gargron](https://github.com/mastodon/mastodon/pull/11580)) +- **Add a spam check** ([Gargron](https://github.com/mastodon/mastodon/pull/11217), [Gargron](https://github.com/mastodon/mastodon/pull/11806), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/11296)) +- Add new languages ([Gargron](https://github.com/mastodon/mastodon/pull/12062)) - Breton - Spanish (Argentina) - Estonian - Macedonian - New Norwegian -- Add NodeInfo endpoint ([Gargron](https://github.com/tootsuite/mastodon/pull/12002), [Gargron](https://github.com/tootsuite/mastodon/pull/12058)) +- Add NodeInfo endpoint ([Gargron](https://github.com/mastodon/mastodon/pull/12002), [Gargron](https://github.com/mastodon/mastodon/pull/12058)) ### Changed -- **Change conversations UI** ([Gargron](https://github.com/tootsuite/mastodon/pull/11896)) -- Change dashboard to short number notation ([noellabo](https://github.com/tootsuite/mastodon/pull/11847), [noellabo](https://github.com/tootsuite/mastodon/pull/11911)) -- Change REST API `GET /api/v1/timelines/public` to require authentication when public preview is off ([ThibG](https://github.com/tootsuite/mastodon/pull/11802)) -- Change REST API `POST /api/v1/follow_requests/:id/(approve|reject)` to return relationship ([ThibG](https://github.com/tootsuite/mastodon/pull/11800)) -- Change rate limit for media proxy ([ykzts](https://github.com/tootsuite/mastodon/pull/11814)) -- Change unlisted custom emoji to not appear in autosuggestions ([Gargron](https://github.com/tootsuite/mastodon/pull/11818)) -- Change max length of media descriptions from 420 to 1500 characters ([Gargron](https://github.com/tootsuite/mastodon/pull/11819), [ThibG](https://github.com/tootsuite/mastodon/pull/11836)) -- **Change deletes to preserve soft-deleted statuses in unresolved reports** ([Gargron](https://github.com/tootsuite/mastodon/pull/11805)) -- **Change tootctl to use inline parallelization instead of Sidekiq** ([Gargron](https://github.com/tootsuite/mastodon/pull/11776)) -- **Change account deletion page to have better explanations** ([Gargron](https://github.com/tootsuite/mastodon/pull/11753), [Gargron](https://github.com/tootsuite/mastodon/pull/11763)) -- Change hashtag component in web UI to show numbers for 2 last days ([Gargron](https://github.com/tootsuite/mastodon/pull/11742), [Gargron](https://github.com/tootsuite/mastodon/pull/11755), [Gargron](https://github.com/tootsuite/mastodon/pull/11754)) -- Change OpenGraph description on sign-up page to reflect invite ([Gargron](https://github.com/tootsuite/mastodon/pull/11744)) -- Change layout of public profile directory to be the same as in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/11705)) -- Change detailed status child ordering to sort self-replies on top ([ThibG](https://github.com/tootsuite/mastodon/pull/11686)) -- Change window resize handler to switch to/from mobile layout as soon as needed ([ThibG](https://github.com/tootsuite/mastodon/pull/11656)) -- Change icon button styles to make hover/focus states more obvious ([ThibG](https://github.com/tootsuite/mastodon/pull/11474)) -- Change contrast of status links that are not mentions or hashtags ([ThibG](https://github.com/tootsuite/mastodon/pull/11406)) -- **Change hashtags to preserve first-used casing** ([Gargron](https://github.com/tootsuite/mastodon/pull/11416), [Gargron](https://github.com/tootsuite/mastodon/pull/11508), [Gargron](https://github.com/tootsuite/mastodon/pull/11504), [Gargron](https://github.com/tootsuite/mastodon/pull/11507), [Gargron](https://github.com/tootsuite/mastodon/pull/11441)) -- **Change unconfirmed user login behaviour** ([Gargron](https://github.com/tootsuite/mastodon/pull/11375), [ThibG](https://github.com/tootsuite/mastodon/pull/11394), [Gargron](https://github.com/tootsuite/mastodon/pull/11860)) -- **Change single-column mode to scroll the whole page** ([Gargron](https://github.com/tootsuite/mastodon/pull/11359), [Gargron](https://github.com/tootsuite/mastodon/pull/11894), [Gargron](https://github.com/tootsuite/mastodon/pull/11891), [ThibG](https://github.com/tootsuite/mastodon/pull/11655), [Gargron](https://github.com/tootsuite/mastodon/pull/11463), [Gargron](https://github.com/tootsuite/mastodon/pull/11458), [ThibG](https://github.com/tootsuite/mastodon/pull/11395), [Gargron](https://github.com/tootsuite/mastodon/pull/11418)) -- Change `tootctl accounts follow` to only work with local accounts ([angristan](https://github.com/tootsuite/mastodon/pull/11592)) -- Change Dockerfile ([Shleeble](https://github.com/tootsuite/mastodon/pull/11710), [ykzts](https://github.com/tootsuite/mastodon/pull/11768), [Shleeble](https://github.com/tootsuite/mastodon/pull/11707)) -- Change supported Node versions to include v12 ([abcang](https://github.com/tootsuite/mastodon/pull/11706)) -- Change Portuguese language from `pt` to `pt-PT` ([Gargron](https://github.com/tootsuite/mastodon/pull/11820)) -- Change domain block silence to always require approval on follow ([ThibG](https://github.com/tootsuite/mastodon/pull/11975)) -- Change link preview fetcher to not perform a HEAD request first ([Gargron](https://github.com/tootsuite/mastodon/pull/12028)) -- Change `tootctl domains purge` to accept multiple domains at once ([Gargron](https://github.com/tootsuite/mastodon/pull/12046)) +- **Change conversations UI** ([Gargron](https://github.com/mastodon/mastodon/pull/11896)) +- Change dashboard to short number notation ([noellabo](https://github.com/mastodon/mastodon/pull/11847), [noellabo](https://github.com/mastodon/mastodon/pull/11911)) +- Change REST API `GET /api/v1/timelines/public` to require authentication when public preview is off ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11802)) +- Change REST API `POST /api/v1/follow_requests/:id/(approve|reject)` to return relationship ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11800)) +- Change rate limit for media proxy ([ykzts](https://github.com/mastodon/mastodon/pull/11814)) +- Change unlisted custom emoji to not appear in autosuggestions ([Gargron](https://github.com/mastodon/mastodon/pull/11818)) +- Change max length of media descriptions from 420 to 1500 characters ([Gargron](https://github.com/mastodon/mastodon/pull/11819), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/11836)) +- **Change deletes to preserve soft-deleted statuses in unresolved reports** ([Gargron](https://github.com/mastodon/mastodon/pull/11805)) +- **Change tootctl to use inline parallelization instead of Sidekiq** ([Gargron](https://github.com/mastodon/mastodon/pull/11776)) +- **Change account deletion page to have better explanations** ([Gargron](https://github.com/mastodon/mastodon/pull/11753), [Gargron](https://github.com/mastodon/mastodon/pull/11763)) +- Change hashtag component in web UI to show numbers for 2 last days ([Gargron](https://github.com/mastodon/mastodon/pull/11742), [Gargron](https://github.com/mastodon/mastodon/pull/11755), [Gargron](https://github.com/mastodon/mastodon/pull/11754)) +- Change OpenGraph description on sign-up page to reflect invite ([Gargron](https://github.com/mastodon/mastodon/pull/11744)) +- Change layout of public profile directory to be the same as in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/11705)) +- Change detailed status child ordering to sort self-replies on top ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11686)) +- Change window resize handler to switch to/from mobile layout as soon as needed ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11656)) +- Change icon button styles to make hover/focus states more obvious ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11474)) +- Change contrast of status links that are not mentions or hashtags ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11406)) +- **Change hashtags to preserve first-used casing** ([Gargron](https://github.com/mastodon/mastodon/pull/11416), [Gargron](https://github.com/mastodon/mastodon/pull/11508), [Gargron](https://github.com/mastodon/mastodon/pull/11504), [Gargron](https://github.com/mastodon/mastodon/pull/11507), [Gargron](https://github.com/mastodon/mastodon/pull/11441)) +- **Change unconfirmed user login behaviour** ([Gargron](https://github.com/mastodon/mastodon/pull/11375), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/11394), [Gargron](https://github.com/mastodon/mastodon/pull/11860)) +- **Change single-column mode to scroll the whole page** ([Gargron](https://github.com/mastodon/mastodon/pull/11359), [Gargron](https://github.com/mastodon/mastodon/pull/11894), [Gargron](https://github.com/mastodon/mastodon/pull/11891), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/11655), [Gargron](https://github.com/mastodon/mastodon/pull/11463), [Gargron](https://github.com/mastodon/mastodon/pull/11458), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/11395), [Gargron](https://github.com/mastodon/mastodon/pull/11418)) +- Change `tootctl accounts follow` to only work with local accounts ([angristan](https://github.com/mastodon/mastodon/pull/11592)) +- Change Dockerfile ([Shleeble](https://github.com/mastodon/mastodon/pull/11710), [ykzts](https://github.com/mastodon/mastodon/pull/11768), [Shleeble](https://github.com/mastodon/mastodon/pull/11707)) +- Change supported Node versions to include v12 ([abcang](https://github.com/mastodon/mastodon/pull/11706)) +- Change Portuguese language from `pt` to `pt-PT` ([Gargron](https://github.com/mastodon/mastodon/pull/11820)) +- Change domain block silence to always require approval on follow ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11975)) +- Change link preview fetcher to not perform a HEAD request first ([Gargron](https://github.com/mastodon/mastodon/pull/12028)) +- Change `tootctl domains purge` to accept multiple domains at once ([Gargron](https://github.com/mastodon/mastodon/pull/12046)) ### Removed -- **Remove OStatus support** ([Gargron](https://github.com/tootsuite/mastodon/pull/11205), [Gargron](https://github.com/tootsuite/mastodon/pull/11303), [Gargron](https://github.com/tootsuite/mastodon/pull/11460), [ThibG](https://github.com/tootsuite/mastodon/pull/11280), [ThibG](https://github.com/tootsuite/mastodon/pull/11278)) -- Remove Atom feeds and old URLs in the form of `GET /:username/updates/:id` ([Gargron](https://github.com/tootsuite/mastodon/pull/11247)) -- Remove WebP support ([angristan](https://github.com/tootsuite/mastodon/pull/11589)) -- Remove deprecated config options from Heroku and Scalingo ([ykzts](https://github.com/tootsuite/mastodon/pull/11925)) -- Remove deprecated REST API `GET /api/v1/search` API ([Gargron](https://github.com/tootsuite/mastodon/pull/11823)) -- Remove deprecated REST API `GET /api/v1/statuses/:id/card` ([Gargron](https://github.com/tootsuite/mastodon/pull/11213)) -- Remove deprecated REST API `POST /api/v1/notifications/dismiss?id=:id` ([Gargron](https://github.com/tootsuite/mastodon/pull/11214)) -- Remove deprecated REST API `GET /api/v1/timelines/direct` ([Gargron](https://github.com/tootsuite/mastodon/pull/11212)) +- **Remove OStatus support** ([Gargron](https://github.com/mastodon/mastodon/pull/11205), [Gargron](https://github.com/mastodon/mastodon/pull/11303), [Gargron](https://github.com/mastodon/mastodon/pull/11460), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/11280), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/11278)) +- Remove Atom feeds and old URLs in the form of `GET /:username/updates/:id` ([Gargron](https://github.com/mastodon/mastodon/pull/11247)) +- Remove WebP support ([angristan](https://github.com/mastodon/mastodon/pull/11589)) +- Remove deprecated config options from Heroku and Scalingo ([ykzts](https://github.com/mastodon/mastodon/pull/11925)) +- Remove deprecated REST API `GET /api/v1/search` API ([Gargron](https://github.com/mastodon/mastodon/pull/11823)) +- Remove deprecated REST API `GET /api/v1/statuses/:id/card` ([Gargron](https://github.com/mastodon/mastodon/pull/11213)) +- Remove deprecated REST API `POST /api/v1/notifications/dismiss?id=:id` ([Gargron](https://github.com/mastodon/mastodon/pull/11214)) +- Remove deprecated REST API `GET /api/v1/timelines/direct` ([Gargron](https://github.com/mastodon/mastodon/pull/11212)) ### Fixed -- Fix manifest warning ([ykzts](https://github.com/tootsuite/mastodon/pull/11767)) -- Fix admin UI for custom emoji not respecting GIF autoplay preference ([ThibG](https://github.com/tootsuite/mastodon/pull/11801)) -- Fix page body not being scrollable in admin/settings layout ([Gargron](https://github.com/tootsuite/mastodon/pull/11893)) -- Fix placeholder colors for inputs not being explicitly defined ([Gargron](https://github.com/tootsuite/mastodon/pull/11890)) -- Fix incorrect enclosure length in RSS ([tsia](https://github.com/tootsuite/mastodon/pull/11889)) -- Fix TOTP codes not being filtered from logs during enabling/disabling ([Gargron](https://github.com/tootsuite/mastodon/pull/11877)) -- Fix webfinger response not returning 410 when account is suspended ([Gargron](https://github.com/tootsuite/mastodon/pull/11869)) -- Fix ActivityPub Move handler queuing jobs that will fail if account is suspended ([Gargron](https://github.com/tootsuite/mastodon/pull/11864)) -- Fix SSO login not using existing account when e-mail is verified ([Gargron](https://github.com/tootsuite/mastodon/pull/11862)) -- Fix web UI allowing uploads past status limit via drag & drop ([Gargron](https://github.com/tootsuite/mastodon/pull/11863)) -- Fix expiring polls not being displayed as such in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/11835)) -- Fix 2FA challenge and password challenge for non-database users ([Gargron](https://github.com/tootsuite/mastodon/pull/11831), [Gargron](https://github.com/tootsuite/mastodon/pull/11943)) -- Fix profile fields overflowing page width in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/11828)) -- Fix web push subscriptions being deleted on rate limit or timeout ([Gargron](https://github.com/tootsuite/mastodon/pull/11826)) -- Fix display of long poll options in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/11717), [ThibG](https://github.com/tootsuite/mastodon/pull/11833)) -- Fix search API not resolving URL when `type` is given ([Gargron](https://github.com/tootsuite/mastodon/pull/11822)) -- Fix hashtags being split by ZWNJ character ([Gargron](https://github.com/tootsuite/mastodon/pull/11821)) -- Fix scroll position resetting when opening media modals in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/11815)) -- Fix duplicate HTML IDs on about page ([ThibG](https://github.com/tootsuite/mastodon/pull/11803)) -- Fix admin UI showing superfluous reject media/reports on suspended domain blocks ([ThibG](https://github.com/tootsuite/mastodon/pull/11749)) -- Fix ActivityPub context not being dynamically computed ([ThibG](https://github.com/tootsuite/mastodon/pull/11746)) -- Fix Mastodon logo style on hover on public pages' footer ([ThibG](https://github.com/tootsuite/mastodon/pull/11735)) -- Fix height of dashboard counters ([ThibG](https://github.com/tootsuite/mastodon/pull/11736)) -- Fix custom emoji animation on hover in web UI directory bios ([ThibG](https://github.com/tootsuite/mastodon/pull/11716)) -- Fix non-numbers being passed to Redis and causing an error ([Gargron](https://github.com/tootsuite/mastodon/pull/11697)) -- Fix error in REST API for an account's statuses ([Gargron](https://github.com/tootsuite/mastodon/pull/11700)) -- Fix uncaught error when resource param is missing in Webfinger request ([Gargron](https://github.com/tootsuite/mastodon/pull/11701)) -- Fix uncaught domain normalization error in remote follow ([Gargron](https://github.com/tootsuite/mastodon/pull/11703)) -- Fix uncaught 422 and 500 errors ([Gargron](https://github.com/tootsuite/mastodon/pull/11590), [Gargron](https://github.com/tootsuite/mastodon/pull/11811)) -- Fix uncaught parameter missing exceptions and missing error templates ([Gargron](https://github.com/tootsuite/mastodon/pull/11702)) -- Fix encoding error when checking e-mail MX records ([Gargron](https://github.com/tootsuite/mastodon/pull/11696)) -- Fix items in StatusContent render list not all having a key ([ThibG](https://github.com/tootsuite/mastodon/pull/11645)) -- Fix remote and staff-removed statuses leaving media behind for a day ([Gargron](https://github.com/tootsuite/mastodon/pull/11638)) -- Fix CSP needlessly allowing blob URLs in script-src ([ThibG](https://github.com/tootsuite/mastodon/pull/11620)) -- Fix ignoring whole status because of one invalid hashtag ([Gargron](https://github.com/tootsuite/mastodon/pull/11621)) -- Fix hidden statuses losing focus ([ThibG](https://github.com/tootsuite/mastodon/pull/11208)) -- Fix loading bar being obscured by other elements in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/11598)) -- Fix multiple issues with replies collection for pages further than self-replies ([ThibG](https://github.com/tootsuite/mastodon/pull/11582)) -- Fix blurhash and autoplay not working on public pages ([Gargron](https://github.com/tootsuite/mastodon/pull/11585)) -- Fix 422 being returned instead of 404 when POSTing to unmatched routes ([Gargron](https://github.com/tootsuite/mastodon/pull/11574), [Gargron](https://github.com/tootsuite/mastodon/pull/11704)) -- Fix client-side resizing of image uploads ([ThibG](https://github.com/tootsuite/mastodon/pull/11570)) -- Fix short number formatting for numbers above million in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/11559)) -- Fix ActivityPub and REST API queries setting cookies and preventing caching ([ThibG](https://github.com/tootsuite/mastodon/pull/11539), [ThibG](https://github.com/tootsuite/mastodon/pull/11557), [ThibG](https://github.com/tootsuite/mastodon/pull/11336), [ThibG](https://github.com/tootsuite/mastodon/pull/11331)) -- Fix some emojis in profile metadata labels are not emojified. ([kedamaDQ](https://github.com/tootsuite/mastodon/pull/11534)) -- Fix account search always returning exact match on paginated results ([Gargron](https://github.com/tootsuite/mastodon/pull/11525)) -- Fix acct URIs with IDN domains not being resolved ([Gargron](https://github.com/tootsuite/mastodon/pull/11520)) -- Fix admin dashboard missing latest features ([Gargron](https://github.com/tootsuite/mastodon/pull/11505)) -- Fix jumping of toot date when clicking spoiler button ([ariasuni](https://github.com/tootsuite/mastodon/pull/11449)) -- Fix boost to original audience not working on mobile in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/11371)) -- Fix handling of webfinger redirects in ResolveAccountService ([ThibG](https://github.com/tootsuite/mastodon/pull/11279)) -- Fix URLs appearing twice in errors of ActivityPub::DeliveryWorker ([Gargron](https://github.com/tootsuite/mastodon/pull/11231)) -- Fix support for HTTP proxies ([ThibG](https://github.com/tootsuite/mastodon/pull/11245)) -- Fix HTTP requests to IPv6 hosts ([ThibG](https://github.com/tootsuite/mastodon/pull/11240)) -- Fix error in ElasticSearch index import ([mayaeh](https://github.com/tootsuite/mastodon/pull/11192)) -- Fix duplicate account error when seeding development database ([ysksn](https://github.com/tootsuite/mastodon/pull/11366)) -- Fix performance of session clean-up scheduler ([abcang](https://github.com/tootsuite/mastodon/pull/11871)) -- Fix older migrations not running ([zunda](https://github.com/tootsuite/mastodon/pull/11377)) -- Fix URLs counting towards RTL detection ([ahangarha](https://github.com/tootsuite/mastodon/pull/11759)) -- Fix unnecessary status re-rendering in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/11211)) -- Fix http_parser.rb gem not being compiled when no network available ([petabyteboy](https://github.com/tootsuite/mastodon/pull/11444)) -- Fix muted text color not applying to all text ([trwnh](https://github.com/tootsuite/mastodon/pull/11996)) -- Fix follower/following lists resetting on back-navigation in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/11986)) -- Fix n+1 query when approving multiple follow requests ([abcang](https://github.com/tootsuite/mastodon/pull/12004)) -- Fix records not being indexed into ElasticSearch sometimes ([Gargron](https://github.com/tootsuite/mastodon/pull/12024)) -- Fix needlessly indexing unsearchable statuses into ElasticSearch ([Gargron](https://github.com/tootsuite/mastodon/pull/12041)) -- Fix new user bootstrapping crashing when to-be-followed accounts are invalid ([ThibG](https://github.com/tootsuite/mastodon/pull/12037)) -- Fix featured hashtag URL being interpreted as media or replies tab ([Gargron](https://github.com/tootsuite/mastodon/pull/12048)) -- Fix account counters being overwritten by parallel writes ([Gargron](https://github.com/tootsuite/mastodon/pull/12045)) +- Fix manifest warning ([ykzts](https://github.com/mastodon/mastodon/pull/11767)) +- Fix admin UI for custom emoji not respecting GIF autoplay preference ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11801)) +- Fix page body not being scrollable in admin/settings layout ([Gargron](https://github.com/mastodon/mastodon/pull/11893)) +- Fix placeholder colors for inputs not being explicitly defined ([Gargron](https://github.com/mastodon/mastodon/pull/11890)) +- Fix incorrect enclosure length in RSS ([tsia](https://github.com/mastodon/mastodon/pull/11889)) +- Fix TOTP codes not being filtered from logs during enabling/disabling ([Gargron](https://github.com/mastodon/mastodon/pull/11877)) +- Fix webfinger response not returning 410 when account is suspended ([Gargron](https://github.com/mastodon/mastodon/pull/11869)) +- Fix ActivityPub Move handler queuing jobs that will fail if account is suspended ([Gargron](https://github.com/mastodon/mastodon/pull/11864)) +- Fix SSO login not using existing account when e-mail is verified ([Gargron](https://github.com/mastodon/mastodon/pull/11862)) +- Fix web UI allowing uploads past status limit via drag & drop ([Gargron](https://github.com/mastodon/mastodon/pull/11863)) +- Fix expiring polls not being displayed as such in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11835)) +- Fix 2FA challenge and password challenge for non-database users ([Gargron](https://github.com/mastodon/mastodon/pull/11831), [Gargron](https://github.com/mastodon/mastodon/pull/11943)) +- Fix profile fields overflowing page width in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/11828)) +- Fix web push subscriptions being deleted on rate limit or timeout ([Gargron](https://github.com/mastodon/mastodon/pull/11826)) +- Fix display of long poll options in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11717), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/11833)) +- Fix search API not resolving URL when `type` is given ([Gargron](https://github.com/mastodon/mastodon/pull/11822)) +- Fix hashtags being split by ZWNJ character ([Gargron](https://github.com/mastodon/mastodon/pull/11821)) +- Fix scroll position resetting when opening media modals in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/11815)) +- Fix duplicate HTML IDs on about page ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11803)) +- Fix admin UI showing superfluous reject media/reports on suspended domain blocks ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11749)) +- Fix ActivityPub context not being dynamically computed ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11746)) +- Fix Mastodon logo style on hover on public pages' footer ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11735)) +- Fix height of dashboard counters ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11736)) +- Fix custom emoji animation on hover in web UI directory bios ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11716)) +- Fix non-numbers being passed to Redis and causing an error ([Gargron](https://github.com/mastodon/mastodon/pull/11697)) +- Fix error in REST API for an account's statuses ([Gargron](https://github.com/mastodon/mastodon/pull/11700)) +- Fix uncaught error when resource param is missing in Webfinger request ([Gargron](https://github.com/mastodon/mastodon/pull/11701)) +- Fix uncaught domain normalization error in remote follow ([Gargron](https://github.com/mastodon/mastodon/pull/11703)) +- Fix uncaught 422 and 500 errors ([Gargron](https://github.com/mastodon/mastodon/pull/11590), [Gargron](https://github.com/mastodon/mastodon/pull/11811)) +- Fix uncaught parameter missing exceptions and missing error templates ([Gargron](https://github.com/mastodon/mastodon/pull/11702)) +- Fix encoding error when checking e-mail MX records ([Gargron](https://github.com/mastodon/mastodon/pull/11696)) +- Fix items in StatusContent render list not all having a key ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11645)) +- Fix remote and staff-removed statuses leaving media behind for a day ([Gargron](https://github.com/mastodon/mastodon/pull/11638)) +- Fix CSP needlessly allowing blob URLs in script-src ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11620)) +- Fix ignoring whole status because of one invalid hashtag ([Gargron](https://github.com/mastodon/mastodon/pull/11621)) +- Fix hidden statuses losing focus ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11208)) +- Fix loading bar being obscured by other elements in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/11598)) +- Fix multiple issues with replies collection for pages further than self-replies ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11582)) +- Fix blurhash and autoplay not working on public pages ([Gargron](https://github.com/mastodon/mastodon/pull/11585)) +- Fix 422 being returned instead of 404 when POSTing to unmatched routes ([Gargron](https://github.com/mastodon/mastodon/pull/11574), [Gargron](https://github.com/mastodon/mastodon/pull/11704)) +- Fix client-side resizing of image uploads ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11570)) +- Fix short number formatting for numbers above million in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/11559)) +- Fix ActivityPub and REST API queries setting cookies and preventing caching ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11539), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/11557), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/11336), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/11331)) +- Fix some emojis in profile metadata labels are not emojified. ([kedamaDQ](https://github.com/mastodon/mastodon/pull/11534)) +- Fix account search always returning exact match on paginated results ([Gargron](https://github.com/mastodon/mastodon/pull/11525)) +- Fix acct URIs with IDN domains not being resolved ([Gargron](https://github.com/mastodon/mastodon/pull/11520)) +- Fix admin dashboard missing latest features ([Gargron](https://github.com/mastodon/mastodon/pull/11505)) +- Fix jumping of toot date when clicking spoiler button ([ariasuni](https://github.com/mastodon/mastodon/pull/11449)) +- Fix boost to original audience not working on mobile in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11371)) +- Fix handling of webfinger redirects in ResolveAccountService ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11279)) +- Fix URLs appearing twice in errors of ActivityPub::DeliveryWorker ([Gargron](https://github.com/mastodon/mastodon/pull/11231)) +- Fix support for HTTP proxies ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11245)) +- Fix HTTP requests to IPv6 hosts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11240)) +- Fix error in ElasticSearch index import ([mayaeh](https://github.com/mastodon/mastodon/pull/11192)) +- Fix duplicate account error when seeding development database ([ysksn](https://github.com/mastodon/mastodon/pull/11366)) +- Fix performance of session clean-up scheduler ([abcang](https://github.com/mastodon/mastodon/pull/11871)) +- Fix older migrations not running ([zunda](https://github.com/mastodon/mastodon/pull/11377)) +- Fix URLs counting towards RTL detection ([ahangarha](https://github.com/mastodon/mastodon/pull/11759)) +- Fix unnecessary status re-rendering in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11211)) +- Fix http_parser.rb gem not being compiled when no network available ([petabyteboy](https://github.com/mastodon/mastodon/pull/11444)) +- Fix muted text color not applying to all text ([trwnh](https://github.com/mastodon/mastodon/pull/11996)) +- Fix follower/following lists resetting on back-navigation in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/11986)) +- Fix n+1 query when approving multiple follow requests ([abcang](https://github.com/mastodon/mastodon/pull/12004)) +- Fix records not being indexed into ElasticSearch sometimes ([Gargron](https://github.com/mastodon/mastodon/pull/12024)) +- Fix needlessly indexing unsearchable statuses into ElasticSearch ([Gargron](https://github.com/mastodon/mastodon/pull/12041)) +- Fix new user bootstrapping crashing when to-be-followed accounts are invalid ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12037)) +- Fix featured hashtag URL being interpreted as media or replies tab ([Gargron](https://github.com/mastodon/mastodon/pull/12048)) +- Fix account counters being overwritten by parallel writes ([Gargron](https://github.com/mastodon/mastodon/pull/12045)) ### Security -- Fix performance of GIF re-encoding and always strip EXIF data from videos ([Gargron](https://github.com/tootsuite/mastodon/pull/12057)) +- Fix performance of GIF re-encoding and always strip EXIF data from videos ([Gargron](https://github.com/mastodon/mastodon/pull/12057)) ## [2.9.3] - 2019-08-10 ### Added -- Add GIF and WebP support for custom emojis ([Gargron](https://github.com/tootsuite/mastodon/pull/11519)) -- Add logout link to dropdown menu in web UI ([koyuawsmbrtn](https://github.com/tootsuite/mastodon/pull/11353)) -- Add indication that text search is unavailable in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/11112), [ThibG](https://github.com/tootsuite/mastodon/pull/11202)) -- Add `suffix` to `Mastodon::Version` to help forks ([clarfon](https://github.com/tootsuite/mastodon/pull/11407)) -- Add on-hover animation to animated custom emoji in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/11348), [ThibG](https://github.com/tootsuite/mastodon/pull/11404), [ThibG](https://github.com/tootsuite/mastodon/pull/11522)) -- Add custom emoji support in profile metadata labels ([ThibG](https://github.com/tootsuite/mastodon/pull/11350)) +- Add GIF and WebP support for custom emojis ([Gargron](https://github.com/mastodon/mastodon/pull/11519)) +- Add logout link to dropdown menu in web UI ([koyuawsmbrtn](https://github.com/mastodon/mastodon/pull/11353)) +- Add indication that text search is unavailable in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11112), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/11202)) +- Add `suffix` to `Mastodon::Version` to help forks ([clarfon](https://github.com/mastodon/mastodon/pull/11407)) +- Add on-hover animation to animated custom emoji in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11348), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/11404), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/11522)) +- Add custom emoji support in profile metadata labels ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11350)) ### Changed -- Change default interface of web and streaming from 0.0.0.0 to 127.0.0.1 ([Gargron](https://github.com/tootsuite/mastodon/pull/11302), [zunda](https://github.com/tootsuite/mastodon/pull/11378), [Gargron](https://github.com/tootsuite/mastodon/pull/11351), [zunda](https://github.com/tootsuite/mastodon/pull/11326)) -- Change the retry limit of web push notifications ([highemerly](https://github.com/tootsuite/mastodon/pull/11292)) -- Change ActivityPub deliveries to not retry HTTP 501 errors ([Gargron](https://github.com/tootsuite/mastodon/pull/11233)) -- Change language detection to include hashtags as words ([Gargron](https://github.com/tootsuite/mastodon/pull/11341)) -- Change terms and privacy policy pages to always be accessible ([Gargron](https://github.com/tootsuite/mastodon/pull/11334)) -- Change robots tag to include `noarchive` when user opts out of indexing ([Kjwon15](https://github.com/tootsuite/mastodon/pull/11421)) +- Change default interface of web and streaming from 0.0.0.0 to 127.0.0.1 ([Gargron](https://github.com/mastodon/mastodon/pull/11302), [zunda](https://github.com/mastodon/mastodon/pull/11378), [Gargron](https://github.com/mastodon/mastodon/pull/11351), [zunda](https://github.com/mastodon/mastodon/pull/11326)) +- Change the retry limit of web push notifications ([highemerly](https://github.com/mastodon/mastodon/pull/11292)) +- Change ActivityPub deliveries to not retry HTTP 501 errors ([Gargron](https://github.com/mastodon/mastodon/pull/11233)) +- Change language detection to include hashtags as words ([Gargron](https://github.com/mastodon/mastodon/pull/11341)) +- Change terms and privacy policy pages to always be accessible ([Gargron](https://github.com/mastodon/mastodon/pull/11334)) +- Change robots tag to include `noarchive` when user opts out of indexing ([Kjwon15](https://github.com/mastodon/mastodon/pull/11421)) ### Fixed -- Fix account domain block not clearing out notifications ([Gargron](https://github.com/tootsuite/mastodon/pull/11393)) -- Fix incorrect locale sometimes being detected for browser ([Gargron](https://github.com/tootsuite/mastodon/pull/8657)) -- Fix crash when saving invalid domain name ([Gargron](https://github.com/tootsuite/mastodon/pull/11528)) -- Fix pinned statuses REST API returning pagination headers ([Gargron](https://github.com/tootsuite/mastodon/pull/11526)) -- Fix "cancel follow request" button having unreadable text in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/11521)) -- Fix image uploads being blank when canvas read access is blocked ([ThibG](https://github.com/tootsuite/mastodon/pull/11499)) -- Fix avatars not being animated on hover when not logged in ([ThibG](https://github.com/tootsuite/mastodon/pull/11349)) -- Fix overzealous sanitization of HTML lists ([ThibG](https://github.com/tootsuite/mastodon/pull/11354)) -- Fix block crashing when a follow request exists ([ThibG](https://github.com/tootsuite/mastodon/pull/11288)) -- Fix backup service crashing when an attachment is missing ([ThibG](https://github.com/tootsuite/mastodon/pull/11241)) -- Fix account moderation action always sending e-mail notification ([Gargron](https://github.com/tootsuite/mastodon/pull/11242)) -- Fix swiping columns on mobile sometimes failing in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/11200)) -- Fix wrong actor URI being serialized into poll updates ([ThibG](https://github.com/tootsuite/mastodon/pull/11194)) -- Fix statsd UDP sockets not being cleaned up in Sidekiq ([Gargron](https://github.com/tootsuite/mastodon/pull/11230)) -- Fix expiration date of filters being set to "never" when editing them ([ThibG](https://github.com/tootsuite/mastodon/pull/11204)) -- Fix support for MP4 files that are actually M4V files ([Gargron](https://github.com/tootsuite/mastodon/pull/11210)) -- Fix `alerts` not being typecast correctly in push subscription in REST API ([Gargron](https://github.com/tootsuite/mastodon/pull/11343)) -- Fix some notices staying on unrelated pages ([ThibG](https://github.com/tootsuite/mastodon/pull/11364)) -- Fix unboosting sometimes preventing a boost from reappearing on feed ([ThibG](https://github.com/tootsuite/mastodon/pull/11405), [Gargron](https://github.com/tootsuite/mastodon/pull/11450)) -- Fix only one middle dot being recognized in hashtags ([Gargron](https://github.com/tootsuite/mastodon/pull/11345), [ThibG](https://github.com/tootsuite/mastodon/pull/11363)) -- Fix unnecessary SQL query performed on unauthenticated requests ([Gargron](https://github.com/tootsuite/mastodon/pull/11179)) -- Fix incorrect timestamp displayed on featured tags ([Kjwon15](https://github.com/tootsuite/mastodon/pull/11477)) -- Fix privacy dropdown active state when dropdown is placed on top of it ([ThibG](https://github.com/tootsuite/mastodon/pull/11495)) -- Fix filters not being applied to poll options ([ThibG](https://github.com/tootsuite/mastodon/pull/11174)) -- Fix keyboard navigation on various dropdowns ([ThibG](https://github.com/tootsuite/mastodon/pull/11511), [ThibG](https://github.com/tootsuite/mastodon/pull/11492), [ThibG](https://github.com/tootsuite/mastodon/pull/11491)) -- Fix keyboard navigation in modals ([ThibG](https://github.com/tootsuite/mastodon/pull/11493)) -- Fix image conversation being non-deterministic due to timestamps ([Gargron](https://github.com/tootsuite/mastodon/pull/11408)) -- Fix web UI performance ([ThibG](https://github.com/tootsuite/mastodon/pull/11211), [ThibG](https://github.com/tootsuite/mastodon/pull/11234)) -- Fix scrolling to compose form when not necessary in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/11246), [ThibG](https://github.com/tootsuite/mastodon/pull/11182)) -- Fix save button being enabled when list title is empty in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/11475)) -- Fix poll expiration not being pre-filled on delete & redraft in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/11203)) -- Fix content warning sometimes being set when not requested in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/11206)) +- Fix account domain block not clearing out notifications ([Gargron](https://github.com/mastodon/mastodon/pull/11393)) +- Fix incorrect locale sometimes being detected for browser ([Gargron](https://github.com/mastodon/mastodon/pull/8657)) +- Fix crash when saving invalid domain name ([Gargron](https://github.com/mastodon/mastodon/pull/11528)) +- Fix pinned statuses REST API returning pagination headers ([Gargron](https://github.com/mastodon/mastodon/pull/11526)) +- Fix "cancel follow request" button having unreadable text in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/11521)) +- Fix image uploads being blank when canvas read access is blocked ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11499)) +- Fix avatars not being animated on hover when not logged in ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11349)) +- Fix overzealous sanitization of HTML lists ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11354)) +- Fix block crashing when a follow request exists ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11288)) +- Fix backup service crashing when an attachment is missing ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11241)) +- Fix account moderation action always sending e-mail notification ([Gargron](https://github.com/mastodon/mastodon/pull/11242)) +- Fix swiping columns on mobile sometimes failing in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11200)) +- Fix wrong actor URI being serialized into poll updates ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11194)) +- Fix statsd UDP sockets not being cleaned up in Sidekiq ([Gargron](https://github.com/mastodon/mastodon/pull/11230)) +- Fix expiration date of filters being set to "never" when editing them ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11204)) +- Fix support for MP4 files that are actually M4V files ([Gargron](https://github.com/mastodon/mastodon/pull/11210)) +- Fix `alerts` not being typecast correctly in push subscription in REST API ([Gargron](https://github.com/mastodon/mastodon/pull/11343)) +- Fix some notices staying on unrelated pages ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11364)) +- Fix unboosting sometimes preventing a boost from reappearing on feed ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11405), [Gargron](https://github.com/mastodon/mastodon/pull/11450)) +- Fix only one middle dot being recognized in hashtags ([Gargron](https://github.com/mastodon/mastodon/pull/11345), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/11363)) +- Fix unnecessary SQL query performed on unauthenticated requests ([Gargron](https://github.com/mastodon/mastodon/pull/11179)) +- Fix incorrect timestamp displayed on featured tags ([Kjwon15](https://github.com/mastodon/mastodon/pull/11477)) +- Fix privacy dropdown active state when dropdown is placed on top of it ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11495)) +- Fix filters not being applied to poll options ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11174)) +- Fix keyboard navigation on various dropdowns ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11511), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/11492), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/11491)) +- Fix keyboard navigation in modals ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11493)) +- Fix image conversation being non-deterministic due to timestamps ([Gargron](https://github.com/mastodon/mastodon/pull/11408)) +- Fix web UI performance ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11211), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/11234)) +- Fix scrolling to compose form when not necessary in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11246), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/11182)) +- Fix save button being enabled when list title is empty in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11475)) +- Fix poll expiration not being pre-filled on delete & redraft in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11203)) +- Fix content warning sometimes being set when not requested in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11206)) ### Security -- Fix invites not being disabled upon account suspension ([ThibG](https://github.com/tootsuite/mastodon/pull/11412)) -- Fix blocked domains still being able to fill database with account records ([Gargron](https://github.com/tootsuite/mastodon/pull/11219)) +- Fix invites not being disabled upon account suspension ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11412)) +- Fix blocked domains still being able to fill database with account records ([Gargron](https://github.com/mastodon/mastodon/pull/11219)) ## [2.9.2] - 2019-06-22 ### Added -- Add `short_description` and `approval_required` to `GET /api/v1/instance` ([Gargron](https://github.com/tootsuite/mastodon/pull/11146)) +- Add `short_description` and `approval_required` to `GET /api/v1/instance` ([Gargron](https://github.com/mastodon/mastodon/pull/11146)) ### Changed -- Change camera icon to paperclip icon in upload form ([koyuawsmbrtn](https://github.com/tootsuite/mastodon/pull/11149)) +- Change camera icon to paperclip icon in upload form ([koyuawsmbrtn](https://github.com/mastodon/mastodon/pull/11149)) ### Fixed -- Fix audio-only OGG and WebM files not being processed as such ([Gargron](https://github.com/tootsuite/mastodon/pull/11151)) -- Fix audio not being downloaded from remote servers ([Gargron](https://github.com/tootsuite/mastodon/pull/11145)) +- Fix audio-only OGG and WebM files not being processed as such ([Gargron](https://github.com/mastodon/mastodon/pull/11151)) +- Fix audio not being downloaded from remote servers ([Gargron](https://github.com/mastodon/mastodon/pull/11145)) ## [2.9.1] - 2019-06-22 ### Added -- Add moderation API ([Gargron](https://github.com/tootsuite/mastodon/pull/9387)) -- Add audio uploads ([Gargron](https://github.com/tootsuite/mastodon/pull/11123), [Gargron](https://github.com/tootsuite/mastodon/pull/11141)) +- Add moderation API ([Gargron](https://github.com/mastodon/mastodon/pull/9387)) +- Add audio uploads ([Gargron](https://github.com/mastodon/mastodon/pull/11123), [Gargron](https://github.com/mastodon/mastodon/pull/11141)) ### Changed -- Change domain blocks to automatically support subdomains ([Gargron](https://github.com/tootsuite/mastodon/pull/11138)) -- Change Nanobox configuration to bring it up to date ([danhunsaker](https://github.com/tootsuite/mastodon/pull/11083)) +- Change domain blocks to automatically support subdomains ([Gargron](https://github.com/mastodon/mastodon/pull/11138)) +- Change Nanobox configuration to bring it up to date ([danhunsaker](https://github.com/mastodon/mastodon/pull/11083)) ### Removed -- Remove expensive counters from federation page in admin UI ([Gargron](https://github.com/tootsuite/mastodon/pull/11139)) +- Remove expensive counters from federation page in admin UI ([Gargron](https://github.com/mastodon/mastodon/pull/11139)) ### Fixed -- Fix converted media being saved with original extension and mime type ([Gargron](https://github.com/tootsuite/mastodon/pull/11130)) -- Fix layout of identity proofs settings ([acid-chicken](https://github.com/tootsuite/mastodon/pull/11126)) -- Fix active scope only returning suspended users ([ThibG](https://github.com/tootsuite/mastodon/pull/11111)) -- Fix sanitizer making block level elements unreadable ([Gargron](https://github.com/tootsuite/mastodon/pull/10836)) -- Fix label for site theme not being translated in admin UI ([palindromordnilap](https://github.com/tootsuite/mastodon/pull/11121)) -- Fix statuses not being filtered irreversibly in web UI under some circumstances ([ThibG](https://github.com/tootsuite/mastodon/pull/11113)) -- Fix scrolling behaviour in compose form ([ThibG](https://github.com/tootsuite/mastodon/pull/11093)) +- Fix converted media being saved with original extension and mime type ([Gargron](https://github.com/mastodon/mastodon/pull/11130)) +- Fix layout of identity proofs settings ([acid-chicken](https://github.com/mastodon/mastodon/pull/11126)) +- Fix active scope only returning suspended users ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11111)) +- Fix sanitizer making block level elements unreadable ([Gargron](https://github.com/mastodon/mastodon/pull/10836)) +- Fix label for site theme not being translated in admin UI ([palindromordnilap](https://github.com/mastodon/mastodon/pull/11121)) +- Fix statuses not being filtered irreversibly in web UI under some circumstances ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11113)) +- Fix scrolling behaviour in compose form ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11093)) ## [2.9.0] - 2019-06-13 ### Added -- **Add single-column mode in web UI** ([Gargron](https://github.com/tootsuite/mastodon/pull/10807), [Gargron](https://github.com/tootsuite/mastodon/pull/10848), [Gargron](https://github.com/tootsuite/mastodon/pull/11003), [Gargron](https://github.com/tootsuite/mastodon/pull/10961), [Hanage999](https://github.com/tootsuite/mastodon/pull/10915), [noellabo](https://github.com/tootsuite/mastodon/pull/10917), [abcang](https://github.com/tootsuite/mastodon/pull/10859), [Gargron](https://github.com/tootsuite/mastodon/pull/10820), [Gargron](https://github.com/tootsuite/mastodon/pull/10835), [Gargron](https://github.com/tootsuite/mastodon/pull/10809), [Gargron](https://github.com/tootsuite/mastodon/pull/10963), [noellabo](https://github.com/tootsuite/mastodon/pull/10883), [Hanage999](https://github.com/tootsuite/mastodon/pull/10839)) -- Add waiting time to the list of pending accounts in admin UI ([Gargron](https://github.com/tootsuite/mastodon/pull/10985)) -- Add a keyboard shortcut to hide/show media in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/10647), [Gargron](https://github.com/tootsuite/mastodon/pull/10838), [ThibG](https://github.com/tootsuite/mastodon/pull/10872)) -- Add `account_id` param to `GET /api/v1/notifications` ([pwoolcoc](https://github.com/tootsuite/mastodon/pull/10796)) -- Add confirmation modal for unboosting toots in web UI ([aurelien-reeves](https://github.com/tootsuite/mastodon/pull/10287)) -- Add emoji suggestions to content warning and poll option fields in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/10555)) -- Add `source` attribute to response of `DELETE /api/v1/statuses/:id` ([ThibG](https://github.com/tootsuite/mastodon/pull/10669)) -- Add some caching for HTML versions of public status pages ([ThibG](https://github.com/tootsuite/mastodon/pull/10701)) -- Add button to conveniently copy OAuth code ([ThibG](https://github.com/tootsuite/mastodon/pull/11065)) +- **Add single-column mode in web UI** ([Gargron](https://github.com/mastodon/mastodon/pull/10807), [Gargron](https://github.com/mastodon/mastodon/pull/10848), [Gargron](https://github.com/mastodon/mastodon/pull/11003), [Gargron](https://github.com/mastodon/mastodon/pull/10961), [Hanage999](https://github.com/mastodon/mastodon/pull/10915), [noellabo](https://github.com/mastodon/mastodon/pull/10917), [abcang](https://github.com/mastodon/mastodon/pull/10859), [Gargron](https://github.com/mastodon/mastodon/pull/10820), [Gargron](https://github.com/mastodon/mastodon/pull/10835), [Gargron](https://github.com/mastodon/mastodon/pull/10809), [Gargron](https://github.com/mastodon/mastodon/pull/10963), [noellabo](https://github.com/mastodon/mastodon/pull/10883), [Hanage999](https://github.com/mastodon/mastodon/pull/10839)) +- Add waiting time to the list of pending accounts in admin UI ([Gargron](https://github.com/mastodon/mastodon/pull/10985)) +- Add a keyboard shortcut to hide/show media in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10647), [Gargron](https://github.com/mastodon/mastodon/pull/10838), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10872)) +- Add `account_id` param to `GET /api/v1/notifications` ([pwoolcoc](https://github.com/mastodon/mastodon/pull/10796)) +- Add confirmation modal for unboosting toots in web UI ([aurelien-reeves](https://github.com/mastodon/mastodon/pull/10287)) +- Add emoji suggestions to content warning and poll option fields in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10555)) +- Add `source` attribute to response of `DELETE /api/v1/statuses/:id` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10669)) +- Add some caching for HTML versions of public status pages ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10701)) +- Add button to conveniently copy OAuth code ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11065)) ### Changed -- **Change default layout to single column in web UI** ([Gargron](https://github.com/tootsuite/mastodon/pull/10847)) -- **Change light theme** ([Gargron](https://github.com/tootsuite/mastodon/pull/10992), [Gargron](https://github.com/tootsuite/mastodon/pull/10996), [yuzulabo](https://github.com/tootsuite/mastodon/pull/10754), [Gargron](https://github.com/tootsuite/mastodon/pull/10845)) -- **Change preferences page into appearance, notifications, and other** ([Gargron](https://github.com/tootsuite/mastodon/pull/10977), [Gargron](https://github.com/tootsuite/mastodon/pull/10988)) -- Change priority of delete activity forwards for replies and reblogs ([Gargron](https://github.com/tootsuite/mastodon/pull/11002)) -- Change Mastodon logo to use primary text color of the given theme ([Gargron](https://github.com/tootsuite/mastodon/pull/10994)) -- Change reblogs counter to be updated when boosted privately ([Gargron](https://github.com/tootsuite/mastodon/pull/10964)) -- Change bio limit from 160 to 500 characters ([trwnh](https://github.com/tootsuite/mastodon/pull/10790)) -- Change API rate limiting to reduce allowed unauthenticated requests ([ThibG](https://github.com/tootsuite/mastodon/pull/10860), [hinaloe](https://github.com/tootsuite/mastodon/pull/10868), [mayaeh](https://github.com/tootsuite/mastodon/pull/10867)) -- Change help text of `tootctl emoji import` command to specify a gzipped TAR archive is required ([dariusk](https://github.com/tootsuite/mastodon/pull/11000)) -- Change web UI to hide poll options behind content warnings ([ThibG](https://github.com/tootsuite/mastodon/pull/10983)) -- Change silencing to ensure local effects and remote effects are the same for silenced local users ([ThibG](https://github.com/tootsuite/mastodon/pull/10575)) -- Change `tootctl domains purge` to remove custom emoji as well ([Kjwon15](https://github.com/tootsuite/mastodon/pull/10721)) -- Change Docker image to keep `apt` working ([SuperSandro2000](https://github.com/tootsuite/mastodon/pull/10830)) +- **Change default layout to single column in web UI** ([Gargron](https://github.com/mastodon/mastodon/pull/10847)) +- **Change light theme** ([Gargron](https://github.com/mastodon/mastodon/pull/10992), [Gargron](https://github.com/mastodon/mastodon/pull/10996), [yuzulabo](https://github.com/mastodon/mastodon/pull/10754), [Gargron](https://github.com/mastodon/mastodon/pull/10845)) +- **Change preferences page into appearance, notifications, and other** ([Gargron](https://github.com/mastodon/mastodon/pull/10977), [Gargron](https://github.com/mastodon/mastodon/pull/10988)) +- Change priority of delete activity forwards for replies and reblogs ([Gargron](https://github.com/mastodon/mastodon/pull/11002)) +- Change Mastodon logo to use primary text color of the given theme ([Gargron](https://github.com/mastodon/mastodon/pull/10994)) +- Change reblogs counter to be updated when boosted privately ([Gargron](https://github.com/mastodon/mastodon/pull/10964)) +- Change bio limit from 160 to 500 characters ([trwnh](https://github.com/mastodon/mastodon/pull/10790)) +- Change API rate limiting to reduce allowed unauthenticated requests ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10860), [hinaloe](https://github.com/mastodon/mastodon/pull/10868), [mayaeh](https://github.com/mastodon/mastodon/pull/10867)) +- Change help text of `tootctl emoji import` command to specify a gzipped TAR archive is required ([dariusk](https://github.com/mastodon/mastodon/pull/11000)) +- Change web UI to hide poll options behind content warnings ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10983)) +- Change silencing to ensure local effects and remote effects are the same for silenced local users ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10575)) +- Change `tootctl domains purge` to remove custom emoji as well ([Kjwon15](https://github.com/mastodon/mastodon/pull/10721)) +- Change Docker image to keep `apt` working ([SuperSandro2000](https://github.com/mastodon/mastodon/pull/10830)) ### Removed -- Remove `dist-upgrade` from Docker image ([SuperSandro2000](https://github.com/tootsuite/mastodon/pull/10822)) +- Remove `dist-upgrade` from Docker image ([SuperSandro2000](https://github.com/mastodon/mastodon/pull/10822)) ### Fixed -- Fix RTL layout not being RTL within the columns area in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/10990)) -- Fix display of alternative text when a media attachment is not available in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/10981)) -- Fix not being able to directly switch between list timelines in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/10973)) -- Fix media sensitivity not being maintained in delete & redraft in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/10980)) -- Fix emoji picker being always displayed in web UI ([noellabo](https://github.com/tootsuite/mastodon/pull/10979), [yuzulabo](https://github.com/tootsuite/mastodon/pull/10801), [wcpaez](https://github.com/tootsuite/mastodon/pull/10978)) -- Fix potential private status leak through caching ([ThibG](https://github.com/tootsuite/mastodon/pull/10969)) -- Fix refreshing featured toots when the new collection is empty in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/10971)) -- Fix undoing domain block also undoing individual moderation on users from before the domain block ([ThibG](https://github.com/tootsuite/mastodon/pull/10660)) -- Fix time not being local in the audit log ([yuzulabo](https://github.com/tootsuite/mastodon/pull/10751)) -- Fix statuses removed by moderation re-appearing on subsequent fetches ([Kjwon15](https://github.com/tootsuite/mastodon/pull/10732)) -- Fix misattribution of inlined announces if `attributedTo` isn't present in ActivityPub ([ThibG](https://github.com/tootsuite/mastodon/pull/10967)) -- Fix `GET /api/v1/polls/:id` not requiring authentication for non-public polls ([Gargron](https://github.com/tootsuite/mastodon/pull/10960)) -- Fix handling of blank poll options in ActivityPub ([ThibG](https://github.com/tootsuite/mastodon/pull/10946)) -- Fix avatar preview aspect ratio on edit profile page ([Kjwon15](https://github.com/tootsuite/mastodon/pull/10931)) -- Fix web push notifications not being sent for polls ([ThibG](https://github.com/tootsuite/mastodon/pull/10864)) -- Fix cut off letters in last paragraph of statuses in web UI ([ariasuni](https://github.com/tootsuite/mastodon/pull/10821)) -- Fix list not being automatically unpinned when it returns 404 in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/11045)) -- Fix login sometimes redirecting to paths that are not pages ([Gargron](https://github.com/tootsuite/mastodon/pull/11019)) +- Fix RTL layout not being RTL within the columns area in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/10990)) +- Fix display of alternative text when a media attachment is not available in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10981)) +- Fix not being able to directly switch between list timelines in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/10973)) +- Fix media sensitivity not being maintained in delete & redraft in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10980)) +- Fix emoji picker being always displayed in web UI ([noellabo](https://github.com/mastodon/mastodon/pull/10979), [yuzulabo](https://github.com/mastodon/mastodon/pull/10801), [wcpaez](https://github.com/mastodon/mastodon/pull/10978)) +- Fix potential private status leak through caching ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10969)) +- Fix refreshing featured toots when the new collection is empty in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10971)) +- Fix undoing domain block also undoing individual moderation on users from before the domain block ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10660)) +- Fix time not being local in the audit log ([yuzulabo](https://github.com/mastodon/mastodon/pull/10751)) +- Fix statuses removed by moderation re-appearing on subsequent fetches ([Kjwon15](https://github.com/mastodon/mastodon/pull/10732)) +- Fix misattribution of inlined announces if `attributedTo` isn't present in ActivityPub ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10967)) +- Fix `GET /api/v1/polls/:id` not requiring authentication for non-public polls ([Gargron](https://github.com/mastodon/mastodon/pull/10960)) +- Fix handling of blank poll options in ActivityPub ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10946)) +- Fix avatar preview aspect ratio on edit profile page ([Kjwon15](https://github.com/mastodon/mastodon/pull/10931)) +- Fix web push notifications not being sent for polls ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10864)) +- Fix cut off letters in last paragraph of statuses in web UI ([ariasuni](https://github.com/mastodon/mastodon/pull/10821)) +- Fix list not being automatically unpinned when it returns 404 in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/11045)) +- Fix login sometimes redirecting to paths that are not pages ([Gargron](https://github.com/mastodon/mastodon/pull/11019)) ## [2.8.4] - 2019-05-24 ### Fixed -- Fix delivery not retrying on some inbox errors that should be retriable ([ThibG](https://github.com/tootsuite/mastodon/pull/10812)) -- Fix unnecessary 5 minute cooldowns on signature verifications in some cases ([ThibG](https://github.com/tootsuite/mastodon/pull/10813)) -- Fix possible race condition when processing statuses ([ThibG](https://github.com/tootsuite/mastodon/pull/10815)) +- Fix delivery not retrying on some inbox errors that should be retriable ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10812)) +- Fix unnecessary 5 minute cooldowns on signature verifications in some cases ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10813)) +- Fix possible race condition when processing statuses ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10815)) ### Security -- Require specific OAuth scopes for specific endpoints of the streaming API, instead of merely requiring a token for all endpoints, and allow using WebSockets protocol negotiation to specify the access token instead of using a query string ([ThibG](https://github.com/tootsuite/mastodon/pull/10818)) +- Require specific OAuth scopes for specific endpoints of the streaming API, instead of merely requiring a token for all endpoints, and allow using WebSockets protocol negotiation to specify the access token instead of using a query string ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10818)) ## [2.8.3] - 2019-05-19 ### Added -- Add `og:image:alt` OpenGraph tag ([BenLubar](https://github.com/tootsuite/mastodon/pull/10779)) -- Add clickable area below avatar in statuses in web UI ([Dar13](https://github.com/tootsuite/mastodon/pull/10766)) -- Add crossed-out eye icon on account gallery in web UI ([Kjwon15](https://github.com/tootsuite/mastodon/pull/10715)) -- Add media description tooltip to thumbnails in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/10713)) +- Add `og:image:alt` OpenGraph tag ([BenLubar](https://github.com/mastodon/mastodon/pull/10779)) +- Add clickable area below avatar in statuses in web UI ([Dar13](https://github.com/mastodon/mastodon/pull/10766)) +- Add crossed-out eye icon on account gallery in web UI ([Kjwon15](https://github.com/mastodon/mastodon/pull/10715)) +- Add media description tooltip to thumbnails in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10713)) ### Changed -- Change "mark as sensitive" button into a checkbox for clarity ([ThibG](https://github.com/tootsuite/mastodon/pull/10748)) +- Change "mark as sensitive" button into a checkbox for clarity ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10748)) ### Fixed -- Fix bug allowing users to publicly boost their private statuses ([ThibG](https://github.com/tootsuite/mastodon/pull/10775), [ThibG](https://github.com/tootsuite/mastodon/pull/10783)) -- Fix performance in formatter by a little ([ThibG](https://github.com/tootsuite/mastodon/pull/10765)) -- Fix some colors in the light theme ([yuzulabo](https://github.com/tootsuite/mastodon/pull/10754)) -- Fix some colors of the high contrast theme ([yuzulabo](https://github.com/tootsuite/mastodon/pull/10711)) -- Fix ambivalent active state of poll refresh button in web UI ([MaciekBaron](https://github.com/tootsuite/mastodon/pull/10720)) -- Fix duplicate posting being possible from web UI ([hinaloe](https://github.com/tootsuite/mastodon/pull/10785)) -- Fix "invited by" not showing up in admin UI ([ThibG](https://github.com/tootsuite/mastodon/pull/10791)) +- Fix bug allowing users to publicly boost their private statuses ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10775), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10783)) +- Fix performance in formatter by a little ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10765)) +- Fix some colors in the light theme ([yuzulabo](https://github.com/mastodon/mastodon/pull/10754)) +- Fix some colors of the high contrast theme ([yuzulabo](https://github.com/mastodon/mastodon/pull/10711)) +- Fix ambivalent active state of poll refresh button in web UI ([MaciekBaron](https://github.com/mastodon/mastodon/pull/10720)) +- Fix duplicate posting being possible from web UI ([hinaloe](https://github.com/mastodon/mastodon/pull/10785)) +- Fix "invited by" not showing up in admin UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10791)) ## [2.8.2] - 2019-05-05 ### Added -- Add `SOURCE_TAG` environment variable ([ushitora-anqou](https://github.com/tootsuite/mastodon/pull/10698)) +- Add `SOURCE_TAG` environment variable ([ushitora-anqou](https://github.com/mastodon/mastodon/pull/10698)) ### Fixed -- Fix cropped hero image on frontpage ([BaptisteGelez](https://github.com/tootsuite/mastodon/pull/10702)) -- Fix blurhash gem not compiling on some operating systems ([Gargron](https://github.com/tootsuite/mastodon/pull/10700)) -- Fix unexpected CSS animations in some browsers ([ThibG](https://github.com/tootsuite/mastodon/pull/10699)) -- Fix closing video modal scrolling timelines to top ([ThibG](https://github.com/tootsuite/mastodon/pull/10695)) +- Fix cropped hero image on frontpage ([BaptisteGelez](https://github.com/mastodon/mastodon/pull/10702)) +- Fix blurhash gem not compiling on some operating systems ([Gargron](https://github.com/mastodon/mastodon/pull/10700)) +- Fix unexpected CSS animations in some browsers ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10699)) +- Fix closing video modal scrolling timelines to top ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10695)) ## [2.8.1] - 2019-05-04 ### Added -- Add link to existing domain block when trying to block an already-blocked domain ([ThibG](https://github.com/tootsuite/mastodon/pull/10663)) -- Add button to view context to media modal when opened from account gallery in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/10676)) -- Add ability to create multiple-choice polls in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/10603)) -- Add `GITHUB_REPOSITORY` and `SOURCE_BASE_URL` environment variables ([rosylilly](https://github.com/tootsuite/mastodon/pull/10600)) -- Add `/interact/` paths to `robots.txt` ([ThibG](https://github.com/tootsuite/mastodon/pull/10666)) -- Add `blurhash` to the Attachment entity in the REST API ([Gargron](https://github.com/tootsuite/mastodon/pull/10630)) +- Add link to existing domain block when trying to block an already-blocked domain ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10663)) +- Add button to view context to media modal when opened from account gallery in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/10676)) +- Add ability to create multiple-choice polls in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10603)) +- Add `GITHUB_REPOSITORY` and `SOURCE_BASE_URL` environment variables ([rosylilly](https://github.com/mastodon/mastodon/pull/10600)) +- Add `/interact/` paths to `robots.txt` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10666)) +- Add `blurhash` to the Attachment entity in the REST API ([Gargron](https://github.com/mastodon/mastodon/pull/10630)) ### Changed -- Change hidden media to be shown as a blurhash-based colorful gradient instead of a black box in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/10630)) -- Change rejected media to be shown as a blurhash-based gradient instead of a list of filenames in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/10630)) -- Change e-mail whitelist/blacklist to not be checked when invited ([Gargron](https://github.com/tootsuite/mastodon/pull/10683)) -- Change cache header of REST API results to no-cache ([ThibG](https://github.com/tootsuite/mastodon/pull/10655)) -- Change the "mark media as sensitive" button to be more obvious in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/10673), [Gargron](https://github.com/tootsuite/mastodon/pull/10682)) -- Change account gallery in web UI to display 3 columns, open media modal ([Gargron](https://github.com/tootsuite/mastodon/pull/10667), [Gargron](https://github.com/tootsuite/mastodon/pull/10674)) +- Change hidden media to be shown as a blurhash-based colorful gradient instead of a black box in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/10630)) +- Change rejected media to be shown as a blurhash-based gradient instead of a list of filenames in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/10630)) +- Change e-mail whitelist/blacklist to not be checked when invited ([Gargron](https://github.com/mastodon/mastodon/pull/10683)) +- Change cache header of REST API results to no-cache ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10655)) +- Change the "mark media as sensitive" button to be more obvious in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/10673), [Gargron](https://github.com/mastodon/mastodon/pull/10682)) +- Change account gallery in web UI to display 3 columns, open media modal ([Gargron](https://github.com/mastodon/mastodon/pull/10667), [Gargron](https://github.com/mastodon/mastodon/pull/10674)) ### Fixed -- Fix LDAP/PAM/SAML/CAS users not being pre-approved ([Gargron](https://github.com/tootsuite/mastodon/pull/10621)) -- Fix accounts created through tootctl not being always pre-approved ([Gargron](https://github.com/tootsuite/mastodon/pull/10684)) -- Fix Sidekiq retrying ActivityPub processing jobs that fail validation ([ThibG](https://github.com/tootsuite/mastodon/pull/10614)) -- Fix toots not being scrolled into view sometimes through keyboard selection ([ThibG](https://github.com/tootsuite/mastodon/pull/10593)) -- Fix expired invite links being usable to bypass approval mode ([ThibG](https://github.com/tootsuite/mastodon/pull/10657)) -- Fix not being able to save e-mail preference for new pending accounts ([Gargron](https://github.com/tootsuite/mastodon/pull/10622)) -- Fix upload progressbar when image resizing is involved ([ThibG](https://github.com/tootsuite/mastodon/pull/10632)) -- Fix block action not automatically cancelling pending follow request ([ThibG](https://github.com/tootsuite/mastodon/pull/10633)) -- Fix stoplight logging to stderr separate from Rails logger ([Gargron](https://github.com/tootsuite/mastodon/pull/10624)) -- Fix sign up button not saying sign up when invite is used ([Gargron](https://github.com/tootsuite/mastodon/pull/10623)) -- Fix health checks in Docker Compose configuration ([fabianonline](https://github.com/tootsuite/mastodon/pull/10553)) -- Fix modal items not being scrollable on touch devices ([kedamaDQ](https://github.com/tootsuite/mastodon/pull/10605)) -- Fix Keybase configuration using wrong domain when a web domain is used ([BenLubar](https://github.com/tootsuite/mastodon/pull/10565)) -- Fix avatar GIFs not being animated on-hover on public profiles ([hyenagirl64](https://github.com/tootsuite/mastodon/pull/10549)) -- Fix OpenGraph parser not understanding some valid property meta tags ([da2x](https://github.com/tootsuite/mastodon/pull/10604)) -- Fix wrong fonts being displayed when Roboto is installed on user's machine ([ThibG](https://github.com/tootsuite/mastodon/pull/10594)) -- Fix confirmation modals being too narrow for a secondary action button ([ThibG](https://github.com/tootsuite/mastodon/pull/10586)) +- Fix LDAP/PAM/SAML/CAS users not being pre-approved ([Gargron](https://github.com/mastodon/mastodon/pull/10621)) +- Fix accounts created through tootctl not being always pre-approved ([Gargron](https://github.com/mastodon/mastodon/pull/10684)) +- Fix Sidekiq retrying ActivityPub processing jobs that fail validation ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10614)) +- Fix toots not being scrolled into view sometimes through keyboard selection ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10593)) +- Fix expired invite links being usable to bypass approval mode ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10657)) +- Fix not being able to save e-mail preference for new pending accounts ([Gargron](https://github.com/mastodon/mastodon/pull/10622)) +- Fix upload progressbar when image resizing is involved ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10632)) +- Fix block action not automatically cancelling pending follow request ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10633)) +- Fix stoplight logging to stderr separate from Rails logger ([Gargron](https://github.com/mastodon/mastodon/pull/10624)) +- Fix sign up button not saying sign up when invite is used ([Gargron](https://github.com/mastodon/mastodon/pull/10623)) +- Fix health checks in Docker Compose configuration ([fabianonline](https://github.com/mastodon/mastodon/pull/10553)) +- Fix modal items not being scrollable on touch devices ([kedamaDQ](https://github.com/mastodon/mastodon/pull/10605)) +- Fix Keybase configuration using wrong domain when a web domain is used ([BenLubar](https://github.com/mastodon/mastodon/pull/10565)) +- Fix avatar GIFs not being animated on-hover on public profiles ([hyenagirl64](https://github.com/mastodon/mastodon/pull/10549)) +- Fix OpenGraph parser not understanding some valid property meta tags ([da2x](https://github.com/mastodon/mastodon/pull/10604)) +- Fix wrong fonts being displayed when Roboto is installed on user's machine ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10594)) +- Fix confirmation modals being too narrow for a secondary action button ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10586)) ## [2.8.0] - 2019-04-10 ### Added -- Add polls ([Gargron](https://github.com/tootsuite/mastodon/pull/10111), [ThibG](https://github.com/tootsuite/mastodon/pull/10155), [Gargron](https://github.com/tootsuite/mastodon/pull/10184), [ThibG](https://github.com/tootsuite/mastodon/pull/10196), [Gargron](https://github.com/tootsuite/mastodon/pull/10248), [ThibG](https://github.com/tootsuite/mastodon/pull/10255), [ThibG](https://github.com/tootsuite/mastodon/pull/10322), [Gargron](https://github.com/tootsuite/mastodon/pull/10138), [Gargron](https://github.com/tootsuite/mastodon/pull/10139), [Gargron](https://github.com/tootsuite/mastodon/pull/10144), [Gargron](https://github.com/tootsuite/mastodon/pull/10145),[Gargron](https://github.com/tootsuite/mastodon/pull/10146), [Gargron](https://github.com/tootsuite/mastodon/pull/10148), [Gargron](https://github.com/tootsuite/mastodon/pull/10151), [ThibG](https://github.com/tootsuite/mastodon/pull/10150), [Gargron](https://github.com/tootsuite/mastodon/pull/10168), [Gargron](https://github.com/tootsuite/mastodon/pull/10165), [Gargron](https://github.com/tootsuite/mastodon/pull/10172), [Gargron](https://github.com/tootsuite/mastodon/pull/10170), [Gargron](https://github.com/tootsuite/mastodon/pull/10171), [Gargron](https://github.com/tootsuite/mastodon/pull/10186), [Gargron](https://github.com/tootsuite/mastodon/pull/10189), [ThibG](https://github.com/tootsuite/mastodon/pull/10200), [rinsuki](https://github.com/tootsuite/mastodon/pull/10203), [Gargron](https://github.com/tootsuite/mastodon/pull/10213), [Gargron](https://github.com/tootsuite/mastodon/pull/10246), [Gargron](https://github.com/tootsuite/mastodon/pull/10265), [Gargron](https://github.com/tootsuite/mastodon/pull/10261), [ThibG](https://github.com/tootsuite/mastodon/pull/10333), [Gargron](https://github.com/tootsuite/mastodon/pull/10352), [ThibG](https://github.com/tootsuite/mastodon/pull/10140), [ThibG](https://github.com/tootsuite/mastodon/pull/10142), [ThibG](https://github.com/tootsuite/mastodon/pull/10141), [ThibG](https://github.com/tootsuite/mastodon/pull/10162), [ThibG](https://github.com/tootsuite/mastodon/pull/10161), [ThibG](https://github.com/tootsuite/mastodon/pull/10158), [ThibG](https://github.com/tootsuite/mastodon/pull/10156), [ThibG](https://github.com/tootsuite/mastodon/pull/10160), [Gargron](https://github.com/tootsuite/mastodon/pull/10185), [Gargron](https://github.com/tootsuite/mastodon/pull/10188), [ThibG](https://github.com/tootsuite/mastodon/pull/10195), [ThibG](https://github.com/tootsuite/mastodon/pull/10208), [Gargron](https://github.com/tootsuite/mastodon/pull/10187), [ThibG](https://github.com/tootsuite/mastodon/pull/10214), [ThibG](https://github.com/tootsuite/mastodon/pull/10209)) -- Add follows & followers managing UI ([Gargron](https://github.com/tootsuite/mastodon/pull/10268), [Gargron](https://github.com/tootsuite/mastodon/pull/10308), [Gargron](https://github.com/tootsuite/mastodon/pull/10404), [Gargron](https://github.com/tootsuite/mastodon/pull/10293)) -- Add identity proof integration with Keybase ([Gargron](https://github.com/tootsuite/mastodon/pull/10297), [xgess](https://github.com/tootsuite/mastodon/pull/10375), [Gargron](https://github.com/tootsuite/mastodon/pull/10338), [Gargron](https://github.com/tootsuite/mastodon/pull/10350), [Gargron](https://github.com/tootsuite/mastodon/pull/10414)) -- Add option to overwrite imported data instead of merging ([Gargron](https://github.com/tootsuite/mastodon/pull/9962)) -- Add featured hashtags to profiles ([Gargron](https://github.com/tootsuite/mastodon/pull/9755), [Gargron](https://github.com/tootsuite/mastodon/pull/10167), [Gargron](https://github.com/tootsuite/mastodon/pull/10249), [ThibG](https://github.com/tootsuite/mastodon/pull/10034)) -- Add admission-based registrations mode ([Gargron](https://github.com/tootsuite/mastodon/pull/10250), [ThibG](https://github.com/tootsuite/mastodon/pull/10269), [Gargron](https://github.com/tootsuite/mastodon/pull/10264), [ThibG](https://github.com/tootsuite/mastodon/pull/10321), [Gargron](https://github.com/tootsuite/mastodon/pull/10349), [Gargron](https://github.com/tootsuite/mastodon/pull/10469)) -- Add support for WebP uploads ([acid-chicken](https://github.com/tootsuite/mastodon/pull/9879)) -- Add "copy link" item to status action bars in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/9983)) -- Add list title editing in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/9748)) -- Add a "Block & Report" button to the block confirmation dialog in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/10360)) -- Add disappointed elephant when the page crashes in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/10275)) -- Add ability to upload multiple files at once in web UI ([tmm576](https://github.com/tootsuite/mastodon/pull/9856)) -- Add indication when you are not allowed to follow an account in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/10420), [Gargron](https://github.com/tootsuite/mastodon/pull/10491)) -- Add validations to admin settings to catch common mistakes ([Gargron](https://github.com/tootsuite/mastodon/pull/10348), [ThibG](https://github.com/tootsuite/mastodon/pull/10354)) -- Add `type`, `limit`, `offset`, `min_id`, `max_id`, `account_id` to search API ([Gargron](https://github.com/tootsuite/mastodon/pull/10091)) -- Add a preferences API so apps can share basic behaviours ([Gargron](https://github.com/tootsuite/mastodon/pull/10109)) -- Add `visibility` param to reblog REST API ([Gargron](https://github.com/tootsuite/mastodon/pull/9851), [ThibG](https://github.com/tootsuite/mastodon/pull/10302)) -- Add `allowfullscreen` attribute to OEmbed iframe ([rinsuki](https://github.com/tootsuite/mastodon/pull/10370)) -- Add `blocked_by` relationship to the REST API ([Gargron](https://github.com/tootsuite/mastodon/pull/10373)) -- Add `tootctl statuses remove` to sweep unreferenced statuses ([Gargron](https://github.com/tootsuite/mastodon/pull/10063)) -- Add `tootctl search deploy` to avoid ugly rake task syntax ([Gargron](https://github.com/tootsuite/mastodon/pull/10403)) -- Add `tootctl self-destruct` to shut down server gracefully ([Gargron](https://github.com/tootsuite/mastodon/pull/10367)) -- Add option to hide application used to toot ([ThibG](https://github.com/tootsuite/mastodon/pull/9897), [rinsuki](https://github.com/tootsuite/mastodon/pull/9994), [hinaloe](https://github.com/tootsuite/mastodon/pull/10086)) -- Add `DB_SSLMODE` configuration variable ([sascha-sl](https://github.com/tootsuite/mastodon/pull/10210)) -- Add click-to-copy UI to invites page ([Gargron](https://github.com/tootsuite/mastodon/pull/10259)) -- Add self-replies fetching ([ThibG](https://github.com/tootsuite/mastodon/pull/10106), [ThibG](https://github.com/tootsuite/mastodon/pull/10128), [ThibG](https://github.com/tootsuite/mastodon/pull/10175), [ThibG](https://github.com/tootsuite/mastodon/pull/10201)) -- Add rate limit for media proxy requests ([Gargron](https://github.com/tootsuite/mastodon/pull/10490)) -- Add `tootctl emoji purge` ([Gargron](https://github.com/tootsuite/mastodon/pull/10481)) -- Add `tootctl accounts approve` ([Gargron](https://github.com/tootsuite/mastodon/pull/10480)) -- Add `tootctl accounts reset-relationships` ([noellabo](https://github.com/tootsuite/mastodon/pull/10483)) +- Add polls ([Gargron](https://github.com/mastodon/mastodon/pull/10111), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10155), [Gargron](https://github.com/mastodon/mastodon/pull/10184), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10196), [Gargron](https://github.com/mastodon/mastodon/pull/10248), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10255), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10322), [Gargron](https://github.com/mastodon/mastodon/pull/10138), [Gargron](https://github.com/mastodon/mastodon/pull/10139), [Gargron](https://github.com/mastodon/mastodon/pull/10144), [Gargron](https://github.com/mastodon/mastodon/pull/10145),[Gargron](https://github.com/mastodon/mastodon/pull/10146), [Gargron](https://github.com/mastodon/mastodon/pull/10148), [Gargron](https://github.com/mastodon/mastodon/pull/10151), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10150), [Gargron](https://github.com/mastodon/mastodon/pull/10168), [Gargron](https://github.com/mastodon/mastodon/pull/10165), [Gargron](https://github.com/mastodon/mastodon/pull/10172), [Gargron](https://github.com/mastodon/mastodon/pull/10170), [Gargron](https://github.com/mastodon/mastodon/pull/10171), [Gargron](https://github.com/mastodon/mastodon/pull/10186), [Gargron](https://github.com/mastodon/mastodon/pull/10189), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10200), [rinsuki](https://github.com/mastodon/mastodon/pull/10203), [Gargron](https://github.com/mastodon/mastodon/pull/10213), [Gargron](https://github.com/mastodon/mastodon/pull/10246), [Gargron](https://github.com/mastodon/mastodon/pull/10265), [Gargron](https://github.com/mastodon/mastodon/pull/10261), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10333), [Gargron](https://github.com/mastodon/mastodon/pull/10352), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10140), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10142), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10141), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10162), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10161), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10158), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10156), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10160), [Gargron](https://github.com/mastodon/mastodon/pull/10185), [Gargron](https://github.com/mastodon/mastodon/pull/10188), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10195), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10208), [Gargron](https://github.com/mastodon/mastodon/pull/10187), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10214), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10209)) +- Add follows & followers managing UI ([Gargron](https://github.com/mastodon/mastodon/pull/10268), [Gargron](https://github.com/mastodon/mastodon/pull/10308), [Gargron](https://github.com/mastodon/mastodon/pull/10404), [Gargron](https://github.com/mastodon/mastodon/pull/10293)) +- Add identity proof integration with Keybase ([Gargron](https://github.com/mastodon/mastodon/pull/10297), [xgess](https://github.com/mastodon/mastodon/pull/10375), [Gargron](https://github.com/mastodon/mastodon/pull/10338), [Gargron](https://github.com/mastodon/mastodon/pull/10350), [Gargron](https://github.com/mastodon/mastodon/pull/10414)) +- Add option to overwrite imported data instead of merging ([Gargron](https://github.com/mastodon/mastodon/pull/9962)) +- Add featured hashtags to profiles ([Gargron](https://github.com/mastodon/mastodon/pull/9755), [Gargron](https://github.com/mastodon/mastodon/pull/10167), [Gargron](https://github.com/mastodon/mastodon/pull/10249), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10034)) +- Add admission-based registrations mode ([Gargron](https://github.com/mastodon/mastodon/pull/10250), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10269), [Gargron](https://github.com/mastodon/mastodon/pull/10264), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10321), [Gargron](https://github.com/mastodon/mastodon/pull/10349), [Gargron](https://github.com/mastodon/mastodon/pull/10469)) +- Add support for WebP uploads ([acid-chicken](https://github.com/mastodon/mastodon/pull/9879)) +- Add "copy link" item to status action bars in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/9983)) +- Add list title editing in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9748)) +- Add a "Block & Report" button to the block confirmation dialog in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10360)) +- Add disappointed elephant when the page crashes in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/10275)) +- Add ability to upload multiple files at once in web UI ([tmm576](https://github.com/mastodon/mastodon/pull/9856)) +- Add indication when you are not allowed to follow an account in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/10420), [Gargron](https://github.com/mastodon/mastodon/pull/10491)) +- Add validations to admin settings to catch common mistakes ([Gargron](https://github.com/mastodon/mastodon/pull/10348), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10354)) +- Add `type`, `limit`, `offset`, `min_id`, `max_id`, `account_id` to search API ([Gargron](https://github.com/mastodon/mastodon/pull/10091)) +- Add a preferences API so apps can share basic behaviours ([Gargron](https://github.com/mastodon/mastodon/pull/10109)) +- Add `visibility` param to reblog REST API ([Gargron](https://github.com/mastodon/mastodon/pull/9851), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10302)) +- Add `allowfullscreen` attribute to OEmbed iframe ([rinsuki](https://github.com/mastodon/mastodon/pull/10370)) +- Add `blocked_by` relationship to the REST API ([Gargron](https://github.com/mastodon/mastodon/pull/10373)) +- Add `tootctl statuses remove` to sweep unreferenced statuses ([Gargron](https://github.com/mastodon/mastodon/pull/10063)) +- Add `tootctl search deploy` to avoid ugly rake task syntax ([Gargron](https://github.com/mastodon/mastodon/pull/10403)) +- Add `tootctl self-destruct` to shut down server gracefully ([Gargron](https://github.com/mastodon/mastodon/pull/10367)) +- Add option to hide application used to toot ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9897), [rinsuki](https://github.com/mastodon/mastodon/pull/9994), [hinaloe](https://github.com/mastodon/mastodon/pull/10086)) +- Add `DB_SSLMODE` configuration variable ([sascha-sl](https://github.com/mastodon/mastodon/pull/10210)) +- Add click-to-copy UI to invites page ([Gargron](https://github.com/mastodon/mastodon/pull/10259)) +- Add self-replies fetching ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10106), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10128), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10175), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10201)) +- Add rate limit for media proxy requests ([Gargron](https://github.com/mastodon/mastodon/pull/10490)) +- Add `tootctl emoji purge` ([Gargron](https://github.com/mastodon/mastodon/pull/10481)) +- Add `tootctl accounts approve` ([Gargron](https://github.com/mastodon/mastodon/pull/10480)) +- Add `tootctl accounts reset-relationships` ([noellabo](https://github.com/mastodon/mastodon/pull/10483)) ### Changed -- Change design of landing page ([Gargron](https://github.com/tootsuite/mastodon/pull/10232), [Gargron](https://github.com/tootsuite/mastodon/pull/10260), [ThibG](https://github.com/tootsuite/mastodon/pull/10284), [ThibG](https://github.com/tootsuite/mastodon/pull/10291), [koyuawsmbrtn](https://github.com/tootsuite/mastodon/pull/10356), [Gargron](https://github.com/tootsuite/mastodon/pull/10245)) -- Change design of profile column in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/10337), [Aditoo17](https://github.com/tootsuite/mastodon/pull/10387), [ThibG](https://github.com/tootsuite/mastodon/pull/10390), [mayaeh](https://github.com/tootsuite/mastodon/pull/10379), [ThibG](https://github.com/tootsuite/mastodon/pull/10411)) -- Change language detector threshold from 140 characters to 4 words ([Gargron](https://github.com/tootsuite/mastodon/pull/10376)) -- Change language detector to always kick in for non-latin alphabets ([Gargron](https://github.com/tootsuite/mastodon/pull/10276)) -- Change icons of features on admin dashboard ([Gargron](https://github.com/tootsuite/mastodon/pull/10366)) -- Change DNS timeouts from 1s to 5s ([ThibG](https://github.com/tootsuite/mastodon/pull/10238)) -- Change Docker image to use Ubuntu with jemalloc ([Sir-Boops](https://github.com/tootsuite/mastodon/pull/10100), [BenLubar](https://github.com/tootsuite/mastodon/pull/10212)) -- Change public pages to be cacheable by proxies ([BenLubar](https://github.com/tootsuite/mastodon/pull/9059)) -- Change the 410 gone response for suspended accounts to be cacheable by proxies ([ThibG](https://github.com/tootsuite/mastodon/pull/10339)) -- Change web UI to not not empty timeline of blocked users on block ([ThibG](https://github.com/tootsuite/mastodon/pull/10359)) -- Change JSON serializer to remove unused `@context` values ([Gargron](https://github.com/tootsuite/mastodon/pull/10378)) -- Change GIFV file size limit to be the same as for other videos ([rinsuki](https://github.com/tootsuite/mastodon/pull/9924)) -- Change Webpack to not use @babel/preset-env to compile node_modules ([ykzts](https://github.com/tootsuite/mastodon/pull/10289)) -- Change web UI to use new Web Share Target API ([gol-cha](https://github.com/tootsuite/mastodon/pull/9963)) -- Change ActivityPub reports to have persistent URIs ([ThibG](https://github.com/tootsuite/mastodon/pull/10303)) -- Change `tootctl accounts cull --dry-run` to list accounts that would be deleted ([BenLubar](https://github.com/tootsuite/mastodon/pull/10460)) -- Change format of CSV exports of follows and mutes to include extra settings ([ThibG](https://github.com/tootsuite/mastodon/pull/10495), [ThibG](https://github.com/tootsuite/mastodon/pull/10335)) -- Change ActivityPub collections to be cacheable by proxies ([ThibG](https://github.com/tootsuite/mastodon/pull/10467)) -- Change REST API and public profiles to not return follows/followers for users that have blocked you ([Gargron](https://github.com/tootsuite/mastodon/pull/10491)) -- Change the groupings of menu items in settings navigation ([Gargron](https://github.com/tootsuite/mastodon/pull/10533)) +- Change design of landing page ([Gargron](https://github.com/mastodon/mastodon/pull/10232), [Gargron](https://github.com/mastodon/mastodon/pull/10260), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10284), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10291), [koyuawsmbrtn](https://github.com/mastodon/mastodon/pull/10356), [Gargron](https://github.com/mastodon/mastodon/pull/10245)) +- Change design of profile column in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/10337), [Aditoo17](https://github.com/mastodon/mastodon/pull/10387), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10390), [mayaeh](https://github.com/mastodon/mastodon/pull/10379), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10411)) +- Change language detector threshold from 140 characters to 4 words ([Gargron](https://github.com/mastodon/mastodon/pull/10376)) +- Change language detector to always kick in for non-latin alphabets ([Gargron](https://github.com/mastodon/mastodon/pull/10276)) +- Change icons of features on admin dashboard ([Gargron](https://github.com/mastodon/mastodon/pull/10366)) +- Change DNS timeouts from 1s to 5s ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10238)) +- Change Docker image to use Ubuntu with jemalloc ([Sir-Boops](https://github.com/mastodon/mastodon/pull/10100), [BenLubar](https://github.com/mastodon/mastodon/pull/10212)) +- Change public pages to be cacheable by proxies ([BenLubar](https://github.com/mastodon/mastodon/pull/9059)) +- Change the 410 gone response for suspended accounts to be cacheable by proxies ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10339)) +- Change web UI to not not empty timeline of blocked users on block ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10359)) +- Change JSON serializer to remove unused `@context` values ([Gargron](https://github.com/mastodon/mastodon/pull/10378)) +- Change GIFV file size limit to be the same as for other videos ([rinsuki](https://github.com/mastodon/mastodon/pull/9924)) +- Change Webpack to not use @babel/preset-env to compile node_modules ([ykzts](https://github.com/mastodon/mastodon/pull/10289)) +- Change web UI to use new Web Share Target API ([gol-cha](https://github.com/mastodon/mastodon/pull/9963)) +- Change ActivityPub reports to have persistent URIs ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10303)) +- Change `tootctl accounts cull --dry-run` to list accounts that would be deleted ([BenLubar](https://github.com/mastodon/mastodon/pull/10460)) +- Change format of CSV exports of follows and mutes to include extra settings ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10495), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10335)) +- Change ActivityPub collections to be cacheable by proxies ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10467)) +- Change REST API and public profiles to not return follows/followers for users that have blocked you ([Gargron](https://github.com/mastodon/mastodon/pull/10491)) +- Change the groupings of menu items in settings navigation ([Gargron](https://github.com/mastodon/mastodon/pull/10533)) ### Removed -- Remove zopfli compression to speed up Webpack from 6min to 1min ([nolanlawson](https://github.com/tootsuite/mastodon/pull/10288)) -- Remove stats.json generation to speed up Webpack ([nolanlawson](https://github.com/tootsuite/mastodon/pull/10290)) +- Remove zopfli compression to speed up Webpack from 6min to 1min ([nolanlawson](https://github.com/mastodon/mastodon/pull/10288)) +- Remove stats.json generation to speed up Webpack ([nolanlawson](https://github.com/mastodon/mastodon/pull/10290)) ### Fixed -- Fix public timelines being broken by new toots when they are not mounted in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/10131)) -- Fix quick filter settings not being saved when selecting a different filter in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/10296)) -- Fix remote interaction dialogs being indexed by search engines ([Gargron](https://github.com/tootsuite/mastodon/pull/10240)) -- Fix maxed-out invites not showing up as expired in UI ([Gargron](https://github.com/tootsuite/mastodon/pull/10274)) -- Fix scrollbar styles on compose textarea ([Gargron](https://github.com/tootsuite/mastodon/pull/10292)) -- Fix timeline merge workers being queued for remote users ([Gargron](https://github.com/tootsuite/mastodon/pull/10355)) -- Fix alternative relay support regression ([Gargron](https://github.com/tootsuite/mastodon/pull/10398)) -- Fix trying to fetch keys of unknown accounts on a self-delete from them ([ThibG](https://github.com/tootsuite/mastodon/pull/10326)) -- Fix CAS `:service_validate_url` option ([enewhuis](https://github.com/tootsuite/mastodon/pull/10328)) -- Fix race conditions when creating backups ([ThibG](https://github.com/tootsuite/mastodon/pull/10234)) -- Fix whitespace not being stripped out of username before validation ([aurelien-reeves](https://github.com/tootsuite/mastodon/pull/10239)) -- Fix n+1 query when deleting status ([Gargron](https://github.com/tootsuite/mastodon/pull/10247)) -- Fix exiting follows not being rejected when suspending a remote account ([ThibG](https://github.com/tootsuite/mastodon/pull/10230)) -- Fix the underlying button element in a disabled icon button not being disabled ([ThibG](https://github.com/tootsuite/mastodon/pull/10194)) -- Fix race condition when streaming out deleted statuses ([ThibG](https://github.com/tootsuite/mastodon/pull/10280)) -- Fix performance of admin federation UI by caching account counts ([Gargron](https://github.com/tootsuite/mastodon/pull/10374)) -- Fix JS error on pages that don't define a CSRF token ([hinaloe](https://github.com/tootsuite/mastodon/pull/10383)) -- Fix `tootctl accounts cull` sometimes removing accounts that are temporarily unreachable ([BenLubar](https://github.com/tootsuite/mastodon/pull/10460)) +- Fix public timelines being broken by new toots when they are not mounted in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/10131)) +- Fix quick filter settings not being saved when selecting a different filter in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10296)) +- Fix remote interaction dialogs being indexed by search engines ([Gargron](https://github.com/mastodon/mastodon/pull/10240)) +- Fix maxed-out invites not showing up as expired in UI ([Gargron](https://github.com/mastodon/mastodon/pull/10274)) +- Fix scrollbar styles on compose textarea ([Gargron](https://github.com/mastodon/mastodon/pull/10292)) +- Fix timeline merge workers being queued for remote users ([Gargron](https://github.com/mastodon/mastodon/pull/10355)) +- Fix alternative relay support regression ([Gargron](https://github.com/mastodon/mastodon/pull/10398)) +- Fix trying to fetch keys of unknown accounts on a self-delete from them ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10326)) +- Fix CAS `:service_validate_url` option ([enewhuis](https://github.com/mastodon/mastodon/pull/10328)) +- Fix race conditions when creating backups ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10234)) +- Fix whitespace not being stripped out of username before validation ([aurelien-reeves](https://github.com/mastodon/mastodon/pull/10239)) +- Fix n+1 query when deleting status ([Gargron](https://github.com/mastodon/mastodon/pull/10247)) +- Fix exiting follows not being rejected when suspending a remote account ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10230)) +- Fix the underlying button element in a disabled icon button not being disabled ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10194)) +- Fix race condition when streaming out deleted statuses ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10280)) +- Fix performance of admin federation UI by caching account counts ([Gargron](https://github.com/mastodon/mastodon/pull/10374)) +- Fix JS error on pages that don't define a CSRF token ([hinaloe](https://github.com/mastodon/mastodon/pull/10383)) +- Fix `tootctl accounts cull` sometimes removing accounts that are temporarily unreachable ([BenLubar](https://github.com/mastodon/mastodon/pull/10460)) ## [2.7.4] - 2019-03-05 ### Fixed -- Fix web UI not cleaning up notifications after block ([Gargron](https://github.com/tootsuite/mastodon/pull/10108)) -- Fix redundant HTTP requests when resolving private statuses ([ThibG](https://github.com/tootsuite/mastodon/pull/10115)) -- Fix performance of account media query ([abcang](https://github.com/tootsuite/mastodon/pull/10121)) -- Fix mention processing for unknown accounts ([ThibG](https://github.com/tootsuite/mastodon/pull/10125)) -- Fix getting started column not scrolling on short screens ([trwnh](https://github.com/tootsuite/mastodon/pull/10075)) -- Fix direct messages pagination in the web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/10126)) -- Fix serialization of Announce activities ([ThibG](https://github.com/tootsuite/mastodon/pull/10129)) -- Fix home timeline perpetually reloading when empty in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/10130)) -- Fix lists export ([ThibG](https://github.com/tootsuite/mastodon/pull/10136)) -- Fix edit profile page crash for suspended-then-unsuspended users ([ThibG](https://github.com/tootsuite/mastodon/pull/10178)) +- Fix web UI not cleaning up notifications after block ([Gargron](https://github.com/mastodon/mastodon/pull/10108)) +- Fix redundant HTTP requests when resolving private statuses ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10115)) +- Fix performance of account media query ([abcang](https://github.com/mastodon/mastodon/pull/10121)) +- Fix mention processing for unknown accounts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10125)) +- Fix getting started column not scrolling on short screens ([trwnh](https://github.com/mastodon/mastodon/pull/10075)) +- Fix direct messages pagination in the web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10126)) +- Fix serialization of Announce activities ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10129)) +- Fix home timeline perpetually reloading when empty in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/10130)) +- Fix lists export ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10136)) +- Fix edit profile page crash for suspended-then-unsuspended users ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10178)) ## [2.7.3] - 2019-02-23 ### Added -- Add domain filter to the admin federation page ([ThibG](https://github.com/tootsuite/mastodon/pull/10071)) -- Add quick link from admin account view to block/unblock instance ([ThibG](https://github.com/tootsuite/mastodon/pull/10073)) +- Add domain filter to the admin federation page ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10071)) +- Add quick link from admin account view to block/unblock instance ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10073)) ### Fixed -- Fix video player width not being updated to fit container width ([ThibG](https://github.com/tootsuite/mastodon/pull/10069)) -- Fix domain filter being shown in admin page when local filter is active ([ThibG](https://github.com/tootsuite/mastodon/pull/10074)) -- Fix crash when conversations have no valid participants ([ThibG](https://github.com/tootsuite/mastodon/pull/10078)) -- Fix error when performing admin actions on no statuses ([ThibG](https://github.com/tootsuite/mastodon/pull/10094)) +- Fix video player width not being updated to fit container width ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10069)) +- Fix domain filter being shown in admin page when local filter is active ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10074)) +- Fix crash when conversations have no valid participants ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10078)) +- Fix error when performing admin actions on no statuses ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10094)) ### Changed -- Change custom emojis to randomize stored file name ([hinaloe](https://github.com/tootsuite/mastodon/pull/10090)) +- Change custom emojis to randomize stored file name ([hinaloe](https://github.com/mastodon/mastodon/pull/10090)) ## [2.7.2] - 2019-02-17 ### Added -- Add support for IPv6 in e-mail validation ([zoc](https://github.com/tootsuite/mastodon/pull/10009)) -- Add record of IP address used for signing up ([ThibG](https://github.com/tootsuite/mastodon/pull/10026)) -- Add tight rate-limit for API deletions (30 per 30 minutes) ([Gargron](https://github.com/tootsuite/mastodon/pull/10042)) -- Add support for embedded `Announce` objects attributed to the same actor ([ThibG](https://github.com/tootsuite/mastodon/pull/9998), [Gargron](https://github.com/tootsuite/mastodon/pull/10065)) -- Add spam filter for `Create` and `Announce` activities ([Gargron](https://github.com/tootsuite/mastodon/pull/10005), [Gargron](https://github.com/tootsuite/mastodon/pull/10041), [Gargron](https://github.com/tootsuite/mastodon/pull/10062)) -- Add `registrations` attribute to `GET /api/v1/instance` ([Gargron](https://github.com/tootsuite/mastodon/pull/10060)) -- Add `vapid_key` to `POST /api/v1/apps` and `GET /api/v1/apps/verify_credentials` ([Gargron](https://github.com/tootsuite/mastodon/pull/10058)) +- Add support for IPv6 in e-mail validation ([zoc](https://github.com/mastodon/mastodon/pull/10009)) +- Add record of IP address used for signing up ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10026)) +- Add tight rate-limit for API deletions (30 per 30 minutes) ([Gargron](https://github.com/mastodon/mastodon/pull/10042)) +- Add support for embedded `Announce` objects attributed to the same actor ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9998), [Gargron](https://github.com/mastodon/mastodon/pull/10065)) +- Add spam filter for `Create` and `Announce` activities ([Gargron](https://github.com/mastodon/mastodon/pull/10005), [Gargron](https://github.com/mastodon/mastodon/pull/10041), [Gargron](https://github.com/mastodon/mastodon/pull/10062)) +- Add `registrations` attribute to `GET /api/v1/instance` ([Gargron](https://github.com/mastodon/mastodon/pull/10060)) +- Add `vapid_key` to `POST /api/v1/apps` and `GET /api/v1/apps/verify_credentials` ([Gargron](https://github.com/mastodon/mastodon/pull/10058)) ### Fixed -- Fix link color and add link underlines in high-contrast theme ([Gargron](https://github.com/tootsuite/mastodon/pull/9949), [Gargron](https://github.com/tootsuite/mastodon/pull/10028)) -- Fix unicode characters in URLs not being linkified ([JMendyk](https://github.com/tootsuite/mastodon/pull/8447), [hinaloe](https://github.com/tootsuite/mastodon/pull/9991)) -- Fix URLs linkifier grabbing ending quotation as part of the link ([Gargron](https://github.com/tootsuite/mastodon/pull/9997)) -- Fix authorized applications page design ([rinsuki](https://github.com/tootsuite/mastodon/pull/9969)) -- Fix custom emojis not showing up in share page emoji picker ([rinsuki](https://github.com/tootsuite/mastodon/pull/9970)) -- Fix too liberal application of whitespace in toots ([trwnh](https://github.com/tootsuite/mastodon/pull/9968)) -- Fix misleading e-mail hint being displayed in admin view ([ThibG](https://github.com/tootsuite/mastodon/pull/9973)) -- Fix tombstones not being cleared out ([abcang](https://github.com/tootsuite/mastodon/pull/9978)) -- Fix some timeline jumps ([ThibG](https://github.com/tootsuite/mastodon/pull/9982), [ThibG](https://github.com/tootsuite/mastodon/pull/10001), [rinsuki](https://github.com/tootsuite/mastodon/pull/10046)) -- Fix content warning input taking keyboard focus even when hidden ([hinaloe](https://github.com/tootsuite/mastodon/pull/10017)) -- Fix hashtags select styling in default and high-contrast themes ([Gargron](https://github.com/tootsuite/mastodon/pull/10029)) -- Fix style regressions on landing page ([Gargron](https://github.com/tootsuite/mastodon/pull/10030)) -- Fix hashtag column not subscribing to stream on mount ([Gargron](https://github.com/tootsuite/mastodon/pull/10040)) -- Fix relay enabling/disabling not resetting inbox availability status ([Gargron](https://github.com/tootsuite/mastodon/pull/10048)) -- Fix mutes, blocks, domain blocks and follow requests not paginating ([Gargron](https://github.com/tootsuite/mastodon/pull/10057)) -- Fix crash on public hashtag pages when streaming fails ([ThibG](https://github.com/tootsuite/mastodon/pull/10061)) +- Fix link color and add link underlines in high-contrast theme ([Gargron](https://github.com/mastodon/mastodon/pull/9949), [Gargron](https://github.com/mastodon/mastodon/pull/10028)) +- Fix unicode characters in URLs not being linkified ([JMendyk](https://github.com/mastodon/mastodon/pull/8447), [hinaloe](https://github.com/mastodon/mastodon/pull/9991)) +- Fix URLs linkifier grabbing ending quotation as part of the link ([Gargron](https://github.com/mastodon/mastodon/pull/9997)) +- Fix authorized applications page design ([rinsuki](https://github.com/mastodon/mastodon/pull/9969)) +- Fix custom emojis not showing up in share page emoji picker ([rinsuki](https://github.com/mastodon/mastodon/pull/9970)) +- Fix too liberal application of whitespace in toots ([trwnh](https://github.com/mastodon/mastodon/pull/9968)) +- Fix misleading e-mail hint being displayed in admin view ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9973)) +- Fix tombstones not being cleared out ([abcang](https://github.com/mastodon/mastodon/pull/9978)) +- Fix some timeline jumps ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9982), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10001), [rinsuki](https://github.com/mastodon/mastodon/pull/10046)) +- Fix content warning input taking keyboard focus even when hidden ([hinaloe](https://github.com/mastodon/mastodon/pull/10017)) +- Fix hashtags select styling in default and high-contrast themes ([Gargron](https://github.com/mastodon/mastodon/pull/10029)) +- Fix style regressions on landing page ([Gargron](https://github.com/mastodon/mastodon/pull/10030)) +- Fix hashtag column not subscribing to stream on mount ([Gargron](https://github.com/mastodon/mastodon/pull/10040)) +- Fix relay enabling/disabling not resetting inbox availability status ([Gargron](https://github.com/mastodon/mastodon/pull/10048)) +- Fix mutes, blocks, domain blocks and follow requests not paginating ([Gargron](https://github.com/mastodon/mastodon/pull/10057)) +- Fix crash on public hashtag pages when streaming fails ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10061)) ### Changed -- Change icon for unlisted visibility level ([clarcharr](https://github.com/tootsuite/mastodon/pull/9952)) -- Change queue of actor deletes from push to pull for non-follower recipients ([ThibG](https://github.com/tootsuite/mastodon/pull/10016)) -- Change robots.txt to exclude media proxy URLs ([nightpool](https://github.com/tootsuite/mastodon/pull/10038)) -- Change upload description input to allow line breaks ([BenLubar](https://github.com/tootsuite/mastodon/pull/10036)) -- Change `dist/mastodon-streaming.service` to recommend running node without intermediary npm command ([nolanlawson](https://github.com/tootsuite/mastodon/pull/10032)) -- Change conversations to always show names of other participants ([Gargron](https://github.com/tootsuite/mastodon/pull/10047)) -- Change buttons on timeline preview to open the interaction dialog ([Gargron](https://github.com/tootsuite/mastodon/pull/10054)) -- Change error graphic to hover-to-play ([Gargron](https://github.com/tootsuite/mastodon/pull/10055)) +- Change icon for unlisted visibility level ([clarcharr](https://github.com/mastodon/mastodon/pull/9952)) +- Change queue of actor deletes from push to pull for non-follower recipients ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10016)) +- Change robots.txt to exclude media proxy URLs ([nightpool](https://github.com/mastodon/mastodon/pull/10038)) +- Change upload description input to allow line breaks ([BenLubar](https://github.com/mastodon/mastodon/pull/10036)) +- Change `dist/mastodon-streaming.service` to recommend running node without intermediary npm command ([nolanlawson](https://github.com/mastodon/mastodon/pull/10032)) +- Change conversations to always show names of other participants ([Gargron](https://github.com/mastodon/mastodon/pull/10047)) +- Change buttons on timeline preview to open the interaction dialog ([Gargron](https://github.com/mastodon/mastodon/pull/10054)) +- Change error graphic to hover-to-play ([Gargron](https://github.com/mastodon/mastodon/pull/10055)) ## [2.7.1] - 2019-01-28 ### Fixed -- Fix SSO authentication not working due to missing agreement boolean ([Gargron](https://github.com/tootsuite/mastodon/pull/9915)) -- Fix slow fallback of CopyAccountStats migration setting stats to 0 ([Gargron](https://github.com/tootsuite/mastodon/pull/9930)) -- Fix wrong command in migration error message ([angristan](https://github.com/tootsuite/mastodon/pull/9877)) -- Fix initial value of volume slider in video player and handle volume changes ([ThibG](https://github.com/tootsuite/mastodon/pull/9929)) -- Fix missing hotkeys for notifications ([ThibG](https://github.com/tootsuite/mastodon/pull/9927)) -- Fix being able to attach unattached media created by other users ([ThibG](https://github.com/tootsuite/mastodon/pull/9921)) -- Fix unrescued SSL error during link verification ([renatolond](https://github.com/tootsuite/mastodon/pull/9914)) -- Fix Firefox scrollbar color regression ([trwnh](https://github.com/tootsuite/mastodon/pull/9908)) -- Fix scheduled status with media immediately creating a status ([ThibG](https://github.com/tootsuite/mastodon/pull/9894)) -- Fix missing strong style for landing page description ([Kjwon15](https://github.com/tootsuite/mastodon/pull/9892)) +- Fix SSO authentication not working due to missing agreement boolean ([Gargron](https://github.com/mastodon/mastodon/pull/9915)) +- Fix slow fallback of CopyAccountStats migration setting stats to 0 ([Gargron](https://github.com/mastodon/mastodon/pull/9930)) +- Fix wrong command in migration error message ([angristan](https://github.com/mastodon/mastodon/pull/9877)) +- Fix initial value of volume slider in video player and handle volume changes ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9929)) +- Fix missing hotkeys for notifications ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9927)) +- Fix being able to attach unattached media created by other users ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9921)) +- Fix unrescued SSL error during link verification ([renatolond](https://github.com/mastodon/mastodon/pull/9914)) +- Fix Firefox scrollbar color regression ([trwnh](https://github.com/mastodon/mastodon/pull/9908)) +- Fix scheduled status with media immediately creating a status ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9894)) +- Fix missing strong style for landing page description ([Kjwon15](https://github.com/mastodon/mastodon/pull/9892)) ## [2.7.0] - 2019-01-20 ### Added -- Add link for adding a user to a list from their profile ([namelessGonbai](https://github.com/tootsuite/mastodon/pull/9062)) -- Add joining several hashtags in a single column ([gdpelican](https://github.com/tootsuite/mastodon/pull/8904)) -- Add volume sliders for videos ([sumdog](https://github.com/tootsuite/mastodon/pull/9366)) -- Add a tooltip explaining what a locked account is ([pawelngei](https://github.com/tootsuite/mastodon/pull/9403)) -- Add preloaded cache for common JSON-LD contexts ([ThibG](https://github.com/tootsuite/mastodon/pull/9412)) -- Add profile directory ([Gargron](https://github.com/tootsuite/mastodon/pull/9427)) -- Add setting to not group reblogs in home feed ([ThibG](https://github.com/tootsuite/mastodon/pull/9248)) -- Add admin ability to remove a user's header image ([ThibG](https://github.com/tootsuite/mastodon/pull/9495)) -- Add account hashtags to ActivityPub actor JSON ([Gargron](https://github.com/tootsuite/mastodon/pull/9450)) -- Add error message for avatar image that's too large ([sumdog](https://github.com/tootsuite/mastodon/pull/9518)) -- Add notification quick-filter bar ([pawelngei](https://github.com/tootsuite/mastodon/pull/9399)) -- Add new first-time tutorial ([Gargron](https://github.com/tootsuite/mastodon/pull/9531)) -- Add moderation warnings ([Gargron](https://github.com/tootsuite/mastodon/pull/9519)) -- Add emoji codepoint mappings for v11.0 ([Gargron](https://github.com/tootsuite/mastodon/pull/9618)) -- Add REST API for creating an account ([Gargron](https://github.com/tootsuite/mastodon/pull/9572)) -- Add support for Malayalam in language filter ([tachyons](https://github.com/tootsuite/mastodon/pull/9624)) -- Add exclude_reblogs option to account statuses API ([Gargron](https://github.com/tootsuite/mastodon/pull/9640)) -- Add local followers page to admin account UI ([chr-1x](https://github.com/tootsuite/mastodon/pull/9610)) -- Add healthcheck commands to docker-compose.yml ([BenLubar](https://github.com/tootsuite/mastodon/pull/9143)) -- Add handler for Move activity to migrate followers ([Gargron](https://github.com/tootsuite/mastodon/pull/9629)) -- Add CSV export for lists and domain blocks ([Gargron](https://github.com/tootsuite/mastodon/pull/9677)) -- Add `tootctl accounts follow ACCT` ([Gargron](https://github.com/tootsuite/mastodon/pull/9414)) -- Add scheduled statuses ([Gargron](https://github.com/tootsuite/mastodon/pull/9706)) -- Add immutable caching for S3 objects ([nolanlawson](https://github.com/tootsuite/mastodon/pull/9722)) -- Add cache to custom emojis API ([Gargron](https://github.com/tootsuite/mastodon/pull/9732)) -- Add preview cards to non-detailed statuses on public pages ([Gargron](https://github.com/tootsuite/mastodon/pull/9714)) -- Add `mod` and `moderator` to list of default reserved usernames ([Gargron](https://github.com/tootsuite/mastodon/pull/9713)) -- Add quick links to the admin interface in the web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/8545)) -- Add `tootctl domains crawl` ([Gargron](https://github.com/tootsuite/mastodon/pull/9809)) -- Add attachment list fallback to public pages ([ThibG](https://github.com/tootsuite/mastodon/pull/9780)) -- Add `tootctl --version` ([Gargron](https://github.com/tootsuite/mastodon/pull/9835)) -- Add information about how to opt-in to the directory on the directory ([Gargron](https://github.com/tootsuite/mastodon/pull/9834)) -- Add timeouts for S3 ([Gargron](https://github.com/tootsuite/mastodon/pull/9842)) -- Add support for non-public reblogs from ActivityPub ([Gargron](https://github.com/tootsuite/mastodon/pull/9841)) -- Add sending of `Reject` activity when sending a `Block` activity ([ThibG](https://github.com/tootsuite/mastodon/pull/9811)) +- Add link for adding a user to a list from their profile ([namelessGonbai](https://github.com/mastodon/mastodon/pull/9062)) +- Add joining several hashtags in a single column ([gdpelican](https://github.com/mastodon/mastodon/pull/8904)) +- Add volume sliders for videos ([sumdog](https://github.com/mastodon/mastodon/pull/9366)) +- Add a tooltip explaining what a locked account is ([pawelngei](https://github.com/mastodon/mastodon/pull/9403)) +- Add preloaded cache for common JSON-LD contexts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9412)) +- Add profile directory ([Gargron](https://github.com/mastodon/mastodon/pull/9427)) +- Add setting to not group reblogs in home feed ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9248)) +- Add admin ability to remove a user's header image ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9495)) +- Add account hashtags to ActivityPub actor JSON ([Gargron](https://github.com/mastodon/mastodon/pull/9450)) +- Add error message for avatar image that's too large ([sumdog](https://github.com/mastodon/mastodon/pull/9518)) +- Add notification quick-filter bar ([pawelngei](https://github.com/mastodon/mastodon/pull/9399)) +- Add new first-time tutorial ([Gargron](https://github.com/mastodon/mastodon/pull/9531)) +- Add moderation warnings ([Gargron](https://github.com/mastodon/mastodon/pull/9519)) +- Add emoji codepoint mappings for v11.0 ([Gargron](https://github.com/mastodon/mastodon/pull/9618)) +- Add REST API for creating an account ([Gargron](https://github.com/mastodon/mastodon/pull/9572)) +- Add support for Malayalam in language filter ([tachyons](https://github.com/mastodon/mastodon/pull/9624)) +- Add exclude_reblogs option to account statuses API ([Gargron](https://github.com/mastodon/mastodon/pull/9640)) +- Add local followers page to admin account UI ([chr-1x](https://github.com/mastodon/mastodon/pull/9610)) +- Add healthcheck commands to docker-compose.yml ([BenLubar](https://github.com/mastodon/mastodon/pull/9143)) +- Add handler for Move activity to migrate followers ([Gargron](https://github.com/mastodon/mastodon/pull/9629)) +- Add CSV export for lists and domain blocks ([Gargron](https://github.com/mastodon/mastodon/pull/9677)) +- Add `tootctl accounts follow ACCT` ([Gargron](https://github.com/mastodon/mastodon/pull/9414)) +- Add scheduled statuses ([Gargron](https://github.com/mastodon/mastodon/pull/9706)) +- Add immutable caching for S3 objects ([nolanlawson](https://github.com/mastodon/mastodon/pull/9722)) +- Add cache to custom emojis API ([Gargron](https://github.com/mastodon/mastodon/pull/9732)) +- Add preview cards to non-detailed statuses on public pages ([Gargron](https://github.com/mastodon/mastodon/pull/9714)) +- Add `mod` and `moderator` to list of default reserved usernames ([Gargron](https://github.com/mastodon/mastodon/pull/9713)) +- Add quick links to the admin interface in the web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/8545)) +- Add `tootctl domains crawl` ([Gargron](https://github.com/mastodon/mastodon/pull/9809)) +- Add attachment list fallback to public pages ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9780)) +- Add `tootctl --version` ([Gargron](https://github.com/mastodon/mastodon/pull/9835)) +- Add information about how to opt-in to the directory on the directory ([Gargron](https://github.com/mastodon/mastodon/pull/9834)) +- Add timeouts for S3 ([Gargron](https://github.com/mastodon/mastodon/pull/9842)) +- Add support for non-public reblogs from ActivityPub ([Gargron](https://github.com/mastodon/mastodon/pull/9841)) +- Add sending of `Reject` activity when sending a `Block` activity ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9811)) ### Changed -- Temporarily pause timeline if mouse moved recently ([lmorchard](https://github.com/tootsuite/mastodon/pull/9200)) -- Change the password form order ([mayaeh](https://github.com/tootsuite/mastodon/pull/9267)) -- Redesign admin UI for accounts ([Gargron](https://github.com/tootsuite/mastodon/pull/9340), [Gargron](https://github.com/tootsuite/mastodon/pull/9643)) -- Redesign admin UI for instances/domain blocks ([Gargron](https://github.com/tootsuite/mastodon/pull/9645)) -- Swap avatar and header input fields in profile page ([ThibG](https://github.com/tootsuite/mastodon/pull/9271)) -- When posting in mobile mode, go back to previous history location ([ThibG](https://github.com/tootsuite/mastodon/pull/9502)) -- Split out is_changing_upload from is_submitting ([ThibG](https://github.com/tootsuite/mastodon/pull/9536)) -- Back to the getting-started when pins the timeline. ([kedamaDQ](https://github.com/tootsuite/mastodon/pull/9561)) -- Allow unauthenticated REST API access to GET /api/v1/accounts/:id/statuses ([Gargron](https://github.com/tootsuite/mastodon/pull/9573)) -- Limit maximum visibility of local silenced users to unlisted ([ThibG](https://github.com/tootsuite/mastodon/pull/9583)) -- Change API error message for unconfirmed accounts ([noellabo](https://github.com/tootsuite/mastodon/pull/9625)) -- Change the icon to "reply-all" when it's a reply to other accounts ([mayaeh](https://github.com/tootsuite/mastodon/pull/9378)) -- Do not ignore federated reports targetting already-reported accounts ([ThibG](https://github.com/tootsuite/mastodon/pull/9534)) -- Upgrade default Ruby version to 2.6.0 ([Gargron](https://github.com/tootsuite/mastodon/pull/9688)) -- Change e-mail digest frequency ([Gargron](https://github.com/tootsuite/mastodon/pull/9689)) -- Change Docker images for Tor support in docker-compose.yml ([Sir-Boops](https://github.com/tootsuite/mastodon/pull/9438)) -- Display fallback link card thumbnail when none is given ([Gargron](https://github.com/tootsuite/mastodon/pull/9715)) -- Change account bio length validation to ignore mention domains and URLs ([Gargron](https://github.com/tootsuite/mastodon/pull/9717)) -- Use configured contact user for "anonymous" federation activities ([yukimochi](https://github.com/tootsuite/mastodon/pull/9661)) -- Change remote interaction dialog to use specific actions instead of generic "interact" ([Gargron](https://github.com/tootsuite/mastodon/pull/9743)) -- Always re-fetch public key when signature verification fails to support blind key rotation ([ThibG](https://github.com/tootsuite/mastodon/pull/9667)) -- Make replies to boosts impossible, connect reply to original status instead ([valerauko](https://github.com/tootsuite/mastodon/pull/9129)) -- Change e-mail MX validation to check both A and MX records against blacklist ([Gargron](https://github.com/tootsuite/mastodon/pull/9489)) -- Hide floating action button on search and getting started pages ([tmm576](https://github.com/tootsuite/mastodon/pull/9826)) -- Redesign public hashtag page to use a masonry layout ([Gargron](https://github.com/tootsuite/mastodon/pull/9822)) -- Use `summary` as summary instead of content warning for converted ActivityPub objects ([Gargron](https://github.com/tootsuite/mastodon/pull/9823)) -- Display a double reply arrow on public pages for toots that are replies ([ThibG](https://github.com/tootsuite/mastodon/pull/9808)) -- Change admin UI right panel size to be wider ([Kjwon15](https://github.com/tootsuite/mastodon/pull/9768)) +- Temporarily pause timeline if mouse moved recently ([lmorchard](https://github.com/mastodon/mastodon/pull/9200)) +- Change the password form order ([mayaeh](https://github.com/mastodon/mastodon/pull/9267)) +- Redesign admin UI for accounts ([Gargron](https://github.com/mastodon/mastodon/pull/9340), [Gargron](https://github.com/mastodon/mastodon/pull/9643)) +- Redesign admin UI for instances/domain blocks ([Gargron](https://github.com/mastodon/mastodon/pull/9645)) +- Swap avatar and header input fields in profile page ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9271)) +- When posting in mobile mode, go back to previous history location ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9502)) +- Split out is_changing_upload from is_submitting ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9536)) +- Back to the getting-started when pins the timeline. ([kedamaDQ](https://github.com/mastodon/mastodon/pull/9561)) +- Allow unauthenticated REST API access to GET /api/v1/accounts/:id/statuses ([Gargron](https://github.com/mastodon/mastodon/pull/9573)) +- Limit maximum visibility of local silenced users to unlisted ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9583)) +- Change API error message for unconfirmed accounts ([noellabo](https://github.com/mastodon/mastodon/pull/9625)) +- Change the icon to "reply-all" when it's a reply to other accounts ([mayaeh](https://github.com/mastodon/mastodon/pull/9378)) +- Do not ignore federated reports targetting already-reported accounts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9534)) +- Upgrade default Ruby version to 2.6.0 ([Gargron](https://github.com/mastodon/mastodon/pull/9688)) +- Change e-mail digest frequency ([Gargron](https://github.com/mastodon/mastodon/pull/9689)) +- Change Docker images for Tor support in docker-compose.yml ([Sir-Boops](https://github.com/mastodon/mastodon/pull/9438)) +- Display fallback link card thumbnail when none is given ([Gargron](https://github.com/mastodon/mastodon/pull/9715)) +- Change account bio length validation to ignore mention domains and URLs ([Gargron](https://github.com/mastodon/mastodon/pull/9717)) +- Use configured contact user for "anonymous" federation activities ([yukimochi](https://github.com/mastodon/mastodon/pull/9661)) +- Change remote interaction dialog to use specific actions instead of generic "interact" ([Gargron](https://github.com/mastodon/mastodon/pull/9743)) +- Always re-fetch public key when signature verification fails to support blind key rotation ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9667)) +- Make replies to boosts impossible, connect reply to original status instead ([valerauko](https://github.com/mastodon/mastodon/pull/9129)) +- Change e-mail MX validation to check both A and MX records against blacklist ([Gargron](https://github.com/mastodon/mastodon/pull/9489)) +- Hide floating action button on search and getting started pages ([tmm576](https://github.com/mastodon/mastodon/pull/9826)) +- Redesign public hashtag page to use a masonry layout ([Gargron](https://github.com/mastodon/mastodon/pull/9822)) +- Use `summary` as summary instead of content warning for converted ActivityPub objects ([Gargron](https://github.com/mastodon/mastodon/pull/9823)) +- Display a double reply arrow on public pages for toots that are replies ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9808)) +- Change admin UI right panel size to be wider ([Kjwon15](https://github.com/mastodon/mastodon/pull/9768)) ### Removed -- Remove links to bridge.joinmastodon.org (non-functional) ([Gargron](https://github.com/tootsuite/mastodon/pull/9608)) -- Remove LD-Signatures from activities that do not need them ([ThibG](https://github.com/tootsuite/mastodon/pull/9659)) +- Remove links to bridge.joinmastodon.org (non-functional) ([Gargron](https://github.com/mastodon/mastodon/pull/9608)) +- Remove LD-Signatures from activities that do not need them ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9659)) ### Fixed -- Remove unused computation of reblog references from updateTimeline ([ThibG](https://github.com/tootsuite/mastodon/pull/9244)) -- Fix loaded embeds resetting if a status arrives from API again ([ThibG](https://github.com/tootsuite/mastodon/pull/9270)) -- Fix race condition causing shallow status with only a "favourited" attribute ([ThibG](https://github.com/tootsuite/mastodon/pull/9272)) -- Remove intermediary arrays when creating hash maps from results ([Gargron](https://github.com/tootsuite/mastodon/pull/9291)) -- Extract counters from accounts table to account_stats table to improve performance ([Gargron](https://github.com/tootsuite/mastodon/pull/9295)) -- Change identities id column to a bigint ([Gargron](https://github.com/tootsuite/mastodon/pull/9371)) -- Fix conversations API pagination ([ThibG](https://github.com/tootsuite/mastodon/pull/9407)) -- Improve account suspension speed and completeness ([Gargron](https://github.com/tootsuite/mastodon/pull/9290)) -- Fix thread depth computation in statuses_controller ([ThibG](https://github.com/tootsuite/mastodon/pull/9426)) -- Fix database deadlocks by moving account stats update outside transaction ([ThibG](https://github.com/tootsuite/mastodon/pull/9437)) -- Escape HTML in profile name preview in profile settings ([pawelngei](https://github.com/tootsuite/mastodon/pull/9446)) -- Use same CORS policy for /@:username and /users/:username ([ThibG](https://github.com/tootsuite/mastodon/pull/9485)) -- Make custom emoji domains case insensitive ([Esteth](https://github.com/tootsuite/mastodon/pull/9474)) -- Various fixes to scrollable lists and media gallery ([ThibG](https://github.com/tootsuite/mastodon/pull/9501)) -- Fix bootsnap cache directory being declared relatively ([Gargron](https://github.com/tootsuite/mastodon/pull/9511)) -- Fix timeline pagination in the web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/9516)) -- Fix padding on dropdown elements in preferences ([ThibG](https://github.com/tootsuite/mastodon/pull/9517)) -- Make avatar and headers respect GIF autoplay settings ([ThibG](https://github.com/tootsuite/mastodon/pull/9515)) -- Do no retry Web Push workers if the server returns a 4xx response ([Gargron](https://github.com/tootsuite/mastodon/pull/9434)) -- Minor scrollable list fixes ([ThibG](https://github.com/tootsuite/mastodon/pull/9551)) -- Ignore low-confidence CharlockHolmes guesses when parsing link cards ([ThibG](https://github.com/tootsuite/mastodon/pull/9510)) -- Fix `tootctl accounts rotate` not updating public keys ([Gargron](https://github.com/tootsuite/mastodon/pull/9556)) -- Fix CSP / X-Frame-Options for media players ([jomo](https://github.com/tootsuite/mastodon/pull/9558)) -- Fix unnecessary loadMore calls when the end of a timeline has been reached ([ThibG](https://github.com/tootsuite/mastodon/pull/9581)) -- Skip mailer job retries when a record no longer exists ([Gargron](https://github.com/tootsuite/mastodon/pull/9590)) -- Fix composer not getting focus after reply confirmation dialog ([ThibG](https://github.com/tootsuite/mastodon/pull/9602)) -- Fix signature verification stoplight triggering on non-timeout errors ([Gargron](https://github.com/tootsuite/mastodon/pull/9617)) -- Fix ThreadResolveWorker getting queued with invalid URLs ([Gargron](https://github.com/tootsuite/mastodon/pull/9628)) -- Fix crash when clearing uninitialized timeline ([ThibG](https://github.com/tootsuite/mastodon/pull/9662)) -- Avoid duplicate work by merging ReplyDistributionWorker into DistributionWorker ([ThibG](https://github.com/tootsuite/mastodon/pull/9660)) -- Skip full text search if it fails, instead of erroring out completely ([Kjwon15](https://github.com/tootsuite/mastodon/pull/9654)) -- Fix profile metadata links not verifying correctly sometimes ([shrft](https://github.com/tootsuite/mastodon/pull/9673)) -- Ensure blocked user unfollows blocker if Block/Undo-Block activities are processed out of order ([ThibG](https://github.com/tootsuite/mastodon/pull/9687)) -- Fix unreadable text color in report modal for some statuses ([Gargron](https://github.com/tootsuite/mastodon/pull/9716)) -- Stop GIFV timeline preview explicitly when it's opened in modal ([kedamaDQ](https://github.com/tootsuite/mastodon/pull/9749)) -- Fix scrollbar width compensation ([ThibG](https://github.com/tootsuite/mastodon/pull/9824)) -- Fix race conditions when processing deleted toots ([ThibG](https://github.com/tootsuite/mastodon/pull/9815)) -- Fix SSO issues on WebKit browsers by disabling Same-Site cookie again ([moritzheiber](https://github.com/tootsuite/mastodon/pull/9819)) -- Fix empty OEmbed error ([renatolond](https://github.com/tootsuite/mastodon/pull/9807)) -- Fix drag & drop modal not disappearing sometimes ([hinaloe](https://github.com/tootsuite/mastodon/pull/9797)) -- Fix statuses with content warnings being displayed in web push notifications sometimes ([ThibG](https://github.com/tootsuite/mastodon/pull/9778)) -- Fix scroll-to-detailed status not working on public pages ([ThibG](https://github.com/tootsuite/mastodon/pull/9773)) -- Fix media modal loading indicator ([ThibG](https://github.com/tootsuite/mastodon/pull/9771)) -- Fix hashtag search results not having a permalink fallback in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/9810)) -- Fix slightly cropped font on settings page dropdowns when using system font ([ariasuni](https://github.com/tootsuite/mastodon/pull/9839)) -- Fix not being able to drag & drop text into forms ([tmm576](https://github.com/tootsuite/mastodon/pull/9840)) +- Remove unused computation of reblog references from updateTimeline ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9244)) +- Fix loaded embeds resetting if a status arrives from API again ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9270)) +- Fix race condition causing shallow status with only a "favourited" attribute ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9272)) +- Remove intermediary arrays when creating hash maps from results ([Gargron](https://github.com/mastodon/mastodon/pull/9291)) +- Extract counters from accounts table to account_stats table to improve performance ([Gargron](https://github.com/mastodon/mastodon/pull/9295)) +- Change identities id column to a bigint ([Gargron](https://github.com/mastodon/mastodon/pull/9371)) +- Fix conversations API pagination ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9407)) +- Improve account suspension speed and completeness ([Gargron](https://github.com/mastodon/mastodon/pull/9290)) +- Fix thread depth computation in statuses_controller ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9426)) +- Fix database deadlocks by moving account stats update outside transaction ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9437)) +- Escape HTML in profile name preview in profile settings ([pawelngei](https://github.com/mastodon/mastodon/pull/9446)) +- Use same CORS policy for /@:username and /users/:username ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9485)) +- Make custom emoji domains case insensitive ([Esteth](https://github.com/mastodon/mastodon/pull/9474)) +- Various fixes to scrollable lists and media gallery ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9501)) +- Fix bootsnap cache directory being declared relatively ([Gargron](https://github.com/mastodon/mastodon/pull/9511)) +- Fix timeline pagination in the web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9516)) +- Fix padding on dropdown elements in preferences ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9517)) +- Make avatar and headers respect GIF autoplay settings ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9515)) +- Do no retry Web Push workers if the server returns a 4xx response ([Gargron](https://github.com/mastodon/mastodon/pull/9434)) +- Minor scrollable list fixes ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9551)) +- Ignore low-confidence CharlockHolmes guesses when parsing link cards ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9510)) +- Fix `tootctl accounts rotate` not updating public keys ([Gargron](https://github.com/mastodon/mastodon/pull/9556)) +- Fix CSP / X-Frame-Options for media players ([jomo](https://github.com/mastodon/mastodon/pull/9558)) +- Fix unnecessary loadMore calls when the end of a timeline has been reached ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9581)) +- Skip mailer job retries when a record no longer exists ([Gargron](https://github.com/mastodon/mastodon/pull/9590)) +- Fix composer not getting focus after reply confirmation dialog ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9602)) +- Fix signature verification stoplight triggering on non-timeout errors ([Gargron](https://github.com/mastodon/mastodon/pull/9617)) +- Fix ThreadResolveWorker getting queued with invalid URLs ([Gargron](https://github.com/mastodon/mastodon/pull/9628)) +- Fix crash when clearing uninitialized timeline ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9662)) +- Avoid duplicate work by merging ReplyDistributionWorker into DistributionWorker ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9660)) +- Skip full text search if it fails, instead of erroring out completely ([Kjwon15](https://github.com/mastodon/mastodon/pull/9654)) +- Fix profile metadata links not verifying correctly sometimes ([shrft](https://github.com/mastodon/mastodon/pull/9673)) +- Ensure blocked user unfollows blocker if Block/Undo-Block activities are processed out of order ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9687)) +- Fix unreadable text color in report modal for some statuses ([Gargron](https://github.com/mastodon/mastodon/pull/9716)) +- Stop GIFV timeline preview explicitly when it's opened in modal ([kedamaDQ](https://github.com/mastodon/mastodon/pull/9749)) +- Fix scrollbar width compensation ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9824)) +- Fix race conditions when processing deleted toots ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9815)) +- Fix SSO issues on WebKit browsers by disabling Same-Site cookie again ([moritzheiber](https://github.com/mastodon/mastodon/pull/9819)) +- Fix empty OEmbed error ([renatolond](https://github.com/mastodon/mastodon/pull/9807)) +- Fix drag & drop modal not disappearing sometimes ([hinaloe](https://github.com/mastodon/mastodon/pull/9797)) +- Fix statuses with content warnings being displayed in web push notifications sometimes ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9778)) +- Fix scroll-to-detailed status not working on public pages ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9773)) +- Fix media modal loading indicator ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9771)) +- Fix hashtag search results not having a permalink fallback in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9810)) +- Fix slightly cropped font on settings page dropdowns when using system font ([ariasuni](https://github.com/mastodon/mastodon/pull/9839)) +- Fix not being able to drag & drop text into forms ([tmm576](https://github.com/mastodon/mastodon/pull/9840)) ### Security -- Sanitize and sandbox toot embeds in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/9552)) -- Add tombstones for remote statuses to prevent replay attacks ([ThibG](https://github.com/tootsuite/mastodon/pull/9830)) +- Sanitize and sandbox toot embeds in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9552)) +- Add tombstones for remote statuses to prevent replay attacks ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9830)) ## [2.6.5] - 2018-12-01 ### Changed -- Change lists to display replies to others on the list and list owner ([ThibG](https://github.com/tootsuite/mastodon/pull/9324)) +- Change lists to display replies to others on the list and list owner ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9324)) ### Fixed -- Fix failures caused by commonly-used JSON-LD contexts being unavailable ([ThibG](https://github.com/tootsuite/mastodon/pull/9412)) +- Fix failures caused by commonly-used JSON-LD contexts being unavailable ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9412)) ## [2.6.4] - 2018-11-30 ### Fixed -- Fix yarn dependencies not installing due to yanked event-stream package ([Gargron](https://github.com/tootsuite/mastodon/pull/9401)) +- Fix yarn dependencies not installing due to yanked event-stream package ([Gargron](https://github.com/mastodon/mastodon/pull/9401)) ## [2.6.3] - 2018-11-30 ### Added -- Add hyphen to characters allowed in remote usernames ([ThibG](https://github.com/tootsuite/mastodon/pull/9345)) +- Add hyphen to characters allowed in remote usernames ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9345)) ### Changed -- Change server user count to exclude suspended accounts ([Gargron](https://github.com/tootsuite/mastodon/pull/9380)) +- Change server user count to exclude suspended accounts ([Gargron](https://github.com/mastodon/mastodon/pull/9380)) ### Fixed -- Fix ffmpeg processing sometimes stalling due to overfilled stdout buffer ([hugogameiro](https://github.com/tootsuite/mastodon/pull/9368)) -- Fix missing DNS records raising the wrong kind of exception ([Gargron](https://github.com/tootsuite/mastodon/pull/9379)) -- Fix already queued deliveries still trying to reach inboxes marked as unavailable ([Gargron](https://github.com/tootsuite/mastodon/pull/9358)) +- Fix ffmpeg processing sometimes stalling due to overfilled stdout buffer ([hugogameiro](https://github.com/mastodon/mastodon/pull/9368)) +- Fix missing DNS records raising the wrong kind of exception ([Gargron](https://github.com/mastodon/mastodon/pull/9379)) +- Fix already queued deliveries still trying to reach inboxes marked as unavailable ([Gargron](https://github.com/mastodon/mastodon/pull/9358)) ### Security -- Fix TLS handshake timeout not being enforced ([Gargron](https://github.com/tootsuite/mastodon/pull/9381)) +- Fix TLS handshake timeout not being enforced ([Gargron](https://github.com/mastodon/mastodon/pull/9381)) ## [2.6.2] - 2018-11-23 ### Added -- Add Page to whitelisted ActivityPub types ([mbajur](https://github.com/tootsuite/mastodon/pull/9188)) -- Add 20px to column width in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/9227)) -- Add amount of freed disk space in `tootctl media remove` ([Gargron](https://github.com/tootsuite/mastodon/pull/9229), [Gargron](https://github.com/tootsuite/mastodon/pull/9239), [mayaeh](https://github.com/tootsuite/mastodon/pull/9288)) -- Add "Show thread" link to self-replies ([Gargron](https://github.com/tootsuite/mastodon/pull/9228)) +- Add Page to whitelisted ActivityPub types ([mbajur](https://github.com/mastodon/mastodon/pull/9188)) +- Add 20px to column width in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/9227)) +- Add amount of freed disk space in `tootctl media remove` ([Gargron](https://github.com/mastodon/mastodon/pull/9229), [Gargron](https://github.com/mastodon/mastodon/pull/9239), [mayaeh](https://github.com/mastodon/mastodon/pull/9288)) +- Add "Show thread" link to self-replies ([Gargron](https://github.com/mastodon/mastodon/pull/9228)) ### Changed -- Change order of Atom and RSS links so Atom is first ([Alkarex](https://github.com/tootsuite/mastodon/pull/9302)) -- Change Nginx configuration for Nanobox apps ([danhunsaker](https://github.com/tootsuite/mastodon/pull/9310)) -- Change the follow action to appear instant in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/9220)) -- Change how the ActiveRecord connection is instantiated in on_worker_boot ([Gargron](https://github.com/tootsuite/mastodon/pull/9238)) -- Change `tootctl accounts cull` to always touch accounts so they can be skipped ([renatolond](https://github.com/tootsuite/mastodon/pull/9293)) -- Change mime type comparison to ignore JSON-LD profile ([valerauko](https://github.com/tootsuite/mastodon/pull/9179)) +- Change order of Atom and RSS links so Atom is first ([Alkarex](https://github.com/mastodon/mastodon/pull/9302)) +- Change Nginx configuration for Nanobox apps ([danhunsaker](https://github.com/mastodon/mastodon/pull/9310)) +- Change the follow action to appear instant in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/9220)) +- Change how the ActiveRecord connection is instantiated in on_worker_boot ([Gargron](https://github.com/mastodon/mastodon/pull/9238)) +- Change `tootctl accounts cull` to always touch accounts so they can be skipped ([renatolond](https://github.com/mastodon/mastodon/pull/9293)) +- Change mime type comparison to ignore JSON-LD profile ([valerauko](https://github.com/mastodon/mastodon/pull/9179)) ### Fixed -- Fix web UI crash when conversation has no last status ([sammy8806](https://github.com/tootsuite/mastodon/pull/9207)) -- Fix follow limit validator reporting lower number past threshold ([Gargron](https://github.com/tootsuite/mastodon/pull/9230)) -- Fix form validation flash message color and input borders ([Gargron](https://github.com/tootsuite/mastodon/pull/9235)) -- Fix invalid twitter:player cards being displayed ([ThibG](https://github.com/tootsuite/mastodon/pull/9254)) -- Fix emoji update date being processed incorrectly ([ThibG](https://github.com/tootsuite/mastodon/pull/9255)) -- Fix playing embed resetting if status is reloaded in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/9270), [Gargron](https://github.com/tootsuite/mastodon/pull/9275)) -- Fix web UI crash when favouriting a deleted status ([ThibG](https://github.com/tootsuite/mastodon/pull/9272)) -- Fix intermediary arrays being created for hash maps ([Gargron](https://github.com/tootsuite/mastodon/pull/9291)) -- Fix filter ID not being a string in REST API ([Gargron](https://github.com/tootsuite/mastodon/pull/9303)) +- Fix web UI crash when conversation has no last status ([sammy8806](https://github.com/mastodon/mastodon/pull/9207)) +- Fix follow limit validator reporting lower number past threshold ([Gargron](https://github.com/mastodon/mastodon/pull/9230)) +- Fix form validation flash message color and input borders ([Gargron](https://github.com/mastodon/mastodon/pull/9235)) +- Fix invalid twitter:player cards being displayed ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9254)) +- Fix emoji update date being processed incorrectly ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9255)) +- Fix playing embed resetting if status is reloaded in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9270), [Gargron](https://github.com/mastodon/mastodon/pull/9275)) +- Fix web UI crash when favouriting a deleted status ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9272)) +- Fix intermediary arrays being created for hash maps ([Gargron](https://github.com/mastodon/mastodon/pull/9291)) +- Fix filter ID not being a string in REST API ([Gargron](https://github.com/mastodon/mastodon/pull/9303)) ### Security -- Fix multiple remote account deletions being able to deadlock the database ([Gargron](https://github.com/tootsuite/mastodon/pull/9292)) -- Fix HTTP connection timeout of 10s not being enforced ([Gargron](https://github.com/tootsuite/mastodon/pull/9329)) +- Fix multiple remote account deletions being able to deadlock the database ([Gargron](https://github.com/mastodon/mastodon/pull/9292)) +- Fix HTTP connection timeout of 10s not being enforced ([Gargron](https://github.com/mastodon/mastodon/pull/9329)) ## [2.6.1] - 2018-10-30 ### Fixed -- Fix resolving resources by URL not working due to a regression in [valerauko](https://github.com/tootsuite/mastodon/pull/9132) ([Gargron](https://github.com/tootsuite/mastodon/pull/9171)) -- Fix reducer error in web UI when a conversation has no last status ([Gargron](https://github.com/tootsuite/mastodon/pull/9173)) +- Fix resolving resources by URL not working due to a regression in [valerauko](https://github.com/mastodon/mastodon/pull/9132) ([Gargron](https://github.com/mastodon/mastodon/pull/9171)) +- Fix reducer error in web UI when a conversation has no last status ([Gargron](https://github.com/mastodon/mastodon/pull/9173)) ## [2.6.0] - 2018-10-30 ### Added -- Add link ownership verification ([Gargron](https://github.com/tootsuite/mastodon/pull/8703)) -- Add conversations API ([Gargron](https://github.com/tootsuite/mastodon/pull/8832)) -- Add limit for the number of people that can be followed from one account ([Gargron](https://github.com/tootsuite/mastodon/pull/8807)) -- Add admin setting to customize mascot ([ashleyhull-versent](https://github.com/tootsuite/mastodon/pull/8766)) -- Add support for more granular ActivityPub audiences from other software, i.e. circles ([Gargron](https://github.com/tootsuite/mastodon/pull/8950), [Gargron](https://github.com/tootsuite/mastodon/pull/9093), [Gargron](https://github.com/tootsuite/mastodon/pull/9150)) -- Add option to block all reports from a domain ([Gargron](https://github.com/tootsuite/mastodon/pull/8830)) -- Add user preference to always expand toots marked with content warnings ([webroo](https://github.com/tootsuite/mastodon/pull/8762)) -- Add user preference to always hide all media ([fvh-P](https://github.com/tootsuite/mastodon/pull/8569)) -- Add `force_login` param to OAuth authorize page ([Gargron](https://github.com/tootsuite/mastodon/pull/8655)) -- Add `tootctl accounts backup` ([Gargron](https://github.com/tootsuite/mastodon/pull/8642), [Gargron](https://github.com/tootsuite/mastodon/pull/8811)) -- Add `tootctl accounts create` ([Gargron](https://github.com/tootsuite/mastodon/pull/8642), [Gargron](https://github.com/tootsuite/mastodon/pull/8811)) -- Add `tootctl accounts cull` ([Gargron](https://github.com/tootsuite/mastodon/pull/8642), [Gargron](https://github.com/tootsuite/mastodon/pull/8811)) -- Add `tootctl accounts delete` ([Gargron](https://github.com/tootsuite/mastodon/pull/8642), [Gargron](https://github.com/tootsuite/mastodon/pull/8811)) -- Add `tootctl accounts modify` ([Gargron](https://github.com/tootsuite/mastodon/pull/8642), [Gargron](https://github.com/tootsuite/mastodon/pull/8811)) -- Add `tootctl accounts refresh` ([Gargron](https://github.com/tootsuite/mastodon/pull/8642), [Gargron](https://github.com/tootsuite/mastodon/pull/8811)) -- Add `tootctl feeds build` ([Gargron](https://github.com/tootsuite/mastodon/pull/8642), [Gargron](https://github.com/tootsuite/mastodon/pull/8811)) -- Add `tootctl feeds clear` ([Gargron](https://github.com/tootsuite/mastodon/pull/8642), [Gargron](https://github.com/tootsuite/mastodon/pull/8811)) -- Add `tootctl settings registrations open` ([Gargron](https://github.com/tootsuite/mastodon/pull/8642), [Gargron](https://github.com/tootsuite/mastodon/pull/8811)) -- Add `tootctl settings registrations close` ([Gargron](https://github.com/tootsuite/mastodon/pull/8642), [Gargron](https://github.com/tootsuite/mastodon/pull/8811)) -- Add `min_id` param to REST API to support backwards pagination ([Gargron](https://github.com/tootsuite/mastodon/pull/8736)) -- Add a confirmation dialog when hitting reply and the compose box isn't empty ([ThibG](https://github.com/tootsuite/mastodon/pull/8893)) -- Add PostgreSQL disk space growth tracking in PGHero ([Gargron](https://github.com/tootsuite/mastodon/pull/8906)) -- Add button for disabling local account to report quick actions bar ([Gargron](https://github.com/tootsuite/mastodon/pull/9024)) -- Add Czech language ([Aditoo17](https://github.com/tootsuite/mastodon/pull/8594)) -- Add `same-site` (`lax`) attribute to cookies ([sorin-davidoi](https://github.com/tootsuite/mastodon/pull/8626)) -- Add support for styled scrollbars in Firefox Nightly ([sorin-davidoi](https://github.com/tootsuite/mastodon/pull/8653)) -- Add highlight to the active tab in web UI profiles ([rhoio](https://github.com/tootsuite/mastodon/pull/8673)) -- Add auto-focus for comment textarea in report modal ([ThibG](https://github.com/tootsuite/mastodon/pull/8689)) -- Add auto-focus for emoji picker's search field ([ThibG](https://github.com/tootsuite/mastodon/pull/8688)) -- Add nginx and systemd templates to `dist/` directory ([Gargron](https://github.com/tootsuite/mastodon/pull/8770)) -- Add support for `/.well-known/change-password` ([Gargron](https://github.com/tootsuite/mastodon/pull/8828)) -- Add option to override FFMPEG binary path ([sascha-sl](https://github.com/tootsuite/mastodon/pull/8855)) -- Add `dns-prefetch` tag when using different host for assets or uploads ([Gargron](https://github.com/tootsuite/mastodon/pull/8942)) -- Add `description` meta tag ([Gargron](https://github.com/tootsuite/mastodon/pull/8941)) -- Add `Content-Security-Policy` header ([ThibG](https://github.com/tootsuite/mastodon/pull/8957)) -- Add cache for the instance info API ([ykzts](https://github.com/tootsuite/mastodon/pull/8765)) -- Add suggested follows to search screen in mobile layout ([Gargron](https://github.com/tootsuite/mastodon/pull/9010)) -- Add CORS header to `/.well-known/*` routes ([BenLubar](https://github.com/tootsuite/mastodon/pull/9083)) -- Add `card` attribute to statuses returned from REST API ([Gargron](https://github.com/tootsuite/mastodon/pull/9120)) -- Add in-stream link preview ([Gargron](https://github.com/tootsuite/mastodon/pull/9120)) -- Add support for ActivityPub `Page` objects ([mbajur](https://github.com/tootsuite/mastodon/pull/9121)) +- Add link ownership verification ([Gargron](https://github.com/mastodon/mastodon/pull/8703)) +- Add conversations API ([Gargron](https://github.com/mastodon/mastodon/pull/8832)) +- Add limit for the number of people that can be followed from one account ([Gargron](https://github.com/mastodon/mastodon/pull/8807)) +- Add admin setting to customize mascot ([ashleyhull-versent](https://github.com/mastodon/mastodon/pull/8766)) +- Add support for more granular ActivityPub audiences from other software, i.e. circles ([Gargron](https://github.com/mastodon/mastodon/pull/8950), [Gargron](https://github.com/mastodon/mastodon/pull/9093), [Gargron](https://github.com/mastodon/mastodon/pull/9150)) +- Add option to block all reports from a domain ([Gargron](https://github.com/mastodon/mastodon/pull/8830)) +- Add user preference to always expand toots marked with content warnings ([webroo](https://github.com/mastodon/mastodon/pull/8762)) +- Add user preference to always hide all media ([fvh-P](https://github.com/mastodon/mastodon/pull/8569)) +- Add `force_login` param to OAuth authorize page ([Gargron](https://github.com/mastodon/mastodon/pull/8655)) +- Add `tootctl accounts backup` ([Gargron](https://github.com/mastodon/mastodon/pull/8642), [Gargron](https://github.com/mastodon/mastodon/pull/8811)) +- Add `tootctl accounts create` ([Gargron](https://github.com/mastodon/mastodon/pull/8642), [Gargron](https://github.com/mastodon/mastodon/pull/8811)) +- Add `tootctl accounts cull` ([Gargron](https://github.com/mastodon/mastodon/pull/8642), [Gargron](https://github.com/mastodon/mastodon/pull/8811)) +- Add `tootctl accounts delete` ([Gargron](https://github.com/mastodon/mastodon/pull/8642), [Gargron](https://github.com/mastodon/mastodon/pull/8811)) +- Add `tootctl accounts modify` ([Gargron](https://github.com/mastodon/mastodon/pull/8642), [Gargron](https://github.com/mastodon/mastodon/pull/8811)) +- Add `tootctl accounts refresh` ([Gargron](https://github.com/mastodon/mastodon/pull/8642), [Gargron](https://github.com/mastodon/mastodon/pull/8811)) +- Add `tootctl feeds build` ([Gargron](https://github.com/mastodon/mastodon/pull/8642), [Gargron](https://github.com/mastodon/mastodon/pull/8811)) +- Add `tootctl feeds clear` ([Gargron](https://github.com/mastodon/mastodon/pull/8642), [Gargron](https://github.com/mastodon/mastodon/pull/8811)) +- Add `tootctl settings registrations open` ([Gargron](https://github.com/mastodon/mastodon/pull/8642), [Gargron](https://github.com/mastodon/mastodon/pull/8811)) +- Add `tootctl settings registrations close` ([Gargron](https://github.com/mastodon/mastodon/pull/8642), [Gargron](https://github.com/mastodon/mastodon/pull/8811)) +- Add `min_id` param to REST API to support backwards pagination ([Gargron](https://github.com/mastodon/mastodon/pull/8736)) +- Add a confirmation dialog when hitting reply and the compose box isn't empty ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/8893)) +- Add PostgreSQL disk space growth tracking in PGHero ([Gargron](https://github.com/mastodon/mastodon/pull/8906)) +- Add button for disabling local account to report quick actions bar ([Gargron](https://github.com/mastodon/mastodon/pull/9024)) +- Add Czech language ([Aditoo17](https://github.com/mastodon/mastodon/pull/8594)) +- Add `same-site` (`lax`) attribute to cookies ([sorin-davidoi](https://github.com/mastodon/mastodon/pull/8626)) +- Add support for styled scrollbars in Firefox Nightly ([sorin-davidoi](https://github.com/mastodon/mastodon/pull/8653)) +- Add highlight to the active tab in web UI profiles ([rhoio](https://github.com/mastodon/mastodon/pull/8673)) +- Add auto-focus for comment textarea in report modal ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/8689)) +- Add auto-focus for emoji picker's search field ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/8688)) +- Add nginx and systemd templates to `dist/` directory ([Gargron](https://github.com/mastodon/mastodon/pull/8770)) +- Add support for `/.well-known/change-password` ([Gargron](https://github.com/mastodon/mastodon/pull/8828)) +- Add option to override FFMPEG binary path ([sascha-sl](https://github.com/mastodon/mastodon/pull/8855)) +- Add `dns-prefetch` tag when using different host for assets or uploads ([Gargron](https://github.com/mastodon/mastodon/pull/8942)) +- Add `description` meta tag ([Gargron](https://github.com/mastodon/mastodon/pull/8941)) +- Add `Content-Security-Policy` header ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/8957)) +- Add cache for the instance info API ([ykzts](https://github.com/mastodon/mastodon/pull/8765)) +- Add suggested follows to search screen in mobile layout ([Gargron](https://github.com/mastodon/mastodon/pull/9010)) +- Add CORS header to `/.well-known/*` routes ([BenLubar](https://github.com/mastodon/mastodon/pull/9083)) +- Add `card` attribute to statuses returned from REST API ([Gargron](https://github.com/mastodon/mastodon/pull/9120)) +- Add in-stream link preview ([Gargron](https://github.com/mastodon/mastodon/pull/9120)) +- Add support for ActivityPub `Page` objects ([mbajur](https://github.com/mastodon/mastodon/pull/9121)) ### Changed -- Change forms design ([Gargron](https://github.com/tootsuite/mastodon/pull/8703)) -- Change reports overview to group by target account ([Gargron](https://github.com/tootsuite/mastodon/pull/8674)) -- Change web UI to show "read more" link on overly long in-stream statuses ([lanodan](https://github.com/tootsuite/mastodon/pull/8205)) -- Change design of direct messages column ([Gargron](https://github.com/tootsuite/mastodon/pull/8832), [Gargron](https://github.com/tootsuite/mastodon/pull/9022)) -- Change home timelines to exclude DMs ([Gargron](https://github.com/tootsuite/mastodon/pull/8940)) -- Change list timelines to exclude all replies ([cbayerlein](https://github.com/tootsuite/mastodon/pull/8683)) -- Change admin accounts UI default sort to most recent ([Gargron](https://github.com/tootsuite/mastodon/pull/8813)) -- Change documentation URL in the UI ([Gargron](https://github.com/tootsuite/mastodon/pull/8898)) -- Change style of success and failure messages ([Gargron](https://github.com/tootsuite/mastodon/pull/8973)) -- Change DM filtering to always allow DMs from staff ([qguv](https://github.com/tootsuite/mastodon/pull/8993)) -- Change recommended Ruby version to 2.5.3 ([zunda](https://github.com/tootsuite/mastodon/pull/9003)) -- Change docker-compose default to persist volumes in current directory ([Gargron](https://github.com/tootsuite/mastodon/pull/9055)) -- Change character counters on edit profile page to input length limit ([Gargron](https://github.com/tootsuite/mastodon/pull/9100)) -- Change notification filtering to always let through messages from staff ([Gargron](https://github.com/tootsuite/mastodon/pull/9152)) -- Change "hide boosts from user" function also hiding notifications about boosts ([ThibG](https://github.com/tootsuite/mastodon/pull/9147)) -- Change CSS `detailed-status__wrapper` class actually wrap the detailed status ([trwnh](https://github.com/tootsuite/mastodon/pull/8547)) +- Change forms design ([Gargron](https://github.com/mastodon/mastodon/pull/8703)) +- Change reports overview to group by target account ([Gargron](https://github.com/mastodon/mastodon/pull/8674)) +- Change web UI to show "read more" link on overly long in-stream statuses ([lanodan](https://github.com/mastodon/mastodon/pull/8205)) +- Change design of direct messages column ([Gargron](https://github.com/mastodon/mastodon/pull/8832), [Gargron](https://github.com/mastodon/mastodon/pull/9022)) +- Change home timelines to exclude DMs ([Gargron](https://github.com/mastodon/mastodon/pull/8940)) +- Change list timelines to exclude all replies ([cbayerlein](https://github.com/mastodon/mastodon/pull/8683)) +- Change admin accounts UI default sort to most recent ([Gargron](https://github.com/mastodon/mastodon/pull/8813)) +- Change documentation URL in the UI ([Gargron](https://github.com/mastodon/mastodon/pull/8898)) +- Change style of success and failure messages ([Gargron](https://github.com/mastodon/mastodon/pull/8973)) +- Change DM filtering to always allow DMs from staff ([qguv](https://github.com/mastodon/mastodon/pull/8993)) +- Change recommended Ruby version to 2.5.3 ([zunda](https://github.com/mastodon/mastodon/pull/9003)) +- Change docker-compose default to persist volumes in current directory ([Gargron](https://github.com/mastodon/mastodon/pull/9055)) +- Change character counters on edit profile page to input length limit ([Gargron](https://github.com/mastodon/mastodon/pull/9100)) +- Change notification filtering to always let through messages from staff ([Gargron](https://github.com/mastodon/mastodon/pull/9152)) +- Change "hide boosts from user" function also hiding notifications about boosts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9147)) +- Change CSS `detailed-status__wrapper` class actually wrap the detailed status ([trwnh](https://github.com/mastodon/mastodon/pull/8547)) ### Deprecated -- `GET /api/v1/timelines/direct` → `GET /api/v1/conversations` ([Gargron](https://github.com/tootsuite/mastodon/pull/8832)) -- `POST /api/v1/notifications/dismiss` → `POST /api/v1/notifications/:id/dismiss` ([Gargron](https://github.com/tootsuite/mastodon/pull/8905)) -- `GET /api/v1/statuses/:id/card` → `card` attributed included in status ([Gargron](https://github.com/tootsuite/mastodon/pull/9120)) +- `GET /api/v1/timelines/direct` → `GET /api/v1/conversations` ([Gargron](https://github.com/mastodon/mastodon/pull/8832)) +- `POST /api/v1/notifications/dismiss` → `POST /api/v1/notifications/:id/dismiss` ([Gargron](https://github.com/mastodon/mastodon/pull/8905)) +- `GET /api/v1/statuses/:id/card` → `card` attributed included in status ([Gargron](https://github.com/mastodon/mastodon/pull/9120)) ### Removed -- Remove "on this device" label in column push settings ([rhoio](https://github.com/tootsuite/mastodon/pull/8704)) -- Remove rake tasks in favour of tootctl commands ([Gargron](https://github.com/tootsuite/mastodon/pull/8675)) +- Remove "on this device" label in column push settings ([rhoio](https://github.com/mastodon/mastodon/pull/8704)) +- Remove rake tasks in favour of tootctl commands ([Gargron](https://github.com/mastodon/mastodon/pull/8675)) ### Fixed -- Fix remote statuses using instance's default locale if no language given ([Kjwon15](https://github.com/tootsuite/mastodon/pull/8861)) -- Fix streaming API not exiting when port or socket is unavailable ([Gargron](https://github.com/tootsuite/mastodon/pull/9023)) -- Fix network calls being performed in database transaction in ActivityPub handler ([Gargron](https://github.com/tootsuite/mastodon/pull/8951)) -- Fix dropdown arrow position ([ThibG](https://github.com/tootsuite/mastodon/pull/8637)) -- Fix first element of dropdowns being focused even if not using keyboard ([ThibG](https://github.com/tootsuite/mastodon/pull/8679)) -- Fix tootctl requiring `bundle exec` invocation ([abcang](https://github.com/tootsuite/mastodon/pull/8619)) -- Fix public pages not using animation preference for avatars ([renatolond](https://github.com/tootsuite/mastodon/pull/8614)) -- Fix OEmbed/OpenGraph cards not understanding relative URLs ([ThibG](https://github.com/tootsuite/mastodon/pull/8669)) -- Fix some dark emojis not having a white outline ([ThibG](https://github.com/tootsuite/mastodon/pull/8597)) -- Fix media description not being displayed in various media modals ([ThibG](https://github.com/tootsuite/mastodon/pull/8678)) -- Fix generated URLs of desktop notifications missing base URL ([GenbuHase](https://github.com/tootsuite/mastodon/pull/8758)) -- Fix RTL styles ([mabkenar](https://github.com/tootsuite/mastodon/pull/8764), [mabkenar](https://github.com/tootsuite/mastodon/pull/8767), [mabkenar](https://github.com/tootsuite/mastodon/pull/8823), [mabkenar](https://github.com/tootsuite/mastodon/pull/8897), [mabkenar](https://github.com/tootsuite/mastodon/pull/9005), [mabkenar](https://github.com/tootsuite/mastodon/pull/9007), [mabkenar](https://github.com/tootsuite/mastodon/pull/9018), [mabkenar](https://github.com/tootsuite/mastodon/pull/9021), [mabkenar](https://github.com/tootsuite/mastodon/pull/9145), [mabkenar](https://github.com/tootsuite/mastodon/pull/9146)) -- Fix crash in streaming API when tag param missing ([Gargron](https://github.com/tootsuite/mastodon/pull/8955)) -- Fix hotkeys not working when no element is focused ([ThibG](https://github.com/tootsuite/mastodon/pull/8998)) -- Fix some hotkeys not working on detailed status view ([ThibG](https://github.com/tootsuite/mastodon/pull/9006)) -- Fix og:url on status pages ([ThibG](https://github.com/tootsuite/mastodon/pull/9047)) -- Fix upload option buttons only being visible on hover ([Gargron](https://github.com/tootsuite/mastodon/pull/9074)) -- Fix tootctl not returning exit code 1 on wrong arguments ([sascha-sl](https://github.com/tootsuite/mastodon/pull/9094)) -- Fix preview cards for appearing for profiles mentioned in toot ([ThibG](https://github.com/tootsuite/mastodon/pull/6934), [ThibG](https://github.com/tootsuite/mastodon/pull/9158)) -- Fix local accounts sometimes being duplicated as faux-remote ([Gargron](https://github.com/tootsuite/mastodon/pull/9109)) -- Fix emoji search when the shortcode has multiple separators ([ThibG](https://github.com/tootsuite/mastodon/pull/9124)) -- Fix dropdowns sometimes being partially obscured by other elements ([kedamaDQ](https://github.com/tootsuite/mastodon/pull/9126)) -- Fix cache not updating when reply/boost/favourite counters or media sensitivity update ([Gargron](https://github.com/tootsuite/mastodon/pull/9119)) -- Fix empty display name precedence over username in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/9163)) -- Fix td instead of th in sessions table header ([Gargron](https://github.com/tootsuite/mastodon/pull/9162)) -- Fix handling of content types with profile ([valerauko](https://github.com/tootsuite/mastodon/pull/9132)) +- Fix remote statuses using instance's default locale if no language given ([Kjwon15](https://github.com/mastodon/mastodon/pull/8861)) +- Fix streaming API not exiting when port or socket is unavailable ([Gargron](https://github.com/mastodon/mastodon/pull/9023)) +- Fix network calls being performed in database transaction in ActivityPub handler ([Gargron](https://github.com/mastodon/mastodon/pull/8951)) +- Fix dropdown arrow position ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/8637)) +- Fix first element of dropdowns being focused even if not using keyboard ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/8679)) +- Fix tootctl requiring `bundle exec` invocation ([abcang](https://github.com/mastodon/mastodon/pull/8619)) +- Fix public pages not using animation preference for avatars ([renatolond](https://github.com/mastodon/mastodon/pull/8614)) +- Fix OEmbed/OpenGraph cards not understanding relative URLs ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/8669)) +- Fix some dark emojis not having a white outline ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/8597)) +- Fix media description not being displayed in various media modals ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/8678)) +- Fix generated URLs of desktop notifications missing base URL ([GenbuHase](https://github.com/mastodon/mastodon/pull/8758)) +- Fix RTL styles ([mabkenar](https://github.com/mastodon/mastodon/pull/8764), [mabkenar](https://github.com/mastodon/mastodon/pull/8767), [mabkenar](https://github.com/mastodon/mastodon/pull/8823), [mabkenar](https://github.com/mastodon/mastodon/pull/8897), [mabkenar](https://github.com/mastodon/mastodon/pull/9005), [mabkenar](https://github.com/mastodon/mastodon/pull/9007), [mabkenar](https://github.com/mastodon/mastodon/pull/9018), [mabkenar](https://github.com/mastodon/mastodon/pull/9021), [mabkenar](https://github.com/mastodon/mastodon/pull/9145), [mabkenar](https://github.com/mastodon/mastodon/pull/9146)) +- Fix crash in streaming API when tag param missing ([Gargron](https://github.com/mastodon/mastodon/pull/8955)) +- Fix hotkeys not working when no element is focused ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/8998)) +- Fix some hotkeys not working on detailed status view ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9006)) +- Fix og:url on status pages ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9047)) +- Fix upload option buttons only being visible on hover ([Gargron](https://github.com/mastodon/mastodon/pull/9074)) +- Fix tootctl not returning exit code 1 on wrong arguments ([sascha-sl](https://github.com/mastodon/mastodon/pull/9094)) +- Fix preview cards for appearing for profiles mentioned in toot ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/6934), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/9158)) +- Fix local accounts sometimes being duplicated as faux-remote ([Gargron](https://github.com/mastodon/mastodon/pull/9109)) +- Fix emoji search when the shortcode has multiple separators ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9124)) +- Fix dropdowns sometimes being partially obscured by other elements ([kedamaDQ](https://github.com/mastodon/mastodon/pull/9126)) +- Fix cache not updating when reply/boost/favourite counters or media sensitivity update ([Gargron](https://github.com/mastodon/mastodon/pull/9119)) +- Fix empty display name precedence over username in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/9163)) +- Fix td instead of th in sessions table header ([Gargron](https://github.com/mastodon/mastodon/pull/9162)) +- Fix handling of content types with profile ([valerauko](https://github.com/mastodon/mastodon/pull/9132)) ## [2.5.2] - 2018-10-12 ### Security -- Fix XSS vulnerability ([Gargron](https://github.com/tootsuite/mastodon/pull/8959)) +- Fix XSS vulnerability ([Gargron](https://github.com/mastodon/mastodon/pull/8959)) ## [2.5.1] - 2018-10-07 ### Fixed -- Fix database migrations for PostgreSQL below 9.5 ([Gargron](https://github.com/tootsuite/mastodon/pull/8903)) -- Fix class autoloading issue in ActivityPub Create handler ([Gargron](https://github.com/tootsuite/mastodon/pull/8820)) -- Fix cache statistics not being sent via statsd when statsd enabled ([ykzts](https://github.com/tootsuite/mastodon/pull/8831)) -- Bump puma from 3.11.4 to 3.12.0 ([dependabot[bot]](https://github.com/tootsuite/mastodon/pull/8883)) +- Fix database migrations for PostgreSQL below 9.5 ([Gargron](https://github.com/mastodon/mastodon/pull/8903)) +- Fix class autoloading issue in ActivityPub Create handler ([Gargron](https://github.com/mastodon/mastodon/pull/8820)) +- Fix cache statistics not being sent via statsd when statsd enabled ([ykzts](https://github.com/mastodon/mastodon/pull/8831)) +- Bump puma from 3.11.4 to 3.12.0 ([dependabot[bot]](https://github.com/mastodon/mastodon/pull/8883)) ### Security -- Fix some local images not having their EXIF metadata stripped on upload ([ThibG](https://github.com/tootsuite/mastodon/pull/8714)) -- Fix being able to enable a disabled relay via ActivityPub Accept handler ([ThibG](https://github.com/tootsuite/mastodon/pull/8864)) -- Bump nokogiri from 1.8.4 to 1.8.5 ([dependabot[bot]](https://github.com/tootsuite/mastodon/pull/8881)) -- Fix being able to report statuses not belonging to the reported account ([ThibG](https://github.com/tootsuite/mastodon/pull/8916)) +- Fix some local images not having their EXIF metadata stripped on upload ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/8714)) +- Fix being able to enable a disabled relay via ActivityPub Accept handler ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/8864)) +- Bump nokogiri from 1.8.4 to 1.8.5 ([dependabot[bot]](https://github.com/mastodon/mastodon/pull/8881)) +- Fix being able to report statuses not belonging to the reported account ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/8916)) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0d563559bdf2bc..3f51c4bd0a0b4b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,7 +14,7 @@ If your contributions are accepted into Mastodon, you can request to be paid thr ## Bug reports -Bug reports and feature suggestions must use descriptive and concise titles and be submitted to [GitHub Issues](https://github.com/tootsuite/mastodon/issues). Please use the search function to make sure that you are not submitting duplicates, and that a similar report or request has not already been resolved or rejected. +Bug reports and feature suggestions must use descriptive and concise titles and be submitted to [GitHub Issues](https://github.com/mastodon/mastodon/issues). Please use the search function to make sure that you are not submitting duplicates, and that a similar report or request has not already been resolved or rejected. ## Translations @@ -44,4 +44,4 @@ It is not always possible to phrase every change in such a manner, but it is des ## Documentation -The [Mastodon documentation](https://docs.joinmastodon.org) is a statically generated site. You can [submit merge requests to tootsuite/documentation](https://github.com/tootsuite/documentation). +The [Mastodon documentation](https://docs.joinmastodon.org) is a statically generated site. You can [submit merge requests to mastodon/documentation](https://github.com/mastodon/documentation). diff --git a/README.md b/README.md index 9fa4ec0075ff55..9c83fa3d4fae3f 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ ![Mastodon](https://i.imgur.com/NhZc40l.png) ======== -[![GitHub release](https://img.shields.io/github/release/tootsuite/mastodon.svg)][releases] -[![Build Status](https://img.shields.io/circleci/project/github/tootsuite/mastodon.svg)][circleci] +[![GitHub release](https://img.shields.io/github/release/mastodon/mastodon.svg)][releases] +[![Build Status](https://img.shields.io/circleci/project/github/mastodon/mastodon.svg)][circleci] [![Code Climate](https://img.shields.io/codeclimate/maintainability/tootsuite/mastodon.svg)][code_climate] [![Crowdin](https://d322cqt584bo4o.cloudfront.net/mastodon/localized.svg)][crowdin] [![Docker Pulls](https://img.shields.io/docker/pulls/tootsuite/mastodon.svg)][docker] -[releases]: https://github.com/tootsuite/mastodon/releases -[circleci]: https://circleci.com/gh/tootsuite/mastodon +[releases]: https://github.com/mastodon/mastodon/releases +[circleci]: https://circleci.com/gh/mastodon/mastodon [code_climate]: https://codeclimate.com/github/tootsuite/mastodon [crowdin]: https://crowdin.com/project/mastodon [docker]: https://hub.docker.com/r/tootsuite/mastodon/ diff --git a/app.json b/app.json index e4f7cf403ec4a0..6b436538348ca1 100644 --- a/app.json +++ b/app.json @@ -1,8 +1,8 @@ { "name": "Mastodon", "description": "A GNU Social-compatible microblogging server", - "repository": "https://github.com/tootsuite/mastodon", - "logo": "https://github.com/tootsuite.png", + "repository": "https://github.com/mastodon/mastodon", + "logo": "https://github.com/mastodon.png", "env": { "HEROKU": { "description": "Leave this as true", diff --git a/app/javascript/mastodon/components/intersection_observer_article.js b/app/javascript/mastodon/components/intersection_observer_article.js index 2d87f19b53525b..26f85fa4004f7c 100644 --- a/app/javascript/mastodon/components/intersection_observer_article.js +++ b/app/javascript/mastodon/components/intersection_observer_article.js @@ -93,7 +93,7 @@ export default class IntersectionObserverArticle extends React.Component { // When the browser gets a chance, test if we're still not intersecting, // and if so, set our isHidden to true to trigger an unrender. The point of // this is to save DOM nodes and avoid using up too much memory. - // See: https://github.com/tootsuite/mastodon/issues/2900 + // See: https://github.com/mastodon/mastodon/issues/2900 this.setState((prevState) => ({ isHidden: !prevState.isIntersecting })); } diff --git a/config/deploy.rb b/config/deploy.rb index f844cc8714e689..f642e6e59d07ff 100644 --- a/config/deploy.rb +++ b/config/deploy.rb @@ -2,7 +2,7 @@ lock '3.16.0' -set :repo_url, ENV.fetch('REPO', 'https://github.com/tootsuite/mastodon.git') +set :repo_url, ENV.fetch('REPO', 'https://github.com/mastodon/mastodon.git') set :branch, ENV.fetch('BRANCH', 'master') set :application, 'mastodon' diff --git a/db/migrate/20200508212852_reset_unique_jobs_locks.rb b/db/migrate/20200508212852_reset_unique_jobs_locks.rb index 304e4932236b1b..d717ffc547f71d 100644 --- a/db/migrate/20200508212852_reset_unique_jobs_locks.rb +++ b/db/migrate/20200508212852_reset_unique_jobs_locks.rb @@ -3,7 +3,7 @@ class ResetUniqueJobsLocks < ActiveRecord::Migration[5.2] def up # We do this to clean up unique job digests that were not properly - # disposed of prior to https://github.com/tootsuite/mastodon/pull/13361 + # disposed of prior to https://github.com/mastodon/mastodon/pull/13361 until SidekiqUniqueJobs::Digests.new.delete_by_pattern('*').nil?; end end diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 287cdc7fdbecce..478d5a43d711fd 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -33,7 +33,7 @@ def to_s end def repository - ENV.fetch('GITHUB_REPOSITORY', 'tootsuite/mastodon') + ENV.fetch('GITHUB_REPOSITORY', 'mastodon/mastodon') end def source_base_url diff --git a/lib/tasks/repo.rake b/lib/tasks/repo.rake index 86c358a94e3948..d004c5751b7408 100644 --- a/lib/tasks/repo.rake +++ b/lib/tasks/repo.rake @@ -1,6 +1,6 @@ # frozen_string_literal: true -REPOSITORY_NAME = 'tootsuite/mastodon' +REPOSITORY_NAME = 'mastodon/mastodon' namespace :repo do desc 'Generate the AUTHORS.md file' @@ -34,7 +34,7 @@ namespace :repo do file << <<~FOOTER - This document is provided for informational purposes only. Since it is only updated once per release, the version you are looking at may be currently out of date. To see the full list of contributors, consider looking at the [git history](https://github.com/tootsuite/mastodon/graphs/contributors) instead. + This document is provided for informational purposes only. Since it is only updated once per release, the version you are looking at may be currently out of date. To see the full list of contributors, consider looking at the [git history](https://github.com/mastodon/mastodon/graphs/contributors) instead. FOOTER end diff --git a/package.json b/package.json index f485b1370dff0f..bedbf24aad749d 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "@tootsuite/mastodon", + "name": "@mastodon/mastodon", "license": "AGPL-3.0-or-later", "engines": { "node": ">=12" @@ -18,7 +18,7 @@ }, "repository": { "type": "git", - "url": "https://github.com/tootsuite/mastodon.git" + "url": "https://github.com/mastodon/mastodon.git" }, "browserslist": [ "last 2 versions", diff --git a/scalingo.json b/scalingo.json index 324356df0c262b..51d9b5b9f5c43a 100644 --- a/scalingo.json +++ b/scalingo.json @@ -1,8 +1,8 @@ { "name": "Mastodon", "description": "A GNU Social-compatible microblogging server", - "repository": "https://github.com/tootsuite/mastodon", - "logo": "https://github.com/tootsuite.png", + "repository": "https://github.com/mastodon/mastodon", + "logo": "https://github.com/mastodon.png", "env": { "LOCAL_DOMAIN": { "description": "The domain that your Mastodon instance will run on (this can be appname.scalingo.io or a custom domain)", diff --git a/spec/fixtures/xml/mastodon.atom b/spec/fixtures/xml/mastodon.atom deleted file mode 100644 index 92921a93800800..00000000000000 --- a/spec/fixtures/xml/mastodon.atom +++ /dev/null @@ -1,261 +0,0 @@ - - - http://kickass.zone/users/localhost.atom - ::1 - 2016-10-10T13:29:56Z - http://kickass.zone/system/accounts/avatars/000/000/001/medium/eris.png - - http://activitystrea.ms/schema/1.0/person - http://kickass.zone/users/localhost - localhost - localhost@kickass.zone - - - - - localhost - ::1 - - - - - - - tag:kickass.zone,2016-10-10:objectId=7:objectType=Follow - 2016-10-10T13:29:56Z - 2016-10-10T13:29:56Z - localhost started following kat@mastodon.social - localhost started following kat@mastodon.social - http://activitystrea.ms/schema/1.0/follow - - - http://activitystrea.ms/schema/1.0/activity - - http://activitystrea.ms/schema/1.0/person - https://mastodon.social/users/kat - kat - kat@mastodon.social - #trans #queer - - - - - kat - Kat - #trans #queer - - - - tag:kickass.zone,2016-10-10:objectId=3:objectType=Favourite - 2016-10-10T13:29:26Z - 2016-10-10T13:29:26Z - localhost favourited a status by kat@mastodon.social - localhost favourited a status by kat@mastodon.social - http://activitystrea.ms/schema/1.0/favorite - - - http://activitystrea.ms/schema/1.0/activity - - - http://activitystrea.ms/schema/1.0/comment - tag:mastodon.social,2016-10-10:objectId=22833:objectType=Status - @localhost oooh more mastodons ❤ - - <p><a href="http://kickass.zone/users/localhost">@localhost</a> oooh more mastodons ❤</p> - http://activitystrea.ms/schema/1.0/post - 2016-10-10T13:23:35Z - 2016-10-10T13:23:35Z - - http://activitystrea.ms/schema/1.0/person - https://mastodon.social/users/kat - kat - kat@mastodon.social - #trans #queer - - - - - kat - Kat - #trans #queer - - - - - - tag:kickass.zone,2016-10-10:objectId=2:objectType=Favourite - 2016-10-10T13:13:15Z - 2016-10-10T13:13:15Z - localhost favourited a status by Gargron@mastodon.social - localhost favourited a status by Gargron@mastodon.social - http://activitystrea.ms/schema/1.0/favorite - - - http://activitystrea.ms/schema/1.0/activity - - - http://activitystrea.ms/schema/1.0/note - tag:mastodon.social,2016-10-10:objectId=22825:objectType=Status - Deployed some fixes - - <p>Deployed some fixes</p> - http://activitystrea.ms/schema/1.0/post - 2016-10-10T13:10:37Z - 2016-10-10T13:10:37Z - - http://activitystrea.ms/schema/1.0/person - https://mastodon.social/users/Gargron - Gargron - Gargron@mastodon.social - Developer of Mastodon, a GNU social alternative: https://github.com/tootsuite/mastodon - - - - - Gargron - Eugen - Developer of Mastodon, a GNU social alternative: https://github.com/tootsuite/mastodon - - - - - tag:kickass.zone,2016-10-10:objectId=17:objectType=Status - 2016-10-10T00:41:31Z - 2016-10-10T00:41:31Z - Social media needs MOAR cats! http://kickass.zone/media/3 - <p>Social media needs MOAR cats! <a rel="nofollow noopener noreferrer" href="http://kickass.zone/media/3">http://kickass.zone/media/3</a></p> - http://activitystrea.ms/schema/1.0/post - - - http://activitystrea.ms/schema/1.0/note - - - - tag:kickass.zone,2016-10-10:objectId=14:objectType=Status - 2016-10-10T00:38:39Z - 2016-10-10T00:38:39Z - http://kickass.zone/media/2 - <p><a rel="nofollow noopener noreferrer" href="http://kickass.zone/media/2">http://kickass.zone/media/2</a></p> - http://activitystrea.ms/schema/1.0/post - - - http://activitystrea.ms/schema/1.0/note - - - - tag:kickass.zone,2016-10-10:objectId=12:objectType=Status - 2016-10-10T00:37:49Z - 2016-10-10T00:37:49Z - - <activity:verb>http://activitystrea.ms/schema/1.0/delete</activity:verb> - <link rel="self" type="application/atom+xml" href="http://kickass.zone/users/localhost/updates/7.atom"/> - <link rel="alternate" type="text/html" href="http://kickass.zone/users/localhost/updates/7"/> - <activity:object-type>http://activitystrea.ms/schema/1.0/activity</activity:object-type> - </entry> - <entry> - <id>tag:kickass.zone,2016-10-10:objectId=4:objectType=Follow</id> - <published>2016-10-10T00:23:07Z</published> - <updated>2016-10-10T00:23:07Z</updated> - <title>localhost started following bignimbus@mastodon.social - localhost started following bignimbus@mastodon.social - http://activitystrea.ms/schema/1.0/follow - - - http://activitystrea.ms/schema/1.0/activity - - http://activitystrea.ms/schema/1.0/person - https://mastodon.social/users/bignimbus - bignimbus - bignimbus@mastodon.social - jdauriemma.com - - - - - bignimbus - Jeff Auriemma - jdauriemma.com - - - - tag:kickass.zone,2016-10-10:objectId=2:objectType=Follow - 2016-10-10T00:14:18Z - 2016-10-10T00:14:18Z - localhost started following Gargron@mastodon.social - localhost started following Gargron@mastodon.social - http://activitystrea.ms/schema/1.0/follow - - - http://activitystrea.ms/schema/1.0/activity - - http://activitystrea.ms/schema/1.0/person - https://mastodon.social/users/Gargron - Gargron - Gargron@mastodon.social - Developer of Mastodon, a GNU social alternative: https://github.com/tootsuite/mastodon - - - - - Gargron - Eugen - Developer of Mastodon, a GNU social alternative: https://github.com/tootsuite/mastodon - - - - tag:kickass.zone,2016-10-10:objectId=1:objectType=Follow - 2016-10-10T00:09:09Z - 2016-10-10T00:09:09Z - localhost started following abc@mastodon.social - localhost started following abc@mastodon.social - http://activitystrea.ms/schema/1.0/follow - - - http://activitystrea.ms/schema/1.0/activity - - http://activitystrea.ms/schema/1.0/person - https://mastodon.social/users/abc - abc - abc@mastodon.social - - - - - abc - abc - - - - tag:kickass.zone,2016-10-10:objectId=3:objectType=Status - 2016-10-10T00:02:47Z - 2016-10-10T00:02:47Z - - <activity:verb>http://activitystrea.ms/schema/1.0/delete</activity:verb> - <link rel="self" type="application/atom+xml" href="http://kickass.zone/users/localhost/updates/3.atom"/> - <link rel="alternate" type="text/html" href="http://kickass.zone/users/localhost/updates/3"/> - <activity:object-type>http://activitystrea.ms/schema/1.0/activity</activity:object-type> - </entry> - <entry> - <id>tag:kickass.zone,2016-10-10:objectId=2:objectType=Status</id> - <published>2016-10-10T00:02:18Z</published> - <updated>2016-10-10T00:02:18Z</updated> - <title>Yes, that was the obligatory first post. :) - <p>Yes, that was the obligatory first post. :)</p> - http://activitystrea.ms/schema/1.0/post - - - http://activitystrea.ms/schema/1.0/comment - - - - tag:kickass.zone,2016-10-10:objectId=1:objectType=Status - 2016-10-10T00:01:56Z - 2016-10-10T00:01:56Z - Hello, world! - <p>Hello, world!</p> - http://activitystrea.ms/schema/1.0/post - - - http://activitystrea.ms/schema/1.0/note - - diff --git a/spec/presenters/instance_presenter_spec.rb b/spec/presenters/instance_presenter_spec.rb index 93a4e88e4136f5..973b3e23c80aa7 100644 --- a/spec/presenters/instance_presenter_spec.rb +++ b/spec/presenters/instance_presenter_spec.rb @@ -91,8 +91,8 @@ end describe '#source_url' do - it 'returns "https://github.com/tootsuite/mastodon"' do - expect(instance_presenter.source_url).to eq('https://github.com/tootsuite/mastodon') + it 'returns "https://github.com/mastodon/mastodon"' do + expect(instance_presenter.source_url).to eq('https://github.com/mastodon/mastodon') end end diff --git a/spec/views/about/show.html.haml_spec.rb b/spec/views/about/show.html.haml_spec.rb index c75c2875989fdd..1c2f5eee9596f8 100644 --- a/spec/views/about/show.html.haml_spec.rb +++ b/spec/views/about/show.html.haml_spec.rb @@ -17,7 +17,7 @@ site_short_description: 'something', site_description: 'something', version_number: '1.0', - source_url: 'https://github.com/tootsuite/mastodon', + source_url: 'https://github.com/mastodon/mastodon', open_registrations: false, thumbnail: nil, hero: nil, From 986397b3a255e5078ff4830a7d4d4687a12f4c19 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 13 Jul 2021 15:45:17 +0200 Subject: [PATCH 32/81] Improve modal flow and back button handling (#16499) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Refactor shouldUpdateScroll passing So far, shouldUpdateScroll has been manually passed down from the very top of the React component hierarchy even though it is a static function common to all ScrollContainer instances, so replaced that with a custom class extending ScrollContainer. * Generalize “press back to close modal” to any modal and to public pages * Fix boost confirmation modal closing media modal --- .../mastodon/components/modal_root.js | 36 ++++++++++++ .../mastodon/components/scrollable_list.js | 7 +-- .../mastodon/components/status_list.js | 5 +- .../mastodon/containers/mastodon.js | 6 +- .../mastodon/containers/scroll_container.js | 18 ++++++ .../features/account_gallery/index.js | 7 +-- .../features/account_timeline/index.js | 4 +- .../mastodon/features/blocks/index.js | 4 +- .../features/bookmarked_statuses/index.js | 4 +- .../features/community_timeline/index.js | 4 +- .../components/conversations_list.js | 1 - .../features/direct_timeline/index.js | 4 +- .../mastodon/features/directory/index.js | 7 +-- .../mastodon/features/domain_blocks/index.js | 4 +- .../features/favourited_statuses/index.js | 4 +- .../mastodon/features/favourites/index.js | 4 +- .../features/follow_requests/index.js | 4 +- .../mastodon/features/followers/index.js | 4 +- .../mastodon/features/following/index.js | 4 +- .../features/hashtag_timeline/index.js | 4 +- .../mastodon/features/home_timeline/index.js | 4 +- .../mastodon/features/list_timeline/index.js | 4 +- .../mastodon/features/lists/index.js | 3 +- .../mastodon/features/mutes/index.js | 4 +- .../mastodon/features/notifications/index.js | 4 +- .../features/pinned_statuses/index.js | 4 +- .../features/public_timeline/index.js | 4 +- .../mastodon/features/reblogs/index.js | 4 +- .../mastodon/features/status/index.js | 6 +- .../features/ui/components/audio_modal.js | 27 --------- .../features/ui/components/media_modal.js | 24 -------- .../features/ui/components/video_modal.js | 24 -------- .../features/ui/containers/modal_container.js | 4 +- app/javascript/mastodon/features/ui/index.js | 58 +++++++++---------- app/javascript/mastodon/reducers/modal.js | 14 ++--- 35 files changed, 123 insertions(+), 200 deletions(-) create mode 100644 app/javascript/mastodon/containers/scroll_container.js diff --git a/app/javascript/mastodon/components/modal_root.js b/app/javascript/mastodon/components/modal_root.js index 26344528ea79c4..8c9409d0144bbb 100644 --- a/app/javascript/mastodon/components/modal_root.js +++ b/app/javascript/mastodon/components/modal_root.js @@ -1,6 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import 'wicg-inert'; +import { createBrowserHistory } from 'history'; import { multiply } from 'color-blend'; export default class ModalRoot extends React.PureComponent { @@ -48,6 +49,7 @@ export default class ModalRoot extends React.PureComponent { componentDidMount () { window.addEventListener('keyup', this.handleKeyUp, false); window.addEventListener('keydown', this.handleKeyDown, false); + this.history = this.context.router ? this.context.router.history : createBrowserHistory(); } componentWillReceiveProps (nextProps) { @@ -69,6 +71,14 @@ export default class ModalRoot extends React.PureComponent { this.activeElement.focus({ preventScroll: true }); this.activeElement = null; }).catch(console.error); + + this._handleModalClose(); + } + if (this.props.children && !prevProps.children) { + this._handleModalOpen(); + } + if (this.props.children) { + this._ensureHistoryBuffer(); } } @@ -77,6 +87,32 @@ export default class ModalRoot extends React.PureComponent { window.removeEventListener('keydown', this.handleKeyDown); } + _handleModalOpen () { + this._modalHistoryKey = Date.now(); + this.unlistenHistory = this.history.listen((_, action) => { + if (action === 'POP') { + this.props.onClose(); + } + }); + } + + _handleModalClose () { + if (this.unlistenHistory) { + this.unlistenHistory(); + } + const { state } = this.history.location; + if (state && state.mastodonModalKey === this._modalHistoryKey) { + this.history.goBack(); + } + } + + _ensureHistoryBuffer () { + const { pathname, state } = this.history.location; + if (!state || state.mastodonModalKey !== this._modalHistoryKey) { + this.history.push(pathname, { ...state, mastodonModalKey: this._modalHistoryKey }); + } + } + getSiblings = () => { return Array(...this.node.parentElement.childNodes).filter(node => node !== this.node); } diff --git a/app/javascript/mastodon/components/scrollable_list.js b/app/javascript/mastodon/components/scrollable_list.js index 2689b18ef3dbaf..68a178512271c3 100644 --- a/app/javascript/mastodon/components/scrollable_list.js +++ b/app/javascript/mastodon/components/scrollable_list.js @@ -1,5 +1,5 @@ import React, { PureComponent } from 'react'; -import { ScrollContainer } from 'react-router-scroll-4'; +import ScrollContainer from 'mastodon/containers/scroll_container'; import PropTypes from 'prop-types'; import IntersectionObserverArticleContainer from '../containers/intersection_observer_article_container'; import LoadMore from './load_more'; @@ -34,7 +34,6 @@ class ScrollableList extends PureComponent { onScrollToTop: PropTypes.func, onScroll: PropTypes.func, trackScroll: PropTypes.bool, - shouldUpdateScroll: PropTypes.func, isLoading: PropTypes.bool, showLoading: PropTypes.bool, hasMore: PropTypes.bool, @@ -290,7 +289,7 @@ class ScrollableList extends PureComponent { } render () { - const { children, scrollKey, trackScroll, shouldUpdateScroll, showLoading, isLoading, hasMore, numPending, prepend, alwaysPrepend, append, emptyMessage, onLoadMore } = this.props; + const { children, scrollKey, trackScroll, showLoading, isLoading, hasMore, numPending, prepend, alwaysPrepend, append, emptyMessage, onLoadMore } = this.props; const { fullscreen } = this.state; const childrenCount = React.Children.count(children); @@ -356,7 +355,7 @@ class ScrollableList extends PureComponent { if (trackScroll) { return ( - + {scrollableArea} ); diff --git a/app/javascript/mastodon/components/status_list.js b/app/javascript/mastodon/components/status_list.js index 25411c12724702..eaaffcc3a8c176 100644 --- a/app/javascript/mastodon/components/status_list.js +++ b/app/javascript/mastodon/components/status_list.js @@ -18,7 +18,6 @@ export default class StatusList extends ImmutablePureComponent { onScrollToTop: PropTypes.func, onScroll: PropTypes.func, trackScroll: PropTypes.bool, - shouldUpdateScroll: PropTypes.func, isLoading: PropTypes.bool, isPartial: PropTypes.bool, hasMore: PropTypes.bool, @@ -77,7 +76,7 @@ export default class StatusList extends ImmutablePureComponent { } render () { - const { statusIds, featuredStatusIds, shouldUpdateScroll, onLoadMore, timelineId, ...other } = this.props; + const { statusIds, featuredStatusIds, onLoadMore, timelineId, ...other } = this.props; const { isLoading, isPartial } = other; if (isPartial) { @@ -120,7 +119,7 @@ export default class StatusList extends ImmutablePureComponent { } return ( - + {scrollableContent} ); diff --git a/app/javascript/mastodon/containers/mastodon.js b/app/javascript/mastodon/containers/mastodon.js index 513b599087760d..892ff1ca99f23f 100644 --- a/app/javascript/mastodon/containers/mastodon.js +++ b/app/javascript/mastodon/containers/mastodon.js @@ -10,8 +10,6 @@ import { hydrateStore } from '../actions/store'; import { connectUserStream } from '../actions/streaming'; import { IntlProvider, addLocaleData } from 'react-intl'; import { getLocale } from '../locales'; -import { previewState as previewMediaState } from 'mastodon/features/ui/components/media_modal'; -import { previewState as previewVideoState } from 'mastodon/features/ui/components/video_modal'; import initialState from '../initial_state'; import ErrorBoundary from '../components/error_boundary'; @@ -41,8 +39,8 @@ export default class Mastodon extends React.PureComponent { } } - shouldUpdateScroll (_, { location }) { - return location.state !== previewMediaState && location.state !== previewVideoState; + shouldUpdateScroll (prevRouterProps, { location }) { + return !(location.state?.mastodonModalKey && location.state?.mastodonModalKey !== prevRouterProps?.location?.state?.mastodonModalKey); } render () { diff --git a/app/javascript/mastodon/containers/scroll_container.js b/app/javascript/mastodon/containers/scroll_container.js new file mode 100644 index 00000000000000..d21ff63687dbfd --- /dev/null +++ b/app/javascript/mastodon/containers/scroll_container.js @@ -0,0 +1,18 @@ +import { ScrollContainer as OriginalScrollContainer } from 'react-router-scroll-4'; + +// ScrollContainer is used to automatically scroll to the top when pushing a +// new history state and remembering the scroll position when going back. +// There are a few things we need to do differently, though. +const defaultShouldUpdateScroll = (prevRouterProps, { location }) => { + // If the change is caused by opening a modal, do not scroll to top + return !(location.state?.mastodonModalKey && location.state?.mastodonModalKey !== prevRouterProps?.location?.state?.mastodonModalKey); +}; + +export default +class ScrollContainer extends OriginalScrollContainer { + + static defaultProps = { + shouldUpdateScroll: defaultShouldUpdateScroll, + }; + +} diff --git a/app/javascript/mastodon/features/account_gallery/index.js b/app/javascript/mastodon/features/account_gallery/index.js index 015a6a6d7067dc..e199c929e29322 100644 --- a/app/javascript/mastodon/features/account_gallery/index.js +++ b/app/javascript/mastodon/features/account_gallery/index.js @@ -11,7 +11,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component'; import { getAccountGallery } from 'mastodon/selectors'; import MediaItem from './components/media_item'; import HeaderContainer from '../account_timeline/containers/header_container'; -import { ScrollContainer } from 'react-router-scroll-4'; +import ScrollContainer from 'mastodon/containers/scroll_container'; import LoadMore from 'mastodon/components/load_more'; import MissingIndicator from 'mastodon/components/missing_indicator'; import { openModal } from 'mastodon/actions/modal'; @@ -29,7 +29,6 @@ const mapStateToProps = (state, props) => ({ class LoadMoreMedia extends ImmutablePureComponent { static propTypes = { - shouldUpdateScroll: PropTypes.func, maxId: PropTypes.string, onLoadMore: PropTypes.func.isRequired, }; @@ -127,7 +126,7 @@ class AccountGallery extends ImmutablePureComponent { } render () { - const { attachments, shouldUpdateScroll, isLoading, hasMore, isAccount, multiColumn, blockedBy, suspended } = this.props; + const { attachments, isLoading, hasMore, isAccount, multiColumn, blockedBy, suspended } = this.props; const { width } = this.state; if (!isAccount) { @@ -164,7 +163,7 @@ class AccountGallery extends ImmutablePureComponent { - +
diff --git a/app/javascript/mastodon/features/account_timeline/index.js b/app/javascript/mastodon/features/account_timeline/index.js index fa4239d6f5f4aa..821c852b6f2501 100644 --- a/app/javascript/mastodon/features/account_timeline/index.js +++ b/app/javascript/mastodon/features/account_timeline/index.js @@ -50,7 +50,6 @@ class AccountTimeline extends ImmutablePureComponent { static propTypes = { params: PropTypes.object.isRequired, dispatch: PropTypes.func.isRequired, - shouldUpdateScroll: PropTypes.func, statusIds: ImmutablePropTypes.list, featuredStatusIds: ImmutablePropTypes.list, isLoading: PropTypes.bool, @@ -115,7 +114,7 @@ class AccountTimeline extends ImmutablePureComponent { } render () { - const { shouldUpdateScroll, statusIds, featuredStatusIds, isLoading, hasMore, blockedBy, suspended, isAccount, multiColumn, remote, remoteUrl } = this.props; + const { statusIds, featuredStatusIds, isLoading, hasMore, blockedBy, suspended, isAccount, multiColumn, remote, remoteUrl } = this.props; if (!isAccount) { return ( @@ -162,7 +161,6 @@ class AccountTimeline extends ImmutablePureComponent { isLoading={isLoading} hasMore={hasMore} onLoadMore={this.handleLoadMore} - shouldUpdateScroll={shouldUpdateScroll} emptyMessage={emptyMessage} bindToDocument={!multiColumn} timelineId='account' diff --git a/app/javascript/mastodon/features/blocks/index.js b/app/javascript/mastodon/features/blocks/index.js index 107deb841706fb..7ec1774344812b 100644 --- a/app/javascript/mastodon/features/blocks/index.js +++ b/app/javascript/mastodon/features/blocks/index.js @@ -29,7 +29,6 @@ class Blocks extends ImmutablePureComponent { static propTypes = { params: PropTypes.object.isRequired, dispatch: PropTypes.func.isRequired, - shouldUpdateScroll: PropTypes.func, accountIds: ImmutablePropTypes.list, hasMore: PropTypes.bool, isLoading: PropTypes.bool, @@ -46,7 +45,7 @@ class Blocks extends ImmutablePureComponent { }, 300, { leading: true }); render () { - const { intl, accountIds, shouldUpdateScroll, hasMore, multiColumn, isLoading } = this.props; + const { intl, accountIds, hasMore, multiColumn, isLoading } = this.props; if (!accountIds) { return ( @@ -66,7 +65,6 @@ class Blocks extends ImmutablePureComponent { onLoadMore={this.handleLoadMore} hasMore={hasMore} isLoading={isLoading} - shouldUpdateScroll={shouldUpdateScroll} emptyMessage={emptyMessage} bindToDocument={!multiColumn} > diff --git a/app/javascript/mastodon/features/bookmarked_statuses/index.js b/app/javascript/mastodon/features/bookmarked_statuses/index.js index c37cb9176765b2..cf067d95435430 100644 --- a/app/javascript/mastodon/features/bookmarked_statuses/index.js +++ b/app/javascript/mastodon/features/bookmarked_statuses/index.js @@ -27,7 +27,6 @@ class Bookmarks extends ImmutablePureComponent { static propTypes = { dispatch: PropTypes.func.isRequired, - shouldUpdateScroll: PropTypes.func, statusIds: ImmutablePropTypes.list.isRequired, intl: PropTypes.object.isRequired, columnId: PropTypes.string, @@ -68,7 +67,7 @@ class Bookmarks extends ImmutablePureComponent { }, 300, { leading: true }) render () { - const { intl, shouldUpdateScroll, statusIds, columnId, multiColumn, hasMore, isLoading } = this.props; + const { intl, statusIds, columnId, multiColumn, hasMore, isLoading } = this.props; const pinned = !!columnId; const emptyMessage = ; @@ -93,7 +92,6 @@ class Bookmarks extends ImmutablePureComponent { hasMore={hasMore} isLoading={isLoading} onLoadMore={this.handleLoadMore} - shouldUpdateScroll={shouldUpdateScroll} emptyMessage={emptyMessage} bindToDocument={!multiColumn} /> diff --git a/app/javascript/mastodon/features/community_timeline/index.js b/app/javascript/mastodon/features/community_timeline/index.js index b3cd39685cd790..30f77604867c25 100644 --- a/app/javascript/mastodon/features/community_timeline/index.js +++ b/app/javascript/mastodon/features/community_timeline/index.js @@ -41,7 +41,6 @@ class CommunityTimeline extends React.PureComponent { static propTypes = { dispatch: PropTypes.func.isRequired, - shouldUpdateScroll: PropTypes.func, columnId: PropTypes.string, intl: PropTypes.object.isRequired, hasUnread: PropTypes.bool, @@ -103,7 +102,7 @@ class CommunityTimeline extends React.PureComponent { } render () { - const { intl, shouldUpdateScroll, hasUnread, columnId, multiColumn, onlyMedia } = this.props; + const { intl, hasUnread, columnId, multiColumn, onlyMedia } = this.props; const pinned = !!columnId; return ( @@ -127,7 +126,6 @@ class CommunityTimeline extends React.PureComponent { timelineId={`community${onlyMedia ? ':media' : ''}`} onLoadMore={this.handleLoadMore} emptyMessage={} - shouldUpdateScroll={shouldUpdateScroll} bindToDocument={!multiColumn} /> diff --git a/app/javascript/mastodon/features/direct_timeline/components/conversations_list.js b/app/javascript/mastodon/features/direct_timeline/components/conversations_list.js index 4ee8e52122302a..fd1df7256896dc 100644 --- a/app/javascript/mastodon/features/direct_timeline/components/conversations_list.js +++ b/app/javascript/mastodon/features/direct_timeline/components/conversations_list.js @@ -14,7 +14,6 @@ export default class ConversationsList extends ImmutablePureComponent { hasMore: PropTypes.bool, isLoading: PropTypes.bool, onLoadMore: PropTypes.func, - shouldUpdateScroll: PropTypes.func, }; getCurrentIndex = id => this.props.conversations.findIndex(x => x.get('id') === id) diff --git a/app/javascript/mastodon/features/direct_timeline/index.js b/app/javascript/mastodon/features/direct_timeline/index.js index 5ce795760076f0..68523666c3beab 100644 --- a/app/javascript/mastodon/features/direct_timeline/index.js +++ b/app/javascript/mastodon/features/direct_timeline/index.js @@ -19,7 +19,6 @@ class DirectTimeline extends React.PureComponent { static propTypes = { dispatch: PropTypes.func.isRequired, - shouldUpdateScroll: PropTypes.func, columnId: PropTypes.string, intl: PropTypes.object.isRequired, hasUnread: PropTypes.bool, @@ -71,7 +70,7 @@ class DirectTimeline extends React.PureComponent { } render () { - const { intl, hasUnread, columnId, multiColumn, shouldUpdateScroll } = this.props; + const { intl, hasUnread, columnId, multiColumn } = this.props; const pinned = !!columnId; return ( @@ -93,7 +92,6 @@ class DirectTimeline extends React.PureComponent { timelineId='direct' onLoadMore={this.handleLoadMore} emptyMessage={} - shouldUpdateScroll={shouldUpdateScroll} /> ); diff --git a/app/javascript/mastodon/features/directory/index.js b/app/javascript/mastodon/features/directory/index.js index 2f91e759b554e5..88f20d33045299 100644 --- a/app/javascript/mastodon/features/directory/index.js +++ b/app/javascript/mastodon/features/directory/index.js @@ -12,7 +12,7 @@ import AccountCard from './components/account_card'; import RadioButton from 'mastodon/components/radio_button'; import classNames from 'classnames'; import LoadMore from 'mastodon/components/load_more'; -import { ScrollContainer } from 'react-router-scroll-4'; +import ScrollContainer from 'mastodon/containers/scroll_container'; const messages = defineMessages({ title: { id: 'column.directory', defaultMessage: 'Browse profiles' }, @@ -40,7 +40,6 @@ class Directory extends React.PureComponent { isLoading: PropTypes.bool, accountIds: ImmutablePropTypes.list.isRequired, dispatch: PropTypes.func.isRequired, - shouldUpdateScroll: PropTypes.func, columnId: PropTypes.string, intl: PropTypes.object.isRequired, multiColumn: PropTypes.bool, @@ -125,7 +124,7 @@ class Directory extends React.PureComponent { } render () { - const { isLoading, accountIds, intl, columnId, multiColumn, domain, shouldUpdateScroll } = this.props; + const { isLoading, accountIds, intl, columnId, multiColumn, domain } = this.props; const { order, local } = this.getParams(this.props, this.state); const pinned = !!columnId; @@ -163,7 +162,7 @@ class Directory extends React.PureComponent { multiColumn={multiColumn} /> - {multiColumn && !pinned ? {scrollableArea} : scrollableArea} + {multiColumn && !pinned ? {scrollableArea} : scrollableArea} ); } diff --git a/app/javascript/mastodon/features/domain_blocks/index.js b/app/javascript/mastodon/features/domain_blocks/index.js index a6d98891244505..edb80aef4100f2 100644 --- a/app/javascript/mastodon/features/domain_blocks/index.js +++ b/app/javascript/mastodon/features/domain_blocks/index.js @@ -29,7 +29,6 @@ class Blocks extends ImmutablePureComponent { static propTypes = { params: PropTypes.object.isRequired, dispatch: PropTypes.func.isRequired, - shouldUpdateScroll: PropTypes.func, hasMore: PropTypes.bool, domains: ImmutablePropTypes.orderedSet, intl: PropTypes.object.isRequired, @@ -45,7 +44,7 @@ class Blocks extends ImmutablePureComponent { }, 300, { leading: true }); render () { - const { intl, domains, shouldUpdateScroll, hasMore, multiColumn } = this.props; + const { intl, domains, hasMore, multiColumn } = this.props; if (!domains) { return ( @@ -64,7 +63,6 @@ class Blocks extends ImmutablePureComponent { scrollKey='domain_blocks' onLoadMore={this.handleLoadMore} hasMore={hasMore} - shouldUpdateScroll={shouldUpdateScroll} emptyMessage={emptyMessage} bindToDocument={!multiColumn} > diff --git a/app/javascript/mastodon/features/favourited_statuses/index.js b/app/javascript/mastodon/features/favourited_statuses/index.js index db8a3f815f1729..9606a144c6a117 100644 --- a/app/javascript/mastodon/features/favourited_statuses/index.js +++ b/app/javascript/mastodon/features/favourited_statuses/index.js @@ -27,7 +27,6 @@ class Favourites extends ImmutablePureComponent { static propTypes = { dispatch: PropTypes.func.isRequired, - shouldUpdateScroll: PropTypes.func, statusIds: ImmutablePropTypes.list.isRequired, intl: PropTypes.object.isRequired, columnId: PropTypes.string, @@ -68,7 +67,7 @@ class Favourites extends ImmutablePureComponent { }, 300, { leading: true }) render () { - const { intl, shouldUpdateScroll, statusIds, columnId, multiColumn, hasMore, isLoading } = this.props; + const { intl, statusIds, columnId, multiColumn, hasMore, isLoading } = this.props; const pinned = !!columnId; const emptyMessage = ; @@ -93,7 +92,6 @@ class Favourites extends ImmutablePureComponent { hasMore={hasMore} isLoading={isLoading} onLoadMore={this.handleLoadMore} - shouldUpdateScroll={shouldUpdateScroll} emptyMessage={emptyMessage} bindToDocument={!multiColumn} /> diff --git a/app/javascript/mastodon/features/favourites/index.js b/app/javascript/mastodon/features/favourites/index.js index 75cb00c0ea2bfc..ac94ae18a2a88f 100644 --- a/app/javascript/mastodon/features/favourites/index.js +++ b/app/javascript/mastodon/features/favourites/index.js @@ -27,7 +27,6 @@ class Favourites extends ImmutablePureComponent { static propTypes = { params: PropTypes.object.isRequired, dispatch: PropTypes.func.isRequired, - shouldUpdateScroll: PropTypes.func, accountIds: ImmutablePropTypes.list, multiColumn: PropTypes.bool, intl: PropTypes.object.isRequired, @@ -50,7 +49,7 @@ class Favourites extends ImmutablePureComponent { } render () { - const { intl, shouldUpdateScroll, accountIds, multiColumn } = this.props; + const { intl, accountIds, multiColumn } = this.props; if (!accountIds) { return ( @@ -74,7 +73,6 @@ class Favourites extends ImmutablePureComponent { diff --git a/app/javascript/mastodon/features/follow_requests/index.js b/app/javascript/mastodon/features/follow_requests/index.js index 18df9d25cd0e39..1f9b635bb78185 100644 --- a/app/javascript/mastodon/features/follow_requests/index.js +++ b/app/javascript/mastodon/features/follow_requests/index.js @@ -32,7 +32,6 @@ class FollowRequests extends ImmutablePureComponent { static propTypes = { params: PropTypes.object.isRequired, dispatch: PropTypes.func.isRequired, - shouldUpdateScroll: PropTypes.func, hasMore: PropTypes.bool, isLoading: PropTypes.bool, accountIds: ImmutablePropTypes.list, @@ -51,7 +50,7 @@ class FollowRequests extends ImmutablePureComponent { }, 300, { leading: true }); render () { - const { intl, shouldUpdateScroll, accountIds, hasMore, multiColumn, locked, domain, isLoading } = this.props; + const { intl, accountIds, hasMore, multiColumn, locked, domain, isLoading } = this.props; if (!accountIds) { return ( @@ -80,7 +79,6 @@ class FollowRequests extends ImmutablePureComponent { onLoadMore={this.handleLoadMore} hasMore={hasMore} isLoading={isLoading} - shouldUpdateScroll={shouldUpdateScroll} emptyMessage={emptyMessage} bindToDocument={!multiColumn} prepend={unlockedPrependMessage} diff --git a/app/javascript/mastodon/features/followers/index.js b/app/javascript/mastodon/features/followers/index.js index ae00d13d3b2967..ee747f0da6f61b 100644 --- a/app/javascript/mastodon/features/followers/index.js +++ b/app/javascript/mastodon/features/followers/index.js @@ -43,7 +43,6 @@ class Followers extends ImmutablePureComponent { static propTypes = { params: PropTypes.object.isRequired, dispatch: PropTypes.func.isRequired, - shouldUpdateScroll: PropTypes.func, accountIds: ImmutablePropTypes.list, hasMore: PropTypes.bool, isLoading: PropTypes.bool, @@ -73,7 +72,7 @@ class Followers extends ImmutablePureComponent { }, 300, { leading: true }); render () { - const { shouldUpdateScroll, accountIds, hasMore, blockedBy, isAccount, multiColumn, isLoading, remote, remoteUrl } = this.props; + const { accountIds, hasMore, blockedBy, isAccount, multiColumn, isLoading, remote, remoteUrl } = this.props; if (!isAccount) { return ( @@ -112,7 +111,6 @@ class Followers extends ImmutablePureComponent { hasMore={hasMore} isLoading={isLoading} onLoadMore={this.handleLoadMore} - shouldUpdateScroll={shouldUpdateScroll} prepend={} alwaysPrepend append={remoteMessage} diff --git a/app/javascript/mastodon/features/following/index.js b/app/javascript/mastodon/features/following/index.js index 666ec7a7f661ee..804df803edf047 100644 --- a/app/javascript/mastodon/features/following/index.js +++ b/app/javascript/mastodon/features/following/index.js @@ -43,7 +43,6 @@ class Following extends ImmutablePureComponent { static propTypes = { params: PropTypes.object.isRequired, dispatch: PropTypes.func.isRequired, - shouldUpdateScroll: PropTypes.func, accountIds: ImmutablePropTypes.list, hasMore: PropTypes.bool, isLoading: PropTypes.bool, @@ -73,7 +72,7 @@ class Following extends ImmutablePureComponent { }, 300, { leading: true }); render () { - const { shouldUpdateScroll, accountIds, hasMore, blockedBy, isAccount, multiColumn, isLoading, remote, remoteUrl } = this.props; + const { accountIds, hasMore, blockedBy, isAccount, multiColumn, isLoading, remote, remoteUrl } = this.props; if (!isAccount) { return ( @@ -112,7 +111,6 @@ class Following extends ImmutablePureComponent { hasMore={hasMore} isLoading={isLoading} onLoadMore={this.handleLoadMore} - shouldUpdateScroll={shouldUpdateScroll} prepend={} alwaysPrepend append={remoteMessage} diff --git a/app/javascript/mastodon/features/hashtag_timeline/index.js b/app/javascript/mastodon/features/hashtag_timeline/index.js index 5ccd9f8ea2ecd3..6a808eb306c482 100644 --- a/app/javascript/mastodon/features/hashtag_timeline/index.js +++ b/app/javascript/mastodon/features/hashtag_timeline/index.js @@ -24,7 +24,6 @@ class HashtagTimeline extends React.PureComponent { params: PropTypes.object.isRequired, columnId: PropTypes.string, dispatch: PropTypes.func.isRequired, - shouldUpdateScroll: PropTypes.func, hasUnread: PropTypes.bool, multiColumn: PropTypes.bool, }; @@ -130,7 +129,7 @@ class HashtagTimeline extends React.PureComponent { } render () { - const { shouldUpdateScroll, hasUnread, columnId, multiColumn } = this.props; + const { hasUnread, columnId, multiColumn } = this.props; const { id, local } = this.props.params; const pinned = !!columnId; @@ -156,7 +155,6 @@ class HashtagTimeline extends React.PureComponent { timelineId={`hashtag:${id}${local ? ':local' : ''}`} onLoadMore={this.handleLoadMore} emptyMessage={} - shouldUpdateScroll={shouldUpdateScroll} bindToDocument={!multiColumn} /> diff --git a/app/javascript/mastodon/features/home_timeline/index.js b/app/javascript/mastodon/features/home_timeline/index.js index b85c69af70c872..dc440f2fe00a9f 100644 --- a/app/javascript/mastodon/features/home_timeline/index.js +++ b/app/javascript/mastodon/features/home_timeline/index.js @@ -34,7 +34,6 @@ class HomeTimeline extends React.PureComponent { static propTypes = { dispatch: PropTypes.func.isRequired, - shouldUpdateScroll: PropTypes.func, intl: PropTypes.object.isRequired, hasUnread: PropTypes.bool, isPartial: PropTypes.bool, @@ -112,7 +111,7 @@ class HomeTimeline extends React.PureComponent { } render () { - const { intl, shouldUpdateScroll, hasUnread, columnId, multiColumn, hasAnnouncements, unreadAnnouncements, showAnnouncements } = this.props; + const { intl, hasUnread, columnId, multiColumn, hasAnnouncements, unreadAnnouncements, showAnnouncements } = this.props; const pinned = !!columnId; let announcementsButton = null; @@ -154,7 +153,6 @@ class HomeTimeline extends React.PureComponent { onLoadMore={this.handleLoadMore} timelineId='home' emptyMessage={ }} />} - shouldUpdateScroll={shouldUpdateScroll} bindToDocument={!multiColumn} /> diff --git a/app/javascript/mastodon/features/list_timeline/index.js b/app/javascript/mastodon/features/list_timeline/index.js index 8eb6456302bbaf..8010274f880254 100644 --- a/app/javascript/mastodon/features/list_timeline/index.js +++ b/app/javascript/mastodon/features/list_timeline/index.js @@ -41,7 +41,6 @@ class ListTimeline extends React.PureComponent { static propTypes = { params: PropTypes.object.isRequired, dispatch: PropTypes.func.isRequired, - shouldUpdateScroll: PropTypes.func, columnId: PropTypes.string, hasUnread: PropTypes.bool, multiColumn: PropTypes.bool, @@ -142,7 +141,7 @@ class ListTimeline extends React.PureComponent { } render () { - const { shouldUpdateScroll, hasUnread, columnId, multiColumn, list, intl } = this.props; + const { hasUnread, columnId, multiColumn, list, intl } = this.props; const { id } = this.props.params; const pinned = !!columnId; const title = list ? list.get('title') : id; @@ -207,7 +206,6 @@ class ListTimeline extends React.PureComponent { timelineId={`list:${id}`} onLoadMore={this.handleLoadMore} emptyMessage={} - shouldUpdateScroll={shouldUpdateScroll} bindToDocument={!multiColumn} /> diff --git a/app/javascript/mastodon/features/lists/index.js b/app/javascript/mastodon/features/lists/index.js index ca1fa1f5ef0ce1..29c31180988d44 100644 --- a/app/javascript/mastodon/features/lists/index.js +++ b/app/javascript/mastodon/features/lists/index.js @@ -48,7 +48,7 @@ class Lists extends ImmutablePureComponent { } render () { - const { intl, shouldUpdateScroll, lists, multiColumn } = this.props; + const { intl, lists, multiColumn } = this.props; if (!lists) { return ( @@ -68,7 +68,6 @@ class Lists extends ImmutablePureComponent { } bindToDocument={!multiColumn} diff --git a/app/javascript/mastodon/features/mutes/index.js b/app/javascript/mastodon/features/mutes/index.js index 17ff5c762adc12..c1d50d19406686 100644 --- a/app/javascript/mastodon/features/mutes/index.js +++ b/app/javascript/mastodon/features/mutes/index.js @@ -29,7 +29,6 @@ class Mutes extends ImmutablePureComponent { static propTypes = { params: PropTypes.object.isRequired, dispatch: PropTypes.func.isRequired, - shouldUpdateScroll: PropTypes.func, hasMore: PropTypes.bool, isLoading: PropTypes.bool, accountIds: ImmutablePropTypes.list, @@ -46,7 +45,7 @@ class Mutes extends ImmutablePureComponent { }, 300, { leading: true }); render () { - const { intl, shouldUpdateScroll, hasMore, accountIds, multiColumn, isLoading } = this.props; + const { intl, hasMore, accountIds, multiColumn, isLoading } = this.props; if (!accountIds) { return ( @@ -66,7 +65,6 @@ class Mutes extends ImmutablePureComponent { onLoadMore={this.handleLoadMore} hasMore={hasMore} isLoading={isLoading} - shouldUpdateScroll={shouldUpdateScroll} emptyMessage={emptyMessage} bindToDocument={!multiColumn} > diff --git a/app/javascript/mastodon/features/notifications/index.js b/app/javascript/mastodon/features/notifications/index.js index 1a621eca9a1519..a6a277d7ed9e2b 100644 --- a/app/javascript/mastodon/features/notifications/index.js +++ b/app/javascript/mastodon/features/notifications/index.js @@ -74,7 +74,6 @@ class Notifications extends React.PureComponent { notifications: ImmutablePropTypes.list.isRequired, showFilterBar: PropTypes.bool.isRequired, dispatch: PropTypes.func.isRequired, - shouldUpdateScroll: PropTypes.func, intl: PropTypes.object.isRequired, isLoading: PropTypes.bool, isUnread: PropTypes.bool, @@ -176,7 +175,7 @@ class Notifications extends React.PureComponent { }; render () { - const { intl, notifications, shouldUpdateScroll, isLoading, isUnread, columnId, multiColumn, hasMore, numPending, showFilterBar, lastReadId, canMarkAsRead, needsNotificationPermission } = this.props; + const { intl, notifications, isLoading, isUnread, columnId, multiColumn, hasMore, numPending, showFilterBar, lastReadId, canMarkAsRead, needsNotificationPermission } = this.props; const pinned = !!columnId; const emptyMessage = ; @@ -227,7 +226,6 @@ class Notifications extends React.PureComponent { onLoadPending={this.handleLoadPending} onScrollToTop={this.handleScrollToTop} onScroll={this.handleScroll} - shouldUpdateScroll={shouldUpdateScroll} bindToDocument={!multiColumn} > {scrollableContent} diff --git a/app/javascript/mastodon/features/pinned_statuses/index.js b/app/javascript/mastodon/features/pinned_statuses/index.js index ad5c9cafc367fc..f32bd6d23489d8 100644 --- a/app/javascript/mastodon/features/pinned_statuses/index.js +++ b/app/javascript/mastodon/features/pinned_statuses/index.js @@ -24,7 +24,6 @@ class PinnedStatuses extends ImmutablePureComponent { static propTypes = { dispatch: PropTypes.func.isRequired, - shouldUpdateScroll: PropTypes.func, statusIds: ImmutablePropTypes.list.isRequired, intl: PropTypes.object.isRequired, hasMore: PropTypes.bool.isRequired, @@ -44,7 +43,7 @@ class PinnedStatuses extends ImmutablePureComponent { } render () { - const { intl, shouldUpdateScroll, statusIds, hasMore, multiColumn } = this.props; + const { intl, statusIds, hasMore, multiColumn } = this.props; return ( @@ -53,7 +52,6 @@ class PinnedStatuses extends ImmutablePureComponent { statusIds={statusIds} scrollKey='pinned_statuses' hasMore={hasMore} - shouldUpdateScroll={shouldUpdateScroll} bindToDocument={!multiColumn} /> diff --git a/app/javascript/mastodon/features/public_timeline/index.js b/app/javascript/mastodon/features/public_timeline/index.js index 988b1b070be69d..b1d5518af55625 100644 --- a/app/javascript/mastodon/features/public_timeline/index.js +++ b/app/javascript/mastodon/features/public_timeline/index.js @@ -43,7 +43,6 @@ class PublicTimeline extends React.PureComponent { static propTypes = { dispatch: PropTypes.func.isRequired, - shouldUpdateScroll: PropTypes.func, intl: PropTypes.object.isRequired, columnId: PropTypes.string, multiColumn: PropTypes.bool, @@ -106,7 +105,7 @@ class PublicTimeline extends React.PureComponent { } render () { - const { intl, shouldUpdateScroll, columnId, hasUnread, multiColumn, onlyMedia, onlyRemote } = this.props; + const { intl, columnId, hasUnread, multiColumn, onlyMedia, onlyRemote } = this.props; const pinned = !!columnId; return ( @@ -130,7 +129,6 @@ class PublicTimeline extends React.PureComponent { trackScroll={!pinned} scrollKey={`public_timeline-${columnId}`} emptyMessage={} - shouldUpdateScroll={shouldUpdateScroll} bindToDocument={!multiColumn} /> diff --git a/app/javascript/mastodon/features/reblogs/index.js b/app/javascript/mastodon/features/reblogs/index.js index 4becb5fb7e8488..0fbd0941549baf 100644 --- a/app/javascript/mastodon/features/reblogs/index.js +++ b/app/javascript/mastodon/features/reblogs/index.js @@ -27,7 +27,6 @@ class Reblogs extends ImmutablePureComponent { static propTypes = { params: PropTypes.object.isRequired, dispatch: PropTypes.func.isRequired, - shouldUpdateScroll: PropTypes.func, accountIds: ImmutablePropTypes.list, multiColumn: PropTypes.bool, intl: PropTypes.object.isRequired, @@ -50,7 +49,7 @@ class Reblogs extends ImmutablePureComponent { } render () { - const { intl, shouldUpdateScroll, accountIds, multiColumn } = this.props; + const { intl, accountIds, multiColumn } = this.props; if (!accountIds) { return ( @@ -74,7 +73,6 @@ class Reblogs extends ImmutablePureComponent { diff --git a/app/javascript/mastodon/features/status/index.js b/app/javascript/mastodon/features/status/index.js index df8362a1bc7743..69cd245fbcec04 100644 --- a/app/javascript/mastodon/features/status/index.js +++ b/app/javascript/mastodon/features/status/index.js @@ -45,7 +45,7 @@ import { initBlockModal } from '../../actions/blocks'; import { initBoostModal } from '../../actions/boosts'; import { initReport } from '../../actions/reports'; import { makeGetStatus, makeGetPictureInPicture } from '../../selectors'; -import { ScrollContainer } from 'react-router-scroll-4'; +import ScrollContainer from 'mastodon/containers/scroll_container'; import ColumnBackButton from '../../components/column_back_button'; import ColumnHeader from '../../components/column_header'; import StatusContainer from '../../containers/status_container'; @@ -498,7 +498,7 @@ class Status extends ImmutablePureComponent { render () { let ancestors, descendants; - const { shouldUpdateScroll, status, ancestorsIds, descendantsIds, intl, domain, multiColumn, pictureInPicture } = this.props; + const { status, ancestorsIds, descendantsIds, intl, domain, multiColumn, pictureInPicture } = this.props; const { fullscreen } = this.state; if (status === null) { @@ -541,7 +541,7 @@ class Status extends ImmutablePureComponent { )} /> - +
{ancestors} diff --git a/app/javascript/mastodon/features/ui/components/audio_modal.js b/app/javascript/mastodon/features/ui/components/audio_modal.js index 0676bd9cf1ce24..c46fefce89d17b 100644 --- a/app/javascript/mastodon/features/ui/components/audio_modal.js +++ b/app/javascript/mastodon/features/ui/components/audio_modal.js @@ -4,7 +4,6 @@ import PropTypes from 'prop-types'; import Audio from 'mastodon/features/audio'; import { connect } from 'react-redux'; import ImmutablePureComponent from 'react-immutable-pure-component'; -import { previewState } from './video_modal'; import Footer from 'mastodon/features/picture_in_picture/components/footer'; const mapStateToProps = (state, { statusId }) => ({ @@ -25,32 +24,6 @@ class AudioModal extends ImmutablePureComponent { onChangeBackgroundColor: PropTypes.func.isRequired, }; - static contextTypes = { - router: PropTypes.object, - }; - - componentDidMount () { - if (this.context.router) { - const history = this.context.router.history; - - history.push(history.location.pathname, previewState); - - this.unlistenHistory = history.listen(() => { - this.props.onClose(); - }); - } - } - - componentWillUnmount () { - if (this.context.router) { - this.unlistenHistory(); - - if (this.context.router.history.location.state === previewState) { - this.context.router.history.goBack(); - } - } - } - render () { const { media, accountStaticAvatar, statusId, onClose } = this.props; const options = this.props.options || {}; diff --git a/app/javascript/mastodon/features/ui/components/media_modal.js b/app/javascript/mastodon/features/ui/components/media_modal.js index 08da1033042b64..061776e24e24cd 100644 --- a/app/javascript/mastodon/features/ui/components/media_modal.js +++ b/app/javascript/mastodon/features/ui/components/media_modal.js @@ -20,8 +20,6 @@ const messages = defineMessages({ next: { id: 'lightbox.next', defaultMessage: 'Next' }, }); -export const previewState = 'previewMediaModal'; - export default @injectIntl class MediaModal extends ImmutablePureComponent { @@ -37,10 +35,6 @@ class MediaModal extends ImmutablePureComponent { volume: PropTypes.number, }; - static contextTypes = { - router: PropTypes.object, - }; - state = { index: null, navigationHidden: false, @@ -98,16 +92,6 @@ class MediaModal extends ImmutablePureComponent { componentDidMount () { window.addEventListener('keydown', this.handleKeyDown, false); - if (this.context.router) { - const history = this.context.router.history; - - history.push(history.location.pathname, previewState); - - this.unlistenHistory = history.listen(() => { - this.props.onClose(); - }); - } - this._sendBackgroundColor(); } @@ -131,14 +115,6 @@ class MediaModal extends ImmutablePureComponent { componentWillUnmount () { window.removeEventListener('keydown', this.handleKeyDown); - if (this.context.router) { - this.unlistenHistory(); - - if (this.context.router.history.location.state === previewState) { - this.context.router.history.goBack(); - } - } - this.props.onChangeBackgroundColor(null); } diff --git a/app/javascript/mastodon/features/ui/components/video_modal.js b/app/javascript/mastodon/features/ui/components/video_modal.js index 2f13a175a1d1c2..7e8e1329d791ee 100644 --- a/app/javascript/mastodon/features/ui/components/video_modal.js +++ b/app/javascript/mastodon/features/ui/components/video_modal.js @@ -6,8 +6,6 @@ import ImmutablePureComponent from 'react-immutable-pure-component'; import Footer from 'mastodon/features/picture_in_picture/components/footer'; import { getAverageFromBlurhash } from 'mastodon/blurhash'; -export const previewState = 'previewVideoModal'; - export default class VideoModal extends ImmutablePureComponent { static propTypes = { @@ -22,19 +20,9 @@ export default class VideoModal extends ImmutablePureComponent { onChangeBackgroundColor: PropTypes.func.isRequired, }; - static contextTypes = { - router: PropTypes.object, - }; - componentDidMount () { - const { router } = this.context; const { media, onChangeBackgroundColor, onClose } = this.props; - if (router) { - router.history.push(router.history.location.pathname, previewState); - this.unlistenHistory = router.history.listen(() => onClose()); - } - const backgroundColor = getAverageFromBlurhash(media.get('blurhash')); if (backgroundColor) { @@ -42,18 +30,6 @@ export default class VideoModal extends ImmutablePureComponent { } } - componentWillUnmount () { - const { router } = this.context; - - if (router) { - this.unlistenHistory(); - - if (router.history.location.state === previewState) { - router.history.goBack(); - } - } - } - render () { const { media, statusId, onClose } = this.props; const options = this.props.options || {}; diff --git a/app/javascript/mastodon/features/ui/containers/modal_container.js b/app/javascript/mastodon/features/ui/containers/modal_container.js index 2d27180f7ed1cc..ad1e8a2eeaaad7 100644 --- a/app/javascript/mastodon/features/ui/containers/modal_container.js +++ b/app/javascript/mastodon/features/ui/containers/modal_container.js @@ -3,8 +3,8 @@ import { closeModal } from '../../../actions/modal'; import ModalRoot from '../components/modal_root'; const mapStateToProps = state => ({ - type: state.get('modal').modalType, - props: state.get('modal').modalProps, + type: state.getIn(['modal', 0, 'modalType'], null), + props: state.getIn(['modal', 0, 'modalProps'], {}), }); const mapDispatchToProps = dispatch => ({ diff --git a/app/javascript/mastodon/features/ui/index.js b/app/javascript/mastodon/features/ui/index.js index c1c6ac739705d0..756a692934fbf4 100644 --- a/app/javascript/mastodon/features/ui/index.js +++ b/app/javascript/mastodon/features/ui/index.js @@ -54,8 +54,6 @@ import { FollowRecommendations, } from './util/async-components'; import { me } from '../../initial_state'; -import { previewState as previewMediaState } from './components/media_modal'; -import { previewState as previewVideoState } from './components/video_modal'; import { closeOnboarding, INTRODUCTION_VERSION } from 'mastodon/actions/onboarding'; // Dummy import, to make sure that ends up in the application bundle. @@ -138,10 +136,6 @@ class SwitchingColumnsArea extends React.PureComponent { } } - shouldUpdateScroll (_, { location }) { - return location.state !== previewMediaState && location.state !== previewVideoState; - } - setRef = c => { if (c) { this.node = c.getWrappedInstance(); @@ -158,38 +152,38 @@ class SwitchingColumnsArea extends React.PureComponent { {redirect} - - - - - - - - - + + + + + + + + + - + - + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/app/javascript/mastodon/reducers/modal.js b/app/javascript/mastodon/reducers/modal.js index cb53887c749e0c..ea81b433235368 100644 --- a/app/javascript/mastodon/reducers/modal.js +++ b/app/javascript/mastodon/reducers/modal.js @@ -1,19 +1,15 @@ import { MODAL_OPEN, MODAL_CLOSE } from '../actions/modal'; import { TIMELINE_DELETE } from '../actions/timelines'; +import { Stack as ImmutableStack, Map as ImmutableMap } from 'immutable'; -const initialState = { - modalType: null, - modalProps: {}, -}; - -export default function modal(state = initialState, action) { +export default function modal(state = ImmutableStack(), action) { switch(action.type) { case MODAL_OPEN: - return { modalType: action.modalType, modalProps: action.modalProps }; + return state.unshift(ImmutableMap({ modalType: action.modalType, modalProps: action.modalProps })); case MODAL_CLOSE: - return (action.modalType === undefined || action.modalType === state.modalType) ? initialState : state; + return (action.modalType === undefined || action.modalType === state.getIn([0, 'modalType'])) ? state.shift() : state; case TIMELINE_DELETE: - return (state.modalProps.statusId === action.id) ? initialState : state; + return state.filterNot((modal) => modal.get('modalProps').statusId === action.id); default: return state; } From c3a6f7b9411a86a8fce71ef99d8801d8930bb8f0 Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 14 Jul 2021 05:35:49 +0200 Subject: [PATCH 33/81] Fix user email address being banned on self-deletion (#16503) * Add tests * Fix user email address being banned on self-deletion Fixes #16498 --- app/controllers/settings/deletes_controller.rb | 2 +- app/models/account.rb | 4 ++-- spec/controllers/settings/deletes_controller_spec.rb | 4 ++++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/controllers/settings/deletes_controller.rb b/app/controllers/settings/deletes_controller.rb index 7b8f8d20788d9f..e0dd5edcb19762 100644 --- a/app/controllers/settings/deletes_controller.rb +++ b/app/controllers/settings/deletes_controller.rb @@ -42,7 +42,7 @@ def challenge_passed? end def destroy_account! - current_account.suspend!(origin: :local) + current_account.suspend!(origin: :local, block_email: false) AccountDeletionWorker.perform_async(current_user.account_id) sign_out end diff --git a/app/models/account.rb b/app/models/account.rb index c15b25d305f1cc..b2ce539dff9f84 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -232,11 +232,11 @@ def suspended_temporarily? suspended? && deletion_request.present? end - def suspend!(date: Time.now.utc, origin: :local) + def suspend!(date: Time.now.utc, origin: :local, block_email: true) transaction do create_deletion_request! update!(suspended_at: date, suspension_origin: origin) - create_canonical_email_block! + create_canonical_email_block! if block_email end end diff --git a/spec/controllers/settings/deletes_controller_spec.rb b/spec/controllers/settings/deletes_controller_spec.rb index 8d5c4774fd8541..92ab401c964ad2 100644 --- a/spec/controllers/settings/deletes_controller_spec.rb +++ b/spec/controllers/settings/deletes_controller_spec.rb @@ -59,6 +59,10 @@ expect(user.account.reload).to be_suspended end + it 'does not create an email block' do + expect(CanonicalEmailBlock.block?(user.email)).to be false + end + context 'when suspended' do let(:user) { Fabricate(:user, account_attributes: { username: 'alice', suspended_at: Time.now.utc }) } From f60bb0784fdf2ac67582ba4e96779acc078d9902 Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 5 Nov 2021 21:14:35 +0100 Subject: [PATCH 34/81] Fix handling announcements with links (#16941) Broken since #15827 --- app/models/status.rb | 2 +- ...lish_scheduled_announcement_worker_spec.rb | 26 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 spec/workers/publish_scheduled_announcement_worker_spec.rb diff --git a/app/models/status.rb b/app/models/status.rb index 3acf759f9de0a6..c7f761bc6ba58f 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -338,7 +338,7 @@ def permitted_for(target_account, account) def from_text(text) return [] if text.blank? - text.scan(FetchLinkCardService::URL_PATTERN).map(&:first).uniq.filter_map do |url| + text.scan(FetchLinkCardService::URL_PATTERN).map(&:second).uniq.filter_map do |url| status = begin if TagManager.instance.local_url?(url) ActivityPub::TagManager.instance.uri_to_resource(url, Status) diff --git a/spec/workers/publish_scheduled_announcement_worker_spec.rb b/spec/workers/publish_scheduled_announcement_worker_spec.rb new file mode 100644 index 00000000000000..0977bba1ee157a --- /dev/null +++ b/spec/workers/publish_scheduled_announcement_worker_spec.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe PublishScheduledAnnouncementWorker do + subject { described_class.new } + + let!(:remote_account) { Fabricate(:account, domain: 'domain.com', username: 'foo', uri: 'https://domain.com/users/foo') } + let!(:remote_status) { Fabricate(:status, uri: 'https://domain.com/users/foo/12345', account: remote_account) } + let!(:local_status) { Fabricate(:status) } + let(:scheduled_announcement) { Fabricate(:announcement, text: "rebooting very soon, see #{ActivityPub::TagManager.instance.uri_for(remote_status)} and #{ActivityPub::TagManager.instance.uri_for(local_status)}") } + + describe 'perform' do + before do + service = double + allow(FetchRemoteStatusService).to receive(:new).and_return(service) + allow(service).to receive(:call).with('https://domain.com/users/foo/12345') { remote_status.reload } + + subject.perform(scheduled_announcement.id) + end + + it 'updates the linked statuses' do + expect(scheduled_announcement.reload.status_ids).to eq [remote_status.id, local_status.id] + end + end +end From 3251b8eeadbeb34fa7846b240ee48df963f16562 Mon Sep 17 00:00:00 2001 From: Claire Date: Sat, 6 Nov 2021 00:13:58 +0100 Subject: [PATCH 35/81] Fix reviving revoked sessions and invalidating login (#16943) Up until now, we have used Devise's Rememberable mechanism to re-log users after the end of their browser sessions. This mechanism relies on a signed cookie containing a token. That token was stored on the user's record, meaning it was shared across all logged in browsers, meaning truly revoking a browser's ability to auto-log-in involves revoking the token itself, and revoking access from *all* logged-in browsers. We had a session mechanism that dynamically checks whether a user's session has been disabled, and would log out the user if so. However, this would only clear a session being actively used, and a new one could be respawned with the `remember_user_token` cookie. In practice, this caused two issues: - sessions could be revived after being closed from /auth/edit (security issue) - auto-log-in would be disabled for *all* browsers after logging out from one of them This PR removes the `remember_token` mechanism and treats the `_session_id` cookie/token as a browser-specific `remember_token`, fixing both issues. --- app/controllers/auth/passwords_controller.rb | 1 - .../auth/registrations_controller.rb | 3 -- app/controllers/auth/sessions_controller.rb | 2 - app/models/user.rb | 2 +- config/initializers/devise.rb | 39 +++++++++++++++++-- 5 files changed, 37 insertions(+), 10 deletions(-) diff --git a/app/controllers/auth/passwords_controller.rb b/app/controllers/auth/passwords_controller.rb index 5db2668f72dde5..2996c0431b9fa2 100644 --- a/app/controllers/auth/passwords_controller.rb +++ b/app/controllers/auth/passwords_controller.rb @@ -10,7 +10,6 @@ def update super do |resource| if resource.errors.empty? resource.session_activations.destroy_all - resource.forget_me! end end end diff --git a/app/controllers/auth/registrations_controller.rb b/app/controllers/auth/registrations_controller.rb index a3114ab2532aad..3c1730f25f4bf3 100644 --- a/app/controllers/auth/registrations_controller.rb +++ b/app/controllers/auth/registrations_controller.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true class Auth::RegistrationsController < Devise::RegistrationsController - include Devise::Controllers::Rememberable include RegistrationSpamConcern layout :determine_layout @@ -30,8 +29,6 @@ def update super do |resource| if resource.saved_change_to_encrypted_password? resource.clear_other_sessions(current_session.session_id) - resource.forget_me! - remember_me(resource) end end end diff --git a/app/controllers/auth/sessions_controller.rb b/app/controllers/auth/sessions_controller.rb index ecb5a0a0384337..6ebd860378b3fc 100644 --- a/app/controllers/auth/sessions_controller.rb +++ b/app/controllers/auth/sessions_controller.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true class Auth::SessionsController < Devise::SessionsController - include Devise::Controllers::Rememberable - layout 'auth' skip_before_action :require_no_authentication, only: [:create] diff --git a/app/models/user.rb b/app/models/user.rb index 4973c68b61bd0b..b3fb55dfc018a1 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -63,7 +63,7 @@ class User < ApplicationRecord devise :two_factor_backupable, otp_number_of_backup_codes: 10 - devise :registerable, :recoverable, :rememberable, :validatable, + devise :registerable, :recoverable, :validatable, :confirmable include Omniauthable diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index ef612e177de2fc..5232e6cfda9d32 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -1,3 +1,5 @@ +require 'devise/strategies/authenticatable' + Warden::Manager.after_set_user except: :fetch do |user, warden| if user.session_active?(warden.cookies.signed['_session_id'] || warden.raw_session['auth_id']) session_id = warden.cookies.signed['_session_id'] || warden.raw_session['auth_id'] @@ -72,17 +74,48 @@ module Devise mattr_accessor :ldap_uid_conversion_replace @@ldap_uid_conversion_replace = nil - class Strategies::PamAuthenticatable - def valid? - super && ::Devise.pam_authentication + module Strategies + class PamAuthenticatable + def valid? + super && ::Devise.pam_authentication + end + end + + class SessionActivationRememberable < Authenticatable + def valid? + @session_cookie = nil + session_cookie.present? + end + + def authenticate! + resource = SessionActivation.find_by(session_id: session_cookie)&.user + + unless resource + cookies.delete('_session_id') + return pass + end + + if validate(resource) + success!(resource) + end + end + + private + + def session_cookie + @session_cookie ||= cookies.signed['_session_id'] + end end end end +Warden::Strategies.add(:session_activation_rememberable, Devise::Strategies::SessionActivationRememberable) + Devise.setup do |config| config.warden do |manager| manager.default_strategies(scope: :user).unshift :two_factor_ldap_authenticatable if Devise.ldap_authentication manager.default_strategies(scope: :user).unshift :two_factor_pam_authenticatable if Devise.pam_authentication + manager.default_strategies(scope: :user).unshift :session_activation_rememberable manager.default_strategies(scope: :user).unshift :two_factor_authenticatable manager.default_strategies(scope: :user).unshift :two_factor_backupable end From 76c2028859e5eef4ac4a07d5b3f4538eec933b88 Mon Sep 17 00:00:00 2001 From: Claire Date: Sat, 6 Nov 2021 00:12:25 +0100 Subject: [PATCH 36/81] Fix AccountNote not having a maximum length (#16942) --- app/models/account_note.rb | 1 + app/workers/move_worker.rb | 8 +++- .../api/v1/accounts/notes_controller_spec.rb | 48 +++++++++++++++++++ spec/workers/move_worker_spec.rb | 41 +++++++++++----- 4 files changed, 86 insertions(+), 12 deletions(-) create mode 100644 spec/controllers/api/v1/accounts/notes_controller_spec.rb diff --git a/app/models/account_note.rb b/app/models/account_note.rb index bf61df923f509c..b338bc92f28f13 100644 --- a/app/models/account_note.rb +++ b/app/models/account_note.rb @@ -17,4 +17,5 @@ class AccountNote < ApplicationRecord belongs_to :target_account, class_name: 'Account' validates :account_id, uniqueness: { scope: :target_account_id } + validates :comment, length: { maximum: 2_000 } end diff --git a/app/workers/move_worker.rb b/app/workers/move_worker.rb index cc2c17d32bf602..4a900e3b80ffbe 100644 --- a/app/workers/move_worker.rb +++ b/app/workers/move_worker.rb @@ -53,10 +53,16 @@ def copy_account_notes! new_note = AccountNote.find_by(account: note.account, target_account: @target_account) if new_note.nil? - AccountNote.create!(account: note.account, target_account: @target_account, comment: [text, note.comment].join("\n")) + begin + AccountNote.create!(account: note.account, target_account: @target_account, comment: [text, note.comment].join("\n")) + rescue ActiveRecord::RecordInvalid + AccountNote.create!(account: note.account, target_account: @target_account, comment: note.comment) + end else new_note.update!(comment: [text, note.comment, "\n", new_note.comment].join("\n")) end + rescue ActiveRecord::RecordInvalid + nil rescue => e @deferred_error = e end diff --git a/spec/controllers/api/v1/accounts/notes_controller_spec.rb b/spec/controllers/api/v1/accounts/notes_controller_spec.rb new file mode 100644 index 00000000000000..0a2957fedece68 --- /dev/null +++ b/spec/controllers/api/v1/accounts/notes_controller_spec.rb @@ -0,0 +1,48 @@ +require 'rails_helper' + +describe Api::V1::Accounts::NotesController do + render_views + + let(:user) { Fabricate(:user, account: Fabricate(:account, username: 'alice')) } + let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'write:accounts') } + let(:account) { Fabricate(:account) } + let(:comment) { 'foo' } + + before do + allow(controller).to receive(:doorkeeper_token) { token } + end + + describe 'POST #create' do + subject do + post :create, params: { account_id: account.id, comment: comment } + end + + context 'when account note has reasonable length' do + let(:comment) { 'foo' } + + it 'returns http success' do + subject + expect(response).to have_http_status(200) + end + + it 'updates account note' do + subject + expect(AccountNote.find_by(account_id: user.account.id, target_account_id: account.id).comment).to eq comment + end + end + + context 'when account note exceends allowed length' do + let(:comment) { 'a' * 2_001 } + + it 'returns 422' do + subject + expect(response).to have_http_status(422) + end + + it 'does not create account note' do + subject + expect(AccountNote.where(account_id: user.account.id, target_account_id: account.id).exists?).to be_falsey + end + end + end +end diff --git a/spec/workers/move_worker_spec.rb b/spec/workers/move_worker_spec.rb index 8ab4f182fd0b7b..82449b0c7e1e26 100644 --- a/spec/workers/move_worker_spec.rb +++ b/spec/workers/move_worker_spec.rb @@ -9,7 +9,8 @@ let(:source_account) { Fabricate(:account, protocol: :activitypub, domain: 'example.com') } let(:target_account) { Fabricate(:account, protocol: :activitypub, domain: 'example.com') } let(:local_user) { Fabricate(:user) } - let!(:account_note) { Fabricate(:account_note, account: local_user.account, target_account: source_account) } + let(:comment) { 'old note prior to move' } + let!(:account_note) { Fabricate(:account_note, account: local_user.account, target_account: source_account, comment: comment) } let(:block_service) { double } @@ -26,19 +27,37 @@ end shared_examples 'user note handling' do - it 'copies user note' do - subject.perform(source_account.id, target_account.id) - expect(AccountNote.find_by(account: account_note.account, target_account: target_account).comment).to include(source_account.acct) - expect(AccountNote.find_by(account: account_note.account, target_account: target_account).comment).to include(account_note.comment) + context 'when user notes are short enough' do + it 'copies user note with prelude' do + subject.perform(source_account.id, target_account.id) + expect(AccountNote.find_by(account: account_note.account, target_account: target_account).comment).to include(source_account.acct) + expect(AccountNote.find_by(account: account_note.account, target_account: target_account).comment).to include(account_note.comment) + end + + it 'merges user notes when needed' do + new_account_note = AccountNote.create!(account: account_note.account, target_account: target_account, comment: 'new note prior to move') + + subject.perform(source_account.id, target_account.id) + expect(AccountNote.find_by(account: account_note.account, target_account: target_account).comment).to include(source_account.acct) + expect(AccountNote.find_by(account: account_note.account, target_account: target_account).comment).to include(account_note.comment) + expect(AccountNote.find_by(account: account_note.account, target_account: target_account).comment).to include(new_account_note.comment) + end end - it 'merges user notes when needed' do - new_account_note = AccountNote.create!(account: account_note.account, target_account: target_account, comment: 'new note prior to move') + context 'when user notes are too long' do + let(:comment) { 'abc' * 333 } - subject.perform(source_account.id, target_account.id) - expect(AccountNote.find_by(account: account_note.account, target_account: target_account).comment).to include(source_account.acct) - expect(AccountNote.find_by(account: account_note.account, target_account: target_account).comment).to include(account_note.comment) - expect(AccountNote.find_by(account: account_note.account, target_account: target_account).comment).to include(new_account_note.comment) + it 'copies user note without prelude' do + subject.perform(source_account.id, target_account.id) + expect(AccountNote.find_by(account: account_note.account, target_account: target_account).comment).to include(account_note.comment) + end + + it 'keeps user notes unchanged' do + new_account_note = AccountNote.create!(account: account_note.account, target_account: target_account, comment: 'new note prior to move') + + subject.perform(source_account.id, target_account.id) + expect(AccountNote.find_by(account: account_note.account, target_account: target_account).comment).to include(new_account_note.comment) + end end end From 8a74d851d2035cf7adbb605b7e994334152fbfc4 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 6 Nov 2021 00:09:41 +0100 Subject: [PATCH 37/81] Bump version to 3.4.2 --- CHANGELOG.md | 43 +++++++++++++++++++++++++++++++++++++++++ lib/mastodon/version.rb | 2 +- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b64da5b52f5ccf..0055112c6d5bb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,49 @@ Changelog All notable changes to this project will be documented in this file. +## [3.4.2] - 2021-11-06 +### Added + +- Add `configuration` attribute to `GET /api/v1/instance` ([Gargron](https://github.com/mastodon/mastodon/pull/16485)) + +### Fixed + +- Fix handling of back button with modal windows in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16499)) +- Fix pop-in player when author has long username in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16468)) +- Fix crash when a status with a playing video gets deleted in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16384)) +- Fix crash with Microsoft Translate in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16525)) +- Fix PWA not being usable from alternate domains ([HolgerHuo](https://github.com/mastodon/mastodon/pull/16714)) +- Fix locale-specific number rounding errors ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16469)) +- Fix scheduling a status decreasing status count ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16791)) +- Fix user's canonical email address being blocked when user deletes own account ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16503)) +- Fix not being able to suspend users that already have their canonical e-mail blocked ([Gargron](https://github.com/mastodon/mastodon/pull/16455)) +- Fix anonymous access to outbox not being cached by the reverse proxy ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16458)) +- Fix followers synchronization mechanism not working when URI has empty path ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16744)) +- Fix serialization of counts in REST API when user hides their network ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16418)) +- Fix inefficiencies in auto-linking code ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16506)) +- Fix `tootctl self-destruct` not sending delete activities for recently-suspended accounts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16688)) +- Fix suspicious sign-in e-mail text being out of date ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16690)) +- Fix some frameworks being unnecessarily loaded ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16725)) +- Fix canonical e-mail blocks missing foreign key constraints ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16448)) +- Fix inconsistent order on account's statuses page in admin UI ([tribela](https://github.com/mastodon/mastodon/pull/16937)) +- Fix media from blocked domains being redownloaded by `tootctl media refresh` ([tribela](https://github.com/mastodon/mastodon/pull/16914)) +- Fix `mastodon:setup` generated env-file syntax ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16896)) +- Fix link previews being incorrectly generated from earlier links ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16885)) +- Fix wrong `to`/`cc` values for remote groups in ActivityPub ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16700)) +- Fix mentions with non-ascii TLDs not being processed ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16689)) +- Fix authentication failures halfway through a sign-in attempt ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16607), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/16792)) +- Fix suspended accounts statuses being merged back into timelines ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16628)) +- Fix crash when encountering invalid account fields ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16598)) +- Fix invalid blurhash handling for remote activities ([noellabo](https://github.com/mastodon/mastodon/pull/16583)) +- Fix newlines being added to accout notes when an account moves ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16415), [noellabo](https://github.com/mastodon/mastodon/pull/16576)) +- Fix crash when creating an announcement with links ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16941)) +- Fix logging out from one browser logging out all other sessions ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16943)) + +### Security + +- Fix user notes not having a length limit ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16942)) +- Fix revoking a specific session not working ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16943)) + ## [3.4.1] - 2021-06-03 ### Added diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 478d5a43d711fd..faab4f2957d222 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -13,7 +13,7 @@ def minor end def patch - 1 + 2 end def flags From 5c47a18c8df3231aa25c6d1f140a71a7fac9cbf9 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 6 Nov 2021 05:17:39 +0100 Subject: [PATCH 38/81] Fix login being broken due to inaccurately applied backport fix in 3.4.2 See #16943 --- app/controllers/auth/sessions_controller.rb | 1 - .../concerns/sign_in_token_authentication_concern.rb | 1 - app/controllers/concerns/two_factor_authentication_concern.rb | 2 -- 3 files changed, 4 deletions(-) diff --git a/app/controllers/auth/sessions_controller.rb b/app/controllers/auth/sessions_controller.rb index 6ebd860378b3fc..630b0dee5da85b 100644 --- a/app/controllers/auth/sessions_controller.rb +++ b/app/controllers/auth/sessions_controller.rb @@ -24,7 +24,6 @@ def new def create super do |resource| resource.update_sign_in!(request, new_sign_in: true) - remember_me(resource) flash.delete(:notice) end end diff --git a/app/controllers/concerns/sign_in_token_authentication_concern.rb b/app/controllers/concerns/sign_in_token_authentication_concern.rb index 33ea5cf8572124..2b5e79115e5358 100644 --- a/app/controllers/concerns/sign_in_token_authentication_concern.rb +++ b/app/controllers/concerns/sign_in_token_authentication_concern.rb @@ -34,7 +34,6 @@ def authenticate_with_sign_in_token def authenticate_with_sign_in_token_attempt(user) if valid_sign_in_token_attempt?(user) clear_attempt_from_session - remember_me(user) sign_in(user) else flash.now[:alert] = I18n.t('users.invalid_sign_in_token') diff --git a/app/controllers/concerns/two_factor_authentication_concern.rb b/app/controllers/concerns/two_factor_authentication_concern.rb index 402638eba91bd4..d65f072ffb278f 100644 --- a/app/controllers/concerns/two_factor_authentication_concern.rb +++ b/app/controllers/concerns/two_factor_authentication_concern.rb @@ -57,7 +57,6 @@ def authenticate_with_two_factor_via_webauthn(user) if valid_webauthn_credential?(user, webauthn_credential) clear_attempt_from_session - remember_me(user) sign_in(user) render json: { redirect_path: root_path }, status: :ok else @@ -68,7 +67,6 @@ def authenticate_with_two_factor_via_webauthn(user) def authenticate_with_two_factor_via_otp(user) if valid_otp_attempt?(user) clear_attempt_from_session - remember_me(user) sign_in(user) else flash.now[:alert] = I18n.t('users.invalid_otp_token') From 4b6668868e92b83c740849ddd397ca3ff5026a86 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 6 Nov 2021 05:19:38 +0100 Subject: [PATCH 39/81] Bump version to 3.4.3 --- CHANGELOG.md | 5 +++++ lib/mastodon/version.rb | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0055112c6d5bb7..c0c6c5315d22ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,11 @@ Changelog All notable changes to this project will be documented in this file. +## [3.4.3] - 2021-11-06 +### Fixed + +- Fix login being broken due to inaccurately applied backport fix in 3.4.2 ([Gargron](https://github.com/mastodon/mastodon/commit/5c47a18c8df3231aa25c6d1f140a71a7fac9cbf9)) + ## [3.4.2] - 2021-11-06 ### Added diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index faab4f2957d222..37f08ad930e6b9 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -13,7 +13,7 @@ def minor end def patch - 2 + 3 end def flags From 77d0297313ca7d0a26ec820381e93030a974c1a9 Mon Sep 17 00:00:00 2001 From: Claire Date: Sat, 17 Jul 2021 17:06:52 +0200 Subject: [PATCH 40/81] Fix replying from modal (#16516) Fixes #16515 Not using a router object somehow made `this.history` lag behind the real browser history whenever pushing a new history item in `replyCompose`. Not using the context-provided router in this case was an oversight made when porting glitch-soc changes in #16499. --- app/javascript/mastodon/components/modal_root.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/javascript/mastodon/components/modal_root.js b/app/javascript/mastodon/components/modal_root.js index 8c9409d0144bbb..755c46fd6b20e6 100644 --- a/app/javascript/mastodon/components/modal_root.js +++ b/app/javascript/mastodon/components/modal_root.js @@ -6,6 +6,10 @@ import { multiply } from 'color-blend'; export default class ModalRoot extends React.PureComponent { + static contextTypes = { + router: PropTypes.object, + }; + static propTypes = { children: PropTypes.node, onClose: PropTypes.func.isRequired, From 9c610ca0a41b1a317188a94d7532e9937dda47b8 Mon Sep 17 00:00:00 2001 From: Claire Date: Sun, 25 Jul 2021 01:13:46 +0200 Subject: [PATCH 41/81] =?UTF-8?q?Fix=20=E2=80=9Copen=E2=80=9D=20link=20of?= =?UTF-8?q?=20media=20modal=20not=20closing=20modal=20(#16524)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../features/picture_in_picture/components/footer.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/javascript/mastodon/features/picture_in_picture/components/footer.js b/app/javascript/mastodon/features/picture_in_picture/components/footer.js index 1ecb18bf820593..f5ce50ac81c951 100644 --- a/app/javascript/mastodon/features/picture_in_picture/components/footer.js +++ b/app/javascript/mastodon/features/picture_in_picture/components/footer.js @@ -114,7 +114,11 @@ class Footer extends ImmutablePureComponent { return; } - const { status } = this.props; + const { status, onClose } = this.props; + + if (onClose) { + onClose(); + } router.history.push(`/statuses/${status.get('id')}`); } From 1bcb3daf7e4255662d6185a2fb74c2263f75060a Mon Sep 17 00:00:00 2001 From: Takeshi Umeda Date: Fri, 6 Aug 2021 19:14:13 +0900 Subject: [PATCH 42/81] Fix logout link not working in safari (#16574) --- .../features/compose/containers/navigation_container.js | 1 + app/javascript/mastodon/features/compose/index.js | 1 + .../features/ui/components/confirmation_modal.js | 9 ++++++++- .../mastodon/features/ui/components/link_footer.js | 1 + 4 files changed, 11 insertions(+), 1 deletion(-) diff --git a/app/javascript/mastodon/features/compose/containers/navigation_container.js b/app/javascript/mastodon/features/compose/containers/navigation_container.js index 8606a642e49781..654c14df9b3912 100644 --- a/app/javascript/mastodon/features/compose/containers/navigation_container.js +++ b/app/javascript/mastodon/features/compose/containers/navigation_container.js @@ -21,6 +21,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({ dispatch(openModal('CONFIRM', { message: intl.formatMessage(messages.logoutMessage), confirm: intl.formatMessage(messages.logoutConfirm), + closeWhenConfirm: false, onConfirm: () => logOut(), })); }, diff --git a/app/javascript/mastodon/features/compose/index.js b/app/javascript/mastodon/features/compose/index.js index e2de8b0e6a20d1..c1bce0a3f42cf2 100644 --- a/app/javascript/mastodon/features/compose/index.js +++ b/app/javascript/mastodon/features/compose/index.js @@ -74,6 +74,7 @@ class Compose extends React.PureComponent { dispatch(openModal('CONFIRM', { message: intl.formatMessage(messages.logoutMessage), confirm: intl.formatMessage(messages.logoutConfirm), + closeWhenConfirm: false, onConfirm: () => logOut(), })); diff --git a/app/javascript/mastodon/features/ui/components/confirmation_modal.js b/app/javascript/mastodon/features/ui/components/confirmation_modal.js index 1227fa453e4f37..65d97ca1610c95 100644 --- a/app/javascript/mastodon/features/ui/components/confirmation_modal.js +++ b/app/javascript/mastodon/features/ui/components/confirmation_modal.js @@ -13,15 +13,22 @@ class ConfirmationModal extends React.PureComponent { onConfirm: PropTypes.func.isRequired, secondary: PropTypes.string, onSecondary: PropTypes.func, + closeWhenConfirm: PropTypes.bool, intl: PropTypes.object.isRequired, }; + static defaultProps = { + closeWhenConfirm: true, + }; + componentDidMount() { this.button.focus(); } handleClick = () => { - this.props.onClose(); + if (this.props.closeWhenConfirm) { + this.props.onClose(); + } this.props.onConfirm(); } diff --git a/app/javascript/mastodon/features/ui/components/link_footer.js b/app/javascript/mastodon/features/ui/components/link_footer.js index 3c4cff9f9c6de8..00977d94cc07da 100644 --- a/app/javascript/mastodon/features/ui/components/link_footer.js +++ b/app/javascript/mastodon/features/ui/components/link_footer.js @@ -17,6 +17,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({ dispatch(openModal('CONFIRM', { message: intl.formatMessage(messages.logoutMessage), confirm: intl.formatMessage(messages.logoutConfirm), + closeWhenConfirm: false, onConfirm: () => logOut(), })); }, From e65ede1ac538501883f7cb68516e9bada2ef25fe Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 16 Nov 2021 21:36:28 +0100 Subject: [PATCH 43/81] Fix upload of remote media with OpenStack Swift sometimes failing (#16998) Under certain conditions, files fetched from remotes trigger an error when being uploaded using OpenStack Swift. This is because in some cases, the remote server will not return a content-length, so our ResponseWithLimitAdapter will hold a `nil` value for `#size`, which will lead to an invalid value for the Content-Length header of the Swift API call. This commit fixes that by taking the size from the actually-downloaded file size rather than the upstream-provided Content-Length header value. --- lib/paperclip/response_with_limit_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/paperclip/response_with_limit_adapter.rb b/lib/paperclip/response_with_limit_adapter.rb index 17a2abd25f2c79..deb89717a40591 100644 --- a/lib/paperclip/response_with_limit_adapter.rb +++ b/lib/paperclip/response_with_limit_adapter.rb @@ -17,9 +17,9 @@ def initialize(target, options = {}) def cache_current_values @original_filename = filename_from_content_disposition.presence || filename_from_path.presence || 'data' - @size = @target.response.content_length @tempfile = copy_to_tempfile(@target) @content_type = ContentTypeDetector.new(@tempfile.path).detect + @size = File.size(@tempfile) end def copy_to_tempfile(source) From 22cd1e6ab593830dc667ef09235faed0b7547891 Mon Sep 17 00:00:00 2001 From: Claire Date: Sun, 14 Nov 2021 21:55:40 +0100 Subject: [PATCH 44/81] Fix confusing error when webfinger request returns empty document (#16986) For some reason, some misconfigured servers return an empty document when queried over webfinger. Since an empty document does not lead to a parse error, the error is not caught properly and triggers uncaught exceptions later on. This PR fixes that by immediately erroring out with `Webfinger::Error` on getting an empty response. --- app/lib/webfinger.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/lib/webfinger.rb b/app/lib/webfinger.rb index e0e022cea0dd92..1ffb5b4bf37cef 100644 --- a/app/lib/webfinger.rb +++ b/app/lib/webfinger.rb @@ -46,7 +46,9 @@ def perform def body_from_webfinger(url = standard_url, use_fallback = true) webfinger_request(url).perform do |res| if res.code == 200 - res.body_with_limit + body = res.body_with_limit + raise Webfinger::Error, "Request for #{@uri} returned empty response" if body.empty? + body elsif res.code == 404 && use_fallback body_from_host_meta elsif res.code == 410 From e5113a8cad23f686129ef04e908afee9ad0f6ada Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 19 Nov 2021 18:22:49 +0100 Subject: [PATCH 45/81] Fix overflow of long profile fields in admin view (#17010) --- app/javascript/styles/mastodon/admin.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/app/javascript/styles/mastodon/admin.scss b/app/javascript/styles/mastodon/admin.scss index 25ebc19b099da5..2744c6c6c3abba 100644 --- a/app/javascript/styles/mastodon/admin.scss +++ b/app/javascript/styles/mastodon/admin.scss @@ -829,6 +829,7 @@ a.name-tag, padding: 0 5px; margin-bottom: 10px; flex: 1 0 50%; + max-width: 100%; } .account__header__fields, From 3c18311d860e829a3bbefe34f166f701242f925a Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 24 Nov 2021 17:41:03 +0100 Subject: [PATCH 46/81] Fix error when suspending user with an already-existing canonical email block (#17036) * Fix error when suspending user with an already-existing canonical email block Fixes #17033 While attempting to create a `CanonicalEmailBlock` with an existing hash would raise an `ActiveRecord::RecordNotUnique` error, this being done within a transaction would cancel the whole transaction. For this reason, checking for uniqueness in Rails would query the database within the transaction and avoid invalidating the whole transaction for this reason. A race condition is still possible, where multiple accounts sharing a canonical email would be blocked in concurrent transactions, in which only one would succeed, but that is way less likely to happen that the current issue, and can always be retried after the first failure, unlike the current situation. * Add tests --- app/models/canonical_email_block.rb | 2 +- spec/models/account_spec.rb | 31 +++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/app/models/canonical_email_block.rb b/app/models/canonical_email_block.rb index a8546d65af6ce7..be8c45bfe3cb31 100644 --- a/app/models/canonical_email_block.rb +++ b/app/models/canonical_email_block.rb @@ -15,7 +15,7 @@ class CanonicalEmailBlock < ApplicationRecord belongs_to :reference_account, class_name: 'Account' - validates :canonical_email_hash, presence: true + validates :canonical_email_hash, presence: true, uniqueness: true def email=(email) self.canonical_email_hash = email_to_canonical_email_hash(email) diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb index 03d6f5fb0f547a..65e6714c0e7edf 100644 --- a/spec/models/account_spec.rb +++ b/spec/models/account_spec.rb @@ -5,6 +5,37 @@ let(:bob) { Fabricate(:account, username: 'bob') } subject { Fabricate(:account) } + describe '#suspend!' do + it 'marks the account as suspended' do + subject.suspend! + expect(subject.suspended?).to be true + end + + it 'creates a deletion request' do + subject.suspend! + expect(AccountDeletionRequest.where(account: subject).exists?).to be true + end + + context 'when the account is of a local user' do + let!(:subject) { Fabricate(:account, user: Fabricate(:user, email: 'foo+bar@domain.org')) } + + it 'creates a canonical domain block' do + subject.suspend! + expect(CanonicalEmailBlock.block?(subject.user_email)).to be true + end + + context 'when a canonical domain block already exists for that email' do + before do + Fabricate(:canonical_email_block, email: subject.user_email) + end + + it 'does not raise an error' do + expect { subject.suspend! }.not_to raise_error + end + end + end + end + describe '#follow!' do it 'creates a follow' do follow = subject.follow!(bob) From 5e4b04de8853822a9b0c6b124d0427ac9df2327e Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 25 Nov 2021 23:46:39 +0100 Subject: [PATCH 47/81] Fix handling of recursive toots in WebUI (#17041) --- app/javascript/mastodon/features/status/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/javascript/mastodon/features/status/index.js b/app/javascript/mastodon/features/status/index.js index 69cd245fbcec04..907236bb812bb9 100644 --- a/app/javascript/mastodon/features/status/index.js +++ b/app/javascript/mastodon/features/status/index.js @@ -83,7 +83,7 @@ const makeMapStateToProps = () => { ancestorsIds = ancestorsIds.withMutations(mutable => { let id = statusId; - while (id) { + while (id && !mutable.includes(id)) { mutable.unshift(id); id = inReplyTos.get(id); } @@ -101,7 +101,7 @@ const makeMapStateToProps = () => { const ids = [statusId]; while (ids.length > 0) { - let id = ids.shift(); + let id = ids.pop(); const replies = contextReplies.get(id); if (statusId !== id) { @@ -110,7 +110,7 @@ const makeMapStateToProps = () => { if (replies) { replies.reverse().forEach(reply => { - ids.unshift(reply); + if (!ids.includes(reply) && !descendantsIds.includes(reply) && statusId !== reply) ids.push(reply); }); } } From f264cca1d20dcd1d80343d31f7c960f1579b57ad Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 25 Nov 2021 23:46:30 +0100 Subject: [PATCH 48/81] Fix filtering DMs from non-followed users (#17042) --- app/services/notify_service.rb | 43 +++++++++++++++++++++++++++- spec/services/notify_service_spec.rb | 17 +++++++++-- 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/app/services/notify_service.rb b/app/services/notify_service.rb index fc187db4030673..e78c74d1edd9a3 100644 --- a/app/services/notify_service.rb +++ b/app/services/notify_service.rb @@ -67,8 +67,49 @@ def direct_message? message? && @notification.target_status.direct_visibility? end + # Returns true if the sender has been mentionned by the recipient up the thread def response_to_recipient? - @notification.target_status.in_reply_to_account_id == @recipient.id && @notification.target_status.thread&.direct_visibility? + return false if @notification.target_status.in_reply_to_id.nil? + + # Using an SQL CTE to avoid unneeded back-and-forth with SQL server in case of long threads + !Status.count_by_sql([<<-SQL.squish, id: @notification.target_status.in_reply_to_id, recipient_id: @recipient.id, sender_id: @notification.from_account.id]).zero? + WITH RECURSIVE ancestors(id, in_reply_to_id, replying_to_sender) AS ( + SELECT + s.id, s.in_reply_to_id, (CASE + WHEN s.account_id = :recipient_id THEN + EXISTS ( + SELECT * + FROM mentions m + WHERE m.silent = FALSE AND m.account_id = :sender_id AND m.status_id = s.id + ) + ELSE + FALSE + END) + FROM statuses s + WHERE s.id = :id + UNION ALL + SELECT + s.id, + s.in_reply_to_id, + (CASE + WHEN s.account_id = :recipient_id THEN + EXISTS ( + SELECT * + FROM mentions m + WHERE m.silent = FALSE AND m.account_id = :sender_id AND m.status_id = s.id + ) + ELSE + FALSE + END) + FROM ancestors st + JOIN statuses s ON s.id = st.in_reply_to_id + WHERE st.replying_to_sender IS FALSE + ) + SELECT COUNT(*) + FROM ancestors st + JOIN statuses s ON s.id = st.id + WHERE st.replying_to_sender IS TRUE AND s.visibility = 3 + SQL end def from_staff? diff --git a/spec/services/notify_service_spec.rb b/spec/services/notify_service_spec.rb index f2cb22c5ef7508..7433866b76db14 100644 --- a/spec/services/notify_service_spec.rb +++ b/spec/services/notify_service_spec.rb @@ -64,8 +64,9 @@ is_expected.to_not change(Notification, :count) end - context 'if the message chain initiated by recipient, but is not direct message' do + context 'if the message chain is initiated by recipient, but is not direct message' do let(:reply_to) { Fabricate(:status, account: recipient) } + let!(:mention) { Fabricate(:mention, account: sender, status: reply_to) } let(:activity) { Fabricate(:mention, account: recipient, status: Fabricate(:status, account: sender, visibility: :direct, thread: reply_to)) } it 'does not notify' do @@ -73,8 +74,20 @@ end end - context 'if the message chain initiated by recipient and is direct message' do + context 'if the message chain is initiated by recipient, but without a mention to the sender, even if the sender sends multiple messages in a row' do + let(:reply_to) { Fabricate(:status, account: recipient) } + let!(:mention) { Fabricate(:mention, account: sender, status: reply_to) } + let(:dummy_reply) { Fabricate(:status, account: sender, visibility: :direct, thread: reply_to) } + let(:activity) { Fabricate(:mention, account: recipient, status: Fabricate(:status, account: sender, visibility: :direct, thread: dummy_reply)) } + + it 'does not notify' do + is_expected.to_not change(Notification, :count) + end + end + + context 'if the message chain is initiated by the recipient with a mention to the sender' do let(:reply_to) { Fabricate(:status, account: recipient, visibility: :direct) } + let!(:mention) { Fabricate(:mention, account: sender, status: reply_to) } let(:activity) { Fabricate(:mention, account: recipient, status: Fabricate(:status, account: sender, visibility: :direct, thread: reply_to)) } it 'does notify' do From 4cd33a2c712acf038d856caaad077e34398306d2 Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 11 Nov 2021 14:00:30 +0100 Subject: [PATCH 49/81] Fix "bundle exec rails mastodon:setup" crashing in some circumstances (#16976) Fix regression from #16896 --- lib/tasks/mastodon.rake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/tasks/mastodon.rake b/lib/tasks/mastodon.rake index 9146f78e108b44..d905a07dad42a3 100644 --- a/lib/tasks/mastodon.rake +++ b/lib/tasks/mastodon.rake @@ -350,11 +350,11 @@ namespace :mastodon do end end.join("\n") - generated_header = "# Generated with mastodon:setup on #{Time.now.utc}\n\n" + generated_header = "# Generated with mastodon:setup on #{Time.now.utc}\n\n".dup if incompatible_syntax - generated_header << "Some variables in this file will be interpreted differently whether you are\n" - generated_header << "using docker-compose or not.\n\n" + generated_header << "# Some variables in this file will be interpreted differently whether you are\n" + generated_header << "# using docker-compose or not.\n\n" end File.write(Rails.root.join('.env.production'), "#{generated_header}#{env_contents}\n") From fd868f8ca0b13afdb3f6fb9a3be063ccec7e1349 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 26 Nov 2021 01:27:08 +0100 Subject: [PATCH 50/81] Bump version to 3.4.4 --- CHANGELOG.md | 17 +++++++++++++++++ lib/mastodon/version.rb | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c0c6c5315d22ec..deca765faf7dc5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,23 @@ Changelog All notable changes to this project will be documented in this file. +## [3.4.4] - 2021-11-26 +### Fixed + +- Fix error when suspending user with an already blocked canonical email ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17036)) +- Fix overflow of long profile fields in admin UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17010)) +- Fix confusing error when WebFinger request returns empty document ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16986)) +- Fix upload of remote media with OpenStack Swift sometimes failing ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16998)) +- Fix logout link not working in Safari ([noellabo](https://github.com/mastodon/mastodon/pull/16574)) +- Fix “open” link of media modal not closing modal in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16524)) +- Fix replying from modal in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16516)) +- Fix `mastodon:setup` command crashing in some circumstances ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16976)) + +### Security + +- Fix filtering DMs from non-followed users ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17042)) +- Fix handling of recursive toots in WebUI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17041)) + ## [3.4.3] - 2021-11-06 ### Fixed diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 37f08ad930e6b9..49347997707255 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -13,7 +13,7 @@ def minor end def patch - 3 + 4 end def flags From b782f86b51e9d9653173b5fc79d5392e8ffebe31 Mon Sep 17 00:00:00 2001 From: Claire Date: Sun, 30 Jan 2022 21:38:54 +0100 Subject: [PATCH 51/81] Fix some old migration scripts (#17394) * Fix some old migration scripts * Fix edge case in two-step migration from older releases --- ...3_remove_faux_remote_account_duplicates.rb | 40 +++++++++++++++++++ .../20190715164535_add_instance_actor.rb | 5 +++ .../20191007013357_update_pt_locales.rb | 4 ++ db/views/follow_recommendations_v01.sql | 4 +- db/views/follow_recommendations_v02.sql | 4 +- 5 files changed, 53 insertions(+), 4 deletions(-) diff --git a/db/migrate/20181026034033_remove_faux_remote_account_duplicates.rb b/db/migrate/20181026034033_remove_faux_remote_account_duplicates.rb index bd4f4c2a36852f..40537e9c9e027b 100644 --- a/db/migrate/20181026034033_remove_faux_remote_account_duplicates.rb +++ b/db/migrate/20181026034033_remove_faux_remote_account_duplicates.rb @@ -1,6 +1,46 @@ class RemoveFauxRemoteAccountDuplicates < ActiveRecord::Migration[5.2] disable_ddl_transaction! + class StreamEntry < ApplicationRecord + # Dummy class, to make migration possible across version changes + belongs_to :account, inverse_of: :stream_entries + end + + class Status < ApplicationRecord + # Dummy class, to make migration possible across version changes + belongs_to :account, inverse_of: :statuses + has_many :favourites, inverse_of: :status, dependent: :destroy + has_many :mentions, dependent: :destroy, inverse_of: :status + end + + class Favourite < ApplicationRecord + # Dummy class, to make migration possible across version changes + belongs_to :account, inverse_of: :favourites + belongs_to :status, inverse_of: :favourites + end + + class Mention < ApplicationRecord + # Dummy class, to make migration possible across version changes + belongs_to :account, inverse_of: :mentions + belongs_to :status + end + + class Notification < ApplicationRecord + # Dummy class, to make migration possible across version changes + belongs_to :account, optional: true + belongs_to :from_account, class_name: 'Account', optional: true + belongs_to :activity, polymorphic: true, optional: true + end + + class Account < ApplicationRecord + # Dummy class, to make migration possible across version changes + has_many :stream_entries, inverse_of: :account, dependent: :destroy + has_many :statuses, inverse_of: :account, dependent: :destroy + has_many :favourites, inverse_of: :account, dependent: :destroy + has_many :mentions, inverse_of: :account, dependent: :destroy + has_many :notifications, inverse_of: :account, dependent: :destroy + end + def up local_domain = Rails.configuration.x.local_domain diff --git a/db/migrate/20190715164535_add_instance_actor.rb b/db/migrate/20190715164535_add_instance_actor.rb index a26d54949320aa..8c0301d69d8bf3 100644 --- a/db/migrate/20190715164535_add_instance_actor.rb +++ b/db/migrate/20190715164535_add_instance_actor.rb @@ -1,4 +1,9 @@ class AddInstanceActor < ActiveRecord::Migration[5.2] + class Account < ApplicationRecord + # Dummy class, to make migration possible across version changes + validates :username, uniqueness: { scope: :domain, case_sensitive: false } + end + def up Account.create!(id: -99, actor_type: 'Application', locked: true, username: Rails.configuration.x.local_domain) end diff --git a/db/migrate/20191007013357_update_pt_locales.rb b/db/migrate/20191007013357_update_pt_locales.rb index b7288d38a0d7c8..9e8f8b42416539 100644 --- a/db/migrate/20191007013357_update_pt_locales.rb +++ b/db/migrate/20191007013357_update_pt_locales.rb @@ -1,4 +1,8 @@ class UpdatePtLocales < ActiveRecord::Migration[5.2] + class User < ApplicationRecord + # Dummy class, to make migration possible across version changes + end + disable_ddl_transaction! def up diff --git a/db/views/follow_recommendations_v01.sql b/db/views/follow_recommendations_v01.sql index 799abeaee597d1..8295bbc0f0ad1b 100644 --- a/db/views/follow_recommendations_v01.sql +++ b/db/views/follow_recommendations_v01.sql @@ -20,7 +20,7 @@ FROM ( HAVING count(follows.id) >= 5 UNION ALL SELECT accounts.id AS account_id, - sum(reblogs_count + favourites_count) / (1.0 + sum(reblogs_count + favourites_count)) AS rank, + sum(status_stats.reblogs_count + status_stats.favourites_count) / (1.0 + sum(status_stats.reblogs_count + status_stats.favourites_count)) AS rank, 'most_interactions' AS reason FROM status_stats INNER JOIN statuses ON statuses.id = status_stats.status_id @@ -32,7 +32,7 @@ FROM ( AND accounts.locked = 'f' AND accounts.discoverable = 't' GROUP BY accounts.id - HAVING sum(reblogs_count + favourites_count) >= 5 + HAVING sum(status_stats.reblogs_count + status_stats.favourites_count) >= 5 ) t0 GROUP BY account_id ORDER BY rank DESC diff --git a/db/views/follow_recommendations_v02.sql b/db/views/follow_recommendations_v02.sql index 673c5cc85db881..f67c6eecf1fd60 100644 --- a/db/views/follow_recommendations_v02.sql +++ b/db/views/follow_recommendations_v02.sql @@ -18,7 +18,7 @@ FROM ( HAVING count(follows.id) >= 5 UNION ALL SELECT account_summaries.account_id AS account_id, - sum(reblogs_count + favourites_count) / (1.0 + sum(reblogs_count + favourites_count)) AS rank, + sum(status_stats.reblogs_count + status_stats.favourites_count) / (1.0 + sum(status_stats.reblogs_count + status_stats.favourites_count)) AS rank, 'most_interactions' AS reason FROM status_stats INNER JOIN statuses ON statuses.id = status_stats.status_id @@ -28,7 +28,7 @@ FROM ( AND account_summaries.sensitive = 'f' AND follow_recommendation_suppressions.id IS NULL GROUP BY account_summaries.account_id - HAVING sum(reblogs_count + favourites_count) >= 5 + HAVING sum(status_stats.reblogs_count + status_stats.favourites_count) >= 5 ) t0 GROUP BY account_id ORDER BY rank DESC From 0dc103ea11c134897b74eac167404a2f83dec783 Mon Sep 17 00:00:00 2001 From: Claire Date: Sun, 30 Jan 2022 22:34:54 +0100 Subject: [PATCH 52/81] Fix edge case in migration helpers that caused crash because of PostgreSQL quirks (#17398) --- lib/mastodon/migration_helpers.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mastodon/migration_helpers.rb b/lib/mastodon/migration_helpers.rb index 39a6e0680800ba..5bc903349af104 100644 --- a/lib/mastodon/migration_helpers.rb +++ b/lib/mastodon/migration_helpers.rb @@ -295,7 +295,7 @@ def update_column_in_batches(table_name, column, value) table = Arel::Table.new(table_name) total = estimate_rows_in_table(table_name).to_i - if total == 0 + if total < 1 count_arel = table.project(Arel.star.count.as('count')) count_arel = yield table, count_arel if block_given? From 959234c1e43f339abab093c62afaef96f6c52cb9 Mon Sep 17 00:00:00 2001 From: Jeong Arm Date: Sun, 26 Dec 2021 06:52:24 +0900 Subject: [PATCH 53/81] Save bundle config as local (#17188) Some bundle options are saved as global user config and not project local. Specially, `deployment` must be saved as local config to be run on copied environment --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 962e5a8c9b26ed..cbcdb20345e4fd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -54,8 +54,8 @@ RUN npm install -g yarn && \ COPY Gemfile* package.json yarn.lock /opt/mastodon/ RUN cd /opt/mastodon && \ - bundle config set deployment 'true' && \ - bundle config set without 'development test' && \ + bundle config set --local deployment 'true' && \ + bundle config set --local without 'development test' && \ bundle install -j"$(nproc)" && \ yarn install --pure-lockfile From 2363b026e6e948fb53ee487804c3a084992ec13a Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 10 Sep 2021 15:43:28 +0200 Subject: [PATCH 54/81] Bump ruby-saml from 1.11.0 to 1.13.0 (#16723) Fixes #16720 --- Gemfile.lock | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 201893d477fc41..5b71445c8d6b5b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -545,8 +545,9 @@ GEM rack (>= 1.1) rubocop (>= 1.7.0, < 2.0) ruby-progressbar (1.11.0) - ruby-saml (1.11.0) - nokogiri (>= 1.5.10) + ruby-saml (1.13.0) + nokogiri (>= 1.10.5) + rexml ruby2_keywords (0.0.4) rufus-scheduler (3.6.0) fugit (~> 1.1, >= 1.1.6) From 0ae91e45de7bbb26561e3557ad3276102ac6c489 Mon Sep 17 00:00:00 2001 From: Yusuke Nakamura Date: Fri, 12 Nov 2021 13:18:29 +0900 Subject: [PATCH 55/81] Build container image by GitHub Actions (#16973) * Build container image by GitHub Actions * Trigger docker build only pushed to main branch * Tweak tagging imgae - "edge" is the main branch - "latest" is the tagged latest release --- .github/workflows/build-image.yml | 34 +++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 .github/workflows/build-image.yml diff --git a/.github/workflows/build-image.yml b/.github/workflows/build-image.yml new file mode 100644 index 00000000000000..2ec6b3d4b21668 --- /dev/null +++ b/.github/workflows/build-image.yml @@ -0,0 +1,34 @@ +name: Build container image +on: + push: + branches: + - "main" + tags: + - "*" +jobs: + build-image: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: docker/setup-buildx-action@v1 + - uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - uses: docker/metadata-action@v3 + id: meta + with: + images: ghcr.io/${{ github.repository_owner }}/mastodon + flavor: | + latest=true + tags: | + type=edge,branch=main + type=semver,pattern={{ raw }} + - uses: docker/build-push-action@v2 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + cache-from: type=registry,ref=ghcr.io/${{ github.repository_owner }}/mastodon:latest + cache-to: type=inline From c8301bcfc339d50d46c10440d8261add86a65635 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sun, 14 Nov 2021 06:11:05 +0100 Subject: [PATCH 56/81] Change workflow to push to Docker Hub (#16980) --- .github/workflows/build-image.yml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build-image.yml b/.github/workflows/build-image.yml index 2ec6b3d4b21668..ae4c2aff935873 100644 --- a/.github/workflows/build-image.yml +++ b/.github/workflows/build-image.yml @@ -13,15 +13,14 @@ jobs: - uses: docker/setup-buildx-action@v1 - uses: docker/login-action@v1 with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} - uses: docker/metadata-action@v3 id: meta with: - images: ghcr.io/${{ github.repository_owner }}/mastodon + images: tootsuite/mastodon flavor: | - latest=true + latest=auto tags: | type=edge,branch=main type=semver,pattern={{ raw }} @@ -30,5 +29,5 @@ jobs: context: . push: true tags: ${{ steps.meta.outputs.tags }} - cache-from: type=registry,ref=ghcr.io/${{ github.repository_owner }}/mastodon:latest + cache-from: type=registry,ref=tootsuite/mastodon:latest cache-to: type=inline From 2c83b9076d8d43714344bd428f796ec6805177b6 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 16 Nov 2021 21:42:14 +0100 Subject: [PATCH 57/81] Add manual GitHub Actions runs (#17000) --- .github/workflows/build-image.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-image.yml b/.github/workflows/build-image.yml index ae4c2aff935873..58f2813d31bbb6 100644 --- a/.github/workflows/build-image.yml +++ b/.github/workflows/build-image.yml @@ -1,5 +1,6 @@ name: Build container image on: + workflow_dispatch: push: branches: - "main" From 03f0e98b3277fd34d95f1501700deb44e85c9bf9 Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 11 Aug 2021 17:48:42 +0200 Subject: [PATCH 58/81] Fix followers synchronization mechanism not working when URI has empty path (#16510) * Fix followers synchronization mechanism not working when URI has empty path To my knowledge, there is no current implementation on the fediverse that can use bare domains (e.g., actor is at https://example.org instead of something like https://example.org/actor) that also plans to support the followers synchronization mechanism. However, Mastodon's current implementation would exclude such accounts from followers list. Also adds tests and rename them to reflect the proper method names. * Move url prefix regexp to its own constant --- app/models/account.rb | 3 +- app/models/concerns/account_interactions.rb | 9 ++- app/workers/activitypub/delivery_worker.rb | 6 +- .../concerns/account_interactions_spec.rb | 61 +++++++++++-------- .../activitypub/delivery_worker_spec.rb | 2 +- 5 files changed, 46 insertions(+), 35 deletions(-) diff --git a/app/models/account.rb b/app/models/account.rb index b2ce539dff9f84..2f2a55b553681b 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -60,6 +60,7 @@ class Account < ApplicationRecord USERNAME_RE = /[a-z0-9_]+([a-z0-9_\.-]+[a-z0-9_]+)?/i MENTION_RE = /(?<=^|[^\/[:word:]])@((#{USERNAME_RE})(?:@[[:word:]\.\-]+[[:word:]]+)?)/i + URL_PREFIX_RE = /\Ahttp(s?):\/\/[^\/]+/ include AccountAssociations include AccountAvatar @@ -379,7 +380,7 @@ def preferred_inbox_url def synchronization_uri_prefix return 'local' if local? - @synchronization_uri_prefix ||= uri[/http(s?):\/\/[^\/]+\//] + @synchronization_uri_prefix ||= "#{uri[URL_PREFIX_RE]}/" end class Field < ActiveModelSerializers::Model diff --git a/app/models/concerns/account_interactions.rb b/app/models/concerns/account_interactions.rb index 958f6c78e2a2fb..763567f42b152c 100644 --- a/app/models/concerns/account_interactions.rb +++ b/app/models/concerns/account_interactions.rb @@ -251,10 +251,13 @@ def lists_for_local_distribution .where('users.current_sign_in_at > ?', User::ACTIVE_DURATION.ago) end - def remote_followers_hash(url_prefix) - Rails.cache.fetch("followers_hash:#{id}:#{url_prefix}") do + def remote_followers_hash(url) + url_prefix = url[Account::URL_PREFIX_RE] + return if url_prefix.blank? + + Rails.cache.fetch("followers_hash:#{id}:#{url_prefix}/") do digest = "\x00" * 32 - followers.where(Account.arel_table[:uri].matches(url_prefix + '%', false, true)).pluck_each(:uri) do |uri| + followers.where(Account.arel_table[:uri].matches("#{Account.sanitize_sql_like(url_prefix)}/%", false, true)).or(followers.where(uri: url_prefix)).pluck_each(:uri) do |uri| Xorcist.xor!(digest, Digest::SHA256.digest(uri)) end digest.unpack('H*')[0] diff --git a/app/workers/activitypub/delivery_worker.rb b/app/workers/activitypub/delivery_worker.rb index 6c5a576a703c4e..788f2cf8094264 100644 --- a/app/workers/activitypub/delivery_worker.rb +++ b/app/workers/activitypub/delivery_worker.rb @@ -44,11 +44,7 @@ def build_request(http_client) end def synchronization_header - "collectionId=\"#{account_followers_url(@source_account)}\", digest=\"#{@source_account.remote_followers_hash(inbox_url_prefix)}\", url=\"#{account_followers_synchronization_url(@source_account)}\"" - end - - def inbox_url_prefix - @inbox_url[/http(s?):\/\/[^\/]+\//] + "collectionId=\"#{account_followers_url(@source_account)}\", digest=\"#{@source_account.remote_followers_hash(@inbox_url)}\", url=\"#{account_followers_synchronization_url(@source_account)}\"" end def perform_request diff --git a/spec/models/concerns/account_interactions_spec.rb b/spec/models/concerns/account_interactions_spec.rb index 85fbf7e79ca513..ca243ebc5ed31a 100644 --- a/spec/models/concerns/account_interactions_spec.rb +++ b/spec/models/concerns/account_interactions_spec.rb @@ -539,46 +539,57 @@ end end - describe '#followers_hash' do + describe '#remote_followers_hash' do let(:me) { Fabricate(:account, username: 'Me') } let(:remote_1) { Fabricate(:account, username: 'alice', domain: 'example.org', uri: 'https://example.org/users/alice') } let(:remote_2) { Fabricate(:account, username: 'bob', domain: 'example.org', uri: 'https://example.org/users/bob') } - let(:remote_3) { Fabricate(:account, username: 'eve', domain: 'foo.org', uri: 'https://foo.org/users/eve') } + let(:remote_3) { Fabricate(:account, username: 'instance-actor', domain: 'example.org', uri: 'https://example.org') } + let(:remote_4) { Fabricate(:account, username: 'eve', domain: 'foo.org', uri: 'https://foo.org/users/eve') } before do remote_1.follow!(me) remote_2.follow!(me) remote_3.follow!(me) + remote_4.follow!(me) me.follow!(remote_1) end - context 'on a local user' do - it 'returns correct hash for remote domains' do - expect(me.remote_followers_hash('https://example.org/')).to eq '707962e297b7bd94468a21bc8e506a1bcea607a9142cd64e27c9b106b2a5f6ec' - expect(me.remote_followers_hash('https://foo.org/')).to eq 'ccb9c18a67134cfff9d62c7f7e7eb88e6b803446c244b84265565f4eba29df0e' - end + it 'returns correct hash for remote domains' do + expect(me.remote_followers_hash('https://example.org/')).to eq '20aecbe774b3d61c25094370baf370012b9271c5b172ecedb05caff8d79ef0c7' + expect(me.remote_followers_hash('https://foo.org/')).to eq 'ccb9c18a67134cfff9d62c7f7e7eb88e6b803446c244b84265565f4eba29df0e' + expect(me.remote_followers_hash('https://foo.org.evil.com/')).to eq '0000000000000000000000000000000000000000000000000000000000000000' + expect(me.remote_followers_hash('https://foo')).to eq '0000000000000000000000000000000000000000000000000000000000000000' + end - it 'invalidates cache as needed when removing or adding followers' do - expect(me.remote_followers_hash('https://example.org/')).to eq '707962e297b7bd94468a21bc8e506a1bcea607a9142cd64e27c9b106b2a5f6ec' - remote_1.unfollow!(me) - expect(me.remote_followers_hash('https://example.org/')).to eq '241b00794ce9b46aa864f3220afadef128318da2659782985bac5ed5bd436bff' - remote_1.follow!(me) - expect(me.remote_followers_hash('https://example.org/')).to eq '707962e297b7bd94468a21bc8e506a1bcea607a9142cd64e27c9b106b2a5f6ec' - end + it 'invalidates cache as needed when removing or adding followers' do + expect(me.remote_followers_hash('https://example.org/')).to eq '20aecbe774b3d61c25094370baf370012b9271c5b172ecedb05caff8d79ef0c7' + remote_3.unfollow!(me) + expect(me.remote_followers_hash('https://example.org/')).to eq '707962e297b7bd94468a21bc8e506a1bcea607a9142cd64e27c9b106b2a5f6ec' + remote_1.unfollow!(me) + expect(me.remote_followers_hash('https://example.org/')).to eq '241b00794ce9b46aa864f3220afadef128318da2659782985bac5ed5bd436bff' + remote_1.follow!(me) + expect(me.remote_followers_hash('https://example.org/')).to eq '707962e297b7bd94468a21bc8e506a1bcea607a9142cd64e27c9b106b2a5f6ec' end + end - context 'on a remote user' do - it 'returns correct hash for remote domains' do - expect(remote_1.local_followers_hash).to eq Digest::SHA256.hexdigest(ActivityPub::TagManager.instance.uri_for(me)) - end + describe '#local_followers_hash' do + let(:me) { Fabricate(:account, username: 'Me') } + let(:remote_1) { Fabricate(:account, username: 'alice', domain: 'example.org', uri: 'https://example.org/users/alice') } - it 'invalidates cache as needed when removing or adding followers' do - expect(remote_1.local_followers_hash).to eq Digest::SHA256.hexdigest(ActivityPub::TagManager.instance.uri_for(me)) - me.unfollow!(remote_1) - expect(remote_1.local_followers_hash).to eq '0000000000000000000000000000000000000000000000000000000000000000' - me.follow!(remote_1) - expect(remote_1.local_followers_hash).to eq Digest::SHA256.hexdigest(ActivityPub::TagManager.instance.uri_for(me)) - end + before do + me.follow!(remote_1) + end + + it 'returns correct hash for local users' do + expect(remote_1.local_followers_hash).to eq Digest::SHA256.hexdigest(ActivityPub::TagManager.instance.uri_for(me)) + end + + it 'invalidates cache as needed when removing or adding followers' do + expect(remote_1.local_followers_hash).to eq Digest::SHA256.hexdigest(ActivityPub::TagManager.instance.uri_for(me)) + me.unfollow!(remote_1) + expect(remote_1.local_followers_hash).to eq '0000000000000000000000000000000000000000000000000000000000000000' + me.follow!(remote_1) + expect(remote_1.local_followers_hash).to eq Digest::SHA256.hexdigest(ActivityPub::TagManager.instance.uri_for(me)) end end diff --git a/spec/workers/activitypub/delivery_worker_spec.rb b/spec/workers/activitypub/delivery_worker_spec.rb index f4633731e50676..d39393d5077b41 100644 --- a/spec/workers/activitypub/delivery_worker_spec.rb +++ b/spec/workers/activitypub/delivery_worker_spec.rb @@ -11,7 +11,7 @@ let(:payload) { 'test' } before do - allow_any_instance_of(Account).to receive(:remote_followers_hash).with('https://example.com/').and_return('somehash') + allow_any_instance_of(Account).to receive(:remote_followers_hash).with('https://example.com/api').and_return('somehash') end describe 'perform' do From d722222fe15fc7b285fe5f90f4827ce68cee6291 Mon Sep 17 00:00:00 2001 From: Claire Date: Sun, 30 Jan 2022 23:50:08 +0100 Subject: [PATCH 59/81] Add more advanced migration tests (#17393) - populate the database with some data when testing migrations - try both one-step and two-step migrations (`SKIP_POST_DEPLOYMENT_MIGRATIONS`) --- .circleci/config.yml | 42 +++++++++- lib/tasks/tests.rake | 181 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 222 insertions(+), 1 deletion(-) create mode 100644 lib/tasks/tests.rake diff --git a/.circleci/config.yml b/.circleci/config.yml index 42c0582958c60d..e1c43ff806ae87 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -167,8 +167,45 @@ jobs: name: Create database command: ./bin/rails db:create - run: - name: Run migrations + command: ./bin/rails db:migrate VERSION=20171010025614 + name: Run migrations up to v2.0.0 + - run: + command: ./bin/rails tests:migrations:populate_v2 + name: Populate database with test data + - run: + command: ./bin/rails db:migrate + name: Run all remaining migrations + + test-two-step-migrations: + <<: *defaults + docker: + - image: circleci/ruby:2.7-buster-node + environment: *ruby_environment + - image: circleci/postgres:12.2 + environment: + POSTGRES_USER: root + POSTGRES_HOST_AUTH_METHOD: trust + - image: circleci/redis:5-alpine + steps: + - *attach_workspace + - *install_system_dependencies + - run: + command: ./bin/rails db:create + name: Create database + - run: + command: ./bin/rails db:migrate VERSION=20171010025614 + name: Run migrations up to v2.0.0 + - run: + command: ./bin/rails tests:migrations:populate_v2 + name: Populate database with test data + - run: + command: ./bin/rails db:migrate + name: Run all pre-deployment migrations + evironment: + SKIP_POST_DEPLOYMENT_MIGRATIONS: true + - run: command: ./bin/rails db:migrate + name: Run all post-deployment remaining migrations test-ruby2.7: <<: *defaults @@ -238,6 +275,9 @@ workflows: - test-migrations: requires: - install-ruby2.7 + - test-two-step-migrations: + requires: + - install-ruby2.7 - test-ruby2.7: requires: - install-ruby2.7 diff --git a/lib/tasks/tests.rake b/lib/tasks/tests.rake new file mode 100644 index 00000000000000..0f38b50e3983d6 --- /dev/null +++ b/lib/tasks/tests.rake @@ -0,0 +1,181 @@ +# frozen_string_literal: true + +namespace :tests do + namespace :migrations do + desc 'Populate the database with test data for 2.0.0' + task populate_v2: :environment do + admin_key = OpenSSL::PKey::RSA.new(2048) + user_key = OpenSSL::PKey::RSA.new(2048) + remote_key = OpenSSL::PKey::RSA.new(2048) + remote_key2 = OpenSSL::PKey::RSA.new(2048) + remote_key3 = OpenSSL::PKey::RSA.new(2048) + admin_private_key = ActiveRecord::Base.connection.quote(admin_key.to_pem) + admin_public_key = ActiveRecord::Base.connection.quote(admin_key.public_key.to_pem) + user_private_key = ActiveRecord::Base.connection.quote(user_key.to_pem) + user_public_key = ActiveRecord::Base.connection.quote(user_key.public_key.to_pem) + remote_public_key = ActiveRecord::Base.connection.quote(remote_key.public_key.to_pem) + remote_public_key2 = ActiveRecord::Base.connection.quote(remote_key2.public_key.to_pem) + remote_public_key_ap = ActiveRecord::Base.connection.quote(remote_key3.public_key.to_pem) + local_domain = ActiveRecord::Base.connection.quote(Rails.configuration.x.local_domain) + + ActiveRecord::Base.connection.execute(<<~SQL) + -- accounts + + INSERT INTO "accounts" + (id, username, domain, private_key, public_key, created_at, updated_at) + VALUES + (1, 'admin', NULL, #{admin_private_key}, #{admin_public_key}, now(), now()), + (2, 'user', NULL, #{user_private_key}, #{user_public_key}, now(), now()); + + INSERT INTO "accounts" + (id, username, domain, private_key, public_key, created_at, updated_at, remote_url, salmon_url) + VALUES + (3, 'remote', 'remote.com', NULL, #{remote_public_key}, now(), now(), + 'https://remote.com/@remote', 'https://remote.com/salmon/1'), + (4, 'Remote', 'remote.com', NULL, #{remote_public_key}, now(), now(), + 'https://remote.com/@Remote', 'https://remote.com/salmon/1'), + (5, 'REMOTE', 'Remote.com', NULL, #{remote_public_key2}, now(), now(), + 'https://remote.com/stale/@REMOTE', 'https://remote.com/stale/salmon/1'); + + INSERT INTO "accounts" + (id, username, domain, private_key, public_key, created_at, updated_at, protocol, inbox_url, outbox_url, followers_url) + VALUES + (6, 'bob', 'activitypub.com', NULL, #{remote_public_key_ap}, now(), now(), + 1, 'https://activitypub.com/users/bob/inbox', 'https://activitypub.com/users/bob/outbox', 'https://activitypub.com/users/bob/followers'); + + INSERT INTO "accounts" + (id, username, domain, private_key, public_key, created_at, updated_at) + VALUES + (7, 'user', #{local_domain}, #{user_private_key}, #{user_public_key}, now(), now()), + (8, 'pt_user', NULL, #{user_private_key}, #{user_public_key}, now(), now()); + + -- users + + INSERT INTO "users" + (id, account_id, email, created_at, updated_at, admin) + VALUES + (1, 1, 'admin@localhost', now(), now(), true), + (2, 2, 'user@localhost', now(), now(), false); + + INSERT INTO "users" + (id, account_id, email, created_at, updated_at, admin, locale) + VALUES + (3, 7, 'ptuser@localhost', now(), now(), false, 'pt'); + + -- statuses + + INSERT INTO "statuses" + (id, account_id, text, created_at, updated_at) + VALUES + (1, 1, 'test', now(), now()), + (2, 1, '@remote@remote.com hello', now(), now()), + (3, 1, '@Remote@remote.com hello', now(), now()), + (4, 1, '@REMOTE@remote.com hello', now(), now()); + + INSERT INTO "statuses" + (id, account_id, text, created_at, updated_at, uri, local) + VALUES + (5, 1, 'activitypub status', now(), now(), 'https://localhost/users/admin/statuses/4', true); + + INSERT INTO "statuses" + (id, account_id, text, created_at, updated_at) + VALUES + (6, 3, 'test', now(), now()); + + INSERT INTO "statuses" + (id, account_id, text, created_at, updated_at, in_reply_to_id, in_reply_to_account_id) + VALUES + (7, 4, '@admin hello', now(), now(), 3, 1); + + INSERT INTO "statuses" + (id, account_id, text, created_at, updated_at) + VALUES + (8, 5, 'test', now(), now()); + + INSERT INTO "statuses" + (id, account_id, reblog_of_id, created_at, updated_at) + VALUES + (9, 1, 2, now(), now()); + + -- mentions (from previous statuses) + + INSERT INTO "mentions" + (status_id, account_id, created_at, updated_at) + VALUES + (2, 3, now(), now()), + (3, 4, now(), now()), + (4, 5, now(), now()); + + -- stream entries + + INSERT INTO "stream_entries" + (activity_id, account_id, activity_type, created_at, updated_at) + VALUES + (1, 1, 'status', now(), now()), + (2, 1, 'status', now(), now()), + (3, 1, 'status', now(), now()), + (4, 1, 'status', now(), now()), + (5, 1, 'status', now(), now()), + (6, 3, 'status', now(), now()), + (7, 4, 'status', now(), now()), + (8, 5, 'status', now(), now()), + (9, 1, 'status', now(), now()); + + + -- custom emoji + + INSERT INTO "custom_emojis" + (shortcode, created_at, updated_at) + VALUES + ('test', now(), now()), + ('Test', now(), now()), + ('blobcat', now(), now()); + + INSERT INTO "custom_emojis" + (shortcode, domain, uri, created_at, updated_at) + VALUES + ('blobcat', 'remote.org', 'https://remote.org/emoji/blobcat', now(), now()), + ('blobcat', 'Remote.org', 'https://remote.org/emoji/blobcat', now(), now()), + ('Blobcat', 'remote.org', 'https://remote.org/emoji/Blobcat', now(), now()); + + -- favourites + + INSERT INTO "favourites" + (account_id, status_id, created_at, updated_at) + VALUES + (1, 1, now(), now()), + (1, 7, now(), now()), + (4, 1, now(), now()), + (3, 1, now(), now()), + (5, 1, now(), now()); + + -- pinned statuses + + INSERT INTO "status_pins" + (account_id, status_id, created_at, updated_at) + VALUES + (1, 1, now(), now()), + (3, 6, now(), now()), + (4, 7, now(), now()); + + -- follows + + INSERT INTO "follows" + (account_id, target_account_id, created_at, updated_at) + VALUES + (1, 5, now(), now()), + (6, 2, now(), now()), + (5, 2, now(), now()), + (6, 1, now(), now()); + + -- follow requests + + INSERT INTO "follow_requests" + (account_id, target_account_id, created_at, updated_at) + VALUES + (2, 5, now(), now()), + (5, 1, now(), now()); + SQL + end + end +end From 1c8c318281ba67ab60c70fab27204296b0af74b1 Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 31 Jan 2022 10:36:48 +0100 Subject: [PATCH 60/81] Bump version to 3.4.5 --- CHANGELOG.md | 12 ++++++++++++ docker-compose.yml | 6 +++--- lib/mastodon/version.rb | 2 +- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index deca765faf7dc5..9deff5a0d1f090 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,18 @@ Changelog All notable changes to this project will be documented in this file. +## [3.4.5] - 2022-01-31 +### Added +- Add more advanced migration tests ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17393)) +- Add github workflow to build Docker images ([unasuke](https://github.com/mastodon/mastodon/pull/16973), [Gargron](https://github.com/mastodon/mastodon/pull/16980), [Gargron](https://github.com/mastodon/mastodon/pull/17000)) + +### Fixed +- Fix some old migrations failing when skipping releases ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17394)) +- Fix migrations script failing in certain edge cases ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17398)) +- Fix Docker build ([tribela](https://github.com/mastodon/mastodon/pull/17188)) +- Fix Ruby 3.0 dependencies ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16723)) +- Fix followers synchronization mechanism ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16510)) + ## [3.4.4] - 2021-11-26 ### Fixed diff --git a/docker-compose.yml b/docker-compose.yml index 52eea7a74f4817..288a4fdf697dbc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -43,7 +43,7 @@ services: web: build: . - image: tootsuite/mastodon + image: tootsuite/mastodon:v3.4.5 restart: always env_file: .env.production command: bash -c "rm -f /mastodon/tmp/pids/server.pid; bundle exec rails s -p 3000" @@ -63,7 +63,7 @@ services: streaming: build: . - image: tootsuite/mastodon + image: tootsuite/mastodon:v3.4.5 restart: always env_file: .env.production command: node ./streaming @@ -80,7 +80,7 @@ services: sidekiq: build: . - image: tootsuite/mastodon + image: tootsuite/mastodon:v3.4.5 restart: always env_file: .env.production command: bundle exec sidekiq diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 49347997707255..1ba45aeca33099 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -13,7 +13,7 @@ def minor end def patch - 4 + 5 end def flags From 6d831fe274011acd2da25c40237a6c3ec6988add Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 2 Feb 2022 22:59:34 +0100 Subject: [PATCH 61/81] Fix spurious errors when receiving an Add activity for a private post (#17425) --- app/lib/activitypub/activity/add.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/lib/activitypub/activity/add.rb b/app/lib/activitypub/activity/add.rb index 688ab00b334e9d..fdd2f90f29ee67 100644 --- a/app/lib/activitypub/activity/add.rb +++ b/app/lib/activitypub/activity/add.rb @@ -7,7 +7,7 @@ def perform status = status_from_uri(object_uri) status ||= fetch_remote_original_status - return unless !status.nil? && status.account_id == @account.id && !@account.pinned?(status) + return unless !status.nil? && status.account_id == @account.id && !@account.pinned?(status) && status.distributable? StatusPin.create!(account: @account, status: status) end From c8dbbd60eb072a5f433b1a6aefd0be5ca2bfdb80 Mon Sep 17 00:00:00 2001 From: Claire Date: Sun, 23 Jan 2022 18:10:10 +0100 Subject: [PATCH 62/81] Fix error-prone SQL queries (#15828) * Fix error-prone SQL queries in Account search While this code seems to not present an actual vulnerability, one could easily be introduced by mistake due to how the query is built. This PR parameterises the `to_tsquery` input to make the query more robust. * Harden code for Status#tagged_with_all and Status#tagged_with_none Those two scopes aren't used in a way that could be vulnerable to an SQL injection, but keeping them unchanged might be a hazard. * Remove unneeded spaces surrounding tsquery term * Please CodeClimate * Move advanced_search_for SQL template to its own function This avoids one level of indentation while making clearer that the SQL template isn't build from all the dynamic parameters of advanced_search_for. * Add tests covering tagged_with, tagged_with_all and tagged_with_none * Rewrite tagged_with_none to avoid multiple joins and make it more robust * Remove obsolete brakeman warnings * Revert "Remove unneeded spaces surrounding tsquery term" The two queries are not strictly equivalent. This reverts commit 86f16c537e06c6ba4a8b250f25dcce9f049023ff. --- app/models/account.rb | 105 +++++++++++++++++++------------------ app/models/status.rb | 7 +-- config/brakeman.ignore | 80 ---------------------------- spec/models/status_spec.rb | 81 ++++++++++++++++++++++++++++ 4 files changed, 138 insertions(+), 135 deletions(-) diff --git a/app/models/account.rb b/app/models/account.rb index 2f2a55b553681b..11345a15c64d74 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -426,6 +426,9 @@ def to_h end class << self + DISALLOWED_TSQUERY_CHARACTERS = /['?\\:‘’]/.freeze + TEXTSEARCH = "(setweight(to_tsvector('simple', accounts.display_name), 'A') || setweight(to_tsvector('simple', accounts.username), 'B') || setweight(to_tsvector('simple', coalesce(accounts.domain, '')), 'C'))" + def readonly_attributes super - %w(statuses_count following_count followers_count) end @@ -436,98 +439,100 @@ def inboxes end def search_for(terms, limit = 10, offset = 0) - textsearch, query = generate_query_for_search(terms) + tsquery = generate_query_for_search(terms) sql = <<-SQL.squish SELECT accounts.*, - ts_rank_cd(#{textsearch}, #{query}, 32) AS rank + ts_rank_cd(#{TEXTSEARCH}, to_tsquery('simple', :tsquery), 32) AS rank FROM accounts - WHERE #{query} @@ #{textsearch} + WHERE to_tsquery('simple', :tsquery) @@ #{TEXTSEARCH} AND accounts.suspended_at IS NULL AND accounts.moved_to_account_id IS NULL ORDER BY rank DESC - LIMIT ? OFFSET ? + LIMIT :limit OFFSET :offset SQL - records = find_by_sql([sql, limit, offset]) + records = find_by_sql([sql, limit: limit, offset: offset, tsquery: tsquery]) ActiveRecord::Associations::Preloader.new.preload(records, :account_stat) records end def advanced_search_for(terms, account, limit = 10, following = false, offset = 0) - textsearch, query = generate_query_for_search(terms) + tsquery = generate_query_for_search(terms) + sql = advanced_search_for_sql_template(following) + records = find_by_sql([sql, id: account.id, limit: limit, offset: offset, tsquery: tsquery]) + ActiveRecord::Associations::Preloader.new.preload(records, :account_stat) + records + end + + def from_text(text) + return [] if text.blank? + text.scan(MENTION_RE).map { |match| match.first.split('@', 2) }.uniq.filter_map do |(username, domain)| + domain = begin + if TagManager.instance.local_domain?(domain) + nil + else + TagManager.instance.normalize_domain(domain) + end + end + EntityCache.instance.mention(username, domain) + end + end + + private + + def generate_query_for_search(unsanitized_terms) + terms = unsanitized_terms.gsub(DISALLOWED_TSQUERY_CHARACTERS, ' ') + + # The final ":*" is for prefix search. + # The trailing space does not seem to fit any purpose, but `to_tsquery` + # behaves differently with and without a leading space if the terms start + # with `./`, `../`, or `.. `. I don't understand why, so, in doubt, keep + # the same query. + "' #{terms} ':*" + end + + def advanced_search_for_sql_template(following) if following - sql = <<-SQL.squish + <<-SQL.squish WITH first_degree AS ( SELECT target_account_id FROM follows - WHERE account_id = ? + WHERE account_id = :id UNION ALL - SELECT ? + SELECT :id ) SELECT accounts.*, - (count(f.id) + 1) * ts_rank_cd(#{textsearch}, #{query}, 32) AS rank + (count(f.id) + 1) * ts_rank_cd(#{TEXTSEARCH}, to_tsquery('simple', :tsquery), 32) AS rank FROM accounts - LEFT OUTER JOIN follows AS f ON (accounts.id = f.account_id AND f.target_account_id = ?) + LEFT OUTER JOIN follows AS f ON (accounts.id = f.account_id AND f.target_account_id = :id) WHERE accounts.id IN (SELECT * FROM first_degree) - AND #{query} @@ #{textsearch} + AND to_tsquery('simple', :tsquery) @@ #{TEXTSEARCH} AND accounts.suspended_at IS NULL AND accounts.moved_to_account_id IS NULL GROUP BY accounts.id ORDER BY rank DESC - LIMIT ? OFFSET ? + LIMIT :limit OFFSET :offset SQL - - records = find_by_sql([sql, account.id, account.id, account.id, limit, offset]) else - sql = <<-SQL.squish + <<-SQL.squish SELECT accounts.*, - (count(f.id) + 1) * ts_rank_cd(#{textsearch}, #{query}, 32) AS rank + (count(f.id) + 1) * ts_rank_cd(#{TEXTSEARCH}, to_tsquery('simple', :tsquery), 32) AS rank FROM accounts - LEFT OUTER JOIN follows AS f ON (accounts.id = f.account_id AND f.target_account_id = ?) OR (accounts.id = f.target_account_id AND f.account_id = ?) - WHERE #{query} @@ #{textsearch} + LEFT OUTER JOIN follows AS f ON (accounts.id = f.account_id AND f.target_account_id = :id) OR (accounts.id = f.target_account_id AND f.account_id = :id) + WHERE to_tsquery('simple', :tsquery) @@ #{TEXTSEARCH} AND accounts.suspended_at IS NULL AND accounts.moved_to_account_id IS NULL GROUP BY accounts.id ORDER BY rank DESC - LIMIT ? OFFSET ? + LIMIT :limit OFFSET :offset SQL - - records = find_by_sql([sql, account.id, account.id, limit, offset]) - end - - ActiveRecord::Associations::Preloader.new.preload(records, :account_stat) - records - end - - def from_text(text) - return [] if text.blank? - - text.scan(MENTION_RE).map { |match| match.first.split('@', 2) }.uniq.filter_map do |(username, domain)| - domain = begin - if TagManager.instance.local_domain?(domain) - nil - else - TagManager.instance.normalize_domain(domain) - end - end - EntityCache.instance.mention(username, domain) end end - - private - - def generate_query_for_search(terms) - terms = Arel.sql(connection.quote(terms.gsub(/['?\\:]/, ' '))) - textsearch = "(setweight(to_tsvector('simple', accounts.display_name), 'A') || setweight(to_tsvector('simple', accounts.username), 'B') || setweight(to_tsvector('simple', coalesce(accounts.domain, '')), 'C'))" - query = "to_tsquery('simple', ''' ' || #{terms} || ' ''' || ':*')" - - [textsearch, query] - end end def emojis diff --git a/app/models/status.rb b/app/models/status.rb index c7f761bc6ba58f..fc8835b542579c 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -96,15 +96,12 @@ class Status < ApplicationRecord scope :not_excluded_by_account, ->(account) { where.not(account_id: account.excluded_from_timeline_account_ids) } scope :not_domain_blocked_by_account, ->(account) { account.excluded_from_timeline_domains.blank? ? left_outer_joins(:account) : left_outer_joins(:account).where('accounts.domain IS NULL OR accounts.domain NOT IN (?)', account.excluded_from_timeline_domains) } scope :tagged_with_all, ->(tag_ids) { - Array(tag_ids).reduce(self) do |result, id| + Array(tag_ids).map(&:to_i).reduce(self) do |result, id| result.joins("INNER JOIN statuses_tags t#{id} ON t#{id}.status_id = statuses.id AND t#{id}.tag_id = #{id}") end } scope :tagged_with_none, ->(tag_ids) { - Array(tag_ids).reduce(self) do |result, id| - result.joins("LEFT OUTER JOIN statuses_tags t#{id} ON t#{id}.status_id = statuses.id AND t#{id}.tag_id = #{id}") - .where("t#{id}.tag_id IS NULL") - end + where('NOT EXISTS (SELECT * FROM statuses_tags forbidden WHERE forbidden.status_id = statuses.id AND forbidden.tag_id IN (?))', tag_ids) } cache_associated :application, diff --git a/config/brakeman.ignore b/config/brakeman.ignore index 35f2c317882c89..07a2c377fdcfd2 100644 --- a/config/brakeman.ignore +++ b/config/brakeman.ignore @@ -60,46 +60,6 @@ "confidence": "High", "note": "" }, - { - "warning_type": "SQL Injection", - "warning_code": 0, - "fingerprint": "6e4051854bb62e2ddbc671f82d6c2328892e1134b8b28105ecba9b0122540714", - "check_name": "SQL", - "message": "Possible SQL injection", - "file": "app/models/account.rb", - "line": 479, - "link": "https://brakemanscanner.org/docs/warning_types/sql_injection/", - "code": "find_by_sql([\" WITH first_degree AS (\\n SELECT target_account_id\\n FROM follows\\n WHERE account_id = ?\\n UNION ALL\\n SELECT ?\\n )\\n SELECT\\n accounts.*,\\n (count(f.id) + 1) * ts_rank_cd(#{textsearch}, #{query}, 32) AS rank\\n FROM accounts\\n LEFT OUTER JOIN follows AS f ON (accounts.id = f.account_id AND f.target_account_id = ?)\\n WHERE accounts.id IN (SELECT * FROM first_degree)\\n AND #{query} @@ #{textsearch}\\n AND accounts.suspended_at IS NULL\\n AND accounts.moved_to_account_id IS NULL\\n GROUP BY accounts.id\\n ORDER BY rank DESC\\n LIMIT ? OFFSET ?\\n\".squish, account.id, account.id, account.id, limit, offset])", - "render_path": null, - "location": { - "type": "method", - "class": "Account", - "method": "advanced_search_for" - }, - "user_input": "textsearch", - "confidence": "Medium", - "note": "" - }, - { - "warning_type": "SQL Injection", - "warning_code": 0, - "fingerprint": "6f075c1484908e3ec9bed21ab7cf3c7866be8da3881485d1c82e13093aefcbd7", - "check_name": "SQL", - "message": "Possible SQL injection", - "file": "app/models/status.rb", - "line": 105, - "link": "https://brakemanscanner.org/docs/warning_types/sql_injection/", - "code": "result.joins(\"LEFT OUTER JOIN statuses_tags t#{id} ON t#{id}.status_id = statuses.id AND t#{id}.tag_id = #{id}\")", - "render_path": null, - "location": { - "type": "method", - "class": "Status", - "method": null - }, - "user_input": "id", - "confidence": "Weak", - "note": "" - }, { "warning_type": "Mass Assignment", "warning_code": 105, @@ -140,26 +100,6 @@ "confidence": "High", "note": "" }, - { - "warning_type": "SQL Injection", - "warning_code": 0, - "fingerprint": "9251d682c4e2840e1b2fea91e7d758efe2097ecb7f6255c065e3750d25eb178c", - "check_name": "SQL", - "message": "Possible SQL injection", - "file": "app/models/account.rb", - "line": 448, - "link": "https://brakemanscanner.org/docs/warning_types/sql_injection/", - "code": "find_by_sql([\" SELECT\\n accounts.*,\\n ts_rank_cd(#{textsearch}, #{query}, 32) AS rank\\n FROM accounts\\n WHERE #{query} @@ #{textsearch}\\n AND accounts.suspended_at IS NULL\\n AND accounts.moved_to_account_id IS NULL\\n ORDER BY rank DESC\\n LIMIT ? OFFSET ?\\n\".squish, limit, offset])", - "render_path": null, - "location": { - "type": "method", - "class": "Account", - "method": "search_for" - }, - "user_input": "textsearch", - "confidence": "Medium", - "note": "" - }, { "warning_type": "SQL Injection", "warning_code": 0, @@ -220,26 +160,6 @@ "confidence": "High", "note": "" }, - { - "warning_type": "SQL Injection", - "warning_code": 0, - "fingerprint": "e21d8fee7a5805761679877ca35ed1029c64c45ef3b4012a30262623e1ba8bb9", - "check_name": "SQL", - "message": "Possible SQL injection", - "file": "app/models/account.rb", - "line": 495, - "link": "https://brakemanscanner.org/docs/warning_types/sql_injection/", - "code": "find_by_sql([\" SELECT\\n accounts.*,\\n (count(f.id) + 1) * ts_rank_cd(#{textsearch}, #{query}, 32) AS rank\\n FROM accounts\\n LEFT OUTER JOIN follows AS f ON (accounts.id = f.account_id AND f.target_account_id = ?) OR (accounts.id = f.target_account_id AND f.account_id = ?)\\n WHERE #{query} @@ #{textsearch}\\n AND accounts.suspended_at IS NULL\\n AND accounts.moved_to_account_id IS NULL\\n GROUP BY accounts.id\\n ORDER BY rank DESC\\n LIMIT ? OFFSET ?\\n\".squish, account.id, account.id, limit, offset])", - "render_path": null, - "location": { - "type": "method", - "class": "Account", - "method": "advanced_search_for" - }, - "user_input": "textsearch", - "confidence": "Medium", - "note": "" - }, { "warning_type": "Mass Assignment", "warning_code": 105, diff --git a/spec/models/status_spec.rb b/spec/models/status_spec.rb index 20fb894e77f369..653575778b21c5 100644 --- a/spec/models/status_spec.rb +++ b/spec/models/status_spec.rb @@ -267,6 +267,87 @@ end end + describe '.tagged_with' do + let(:tag1) { Fabricate(:tag) } + let(:tag2) { Fabricate(:tag) } + let(:tag3) { Fabricate(:tag) } + let!(:status1) { Fabricate(:status, tags: [tag1]) } + let!(:status2) { Fabricate(:status, tags: [tag2]) } + let!(:status3) { Fabricate(:status, tags: [tag3]) } + let!(:status4) { Fabricate(:status, tags: []) } + let!(:status5) { Fabricate(:status, tags: [tag1, tag2, tag3]) } + + context 'when given one tag' do + it 'returns the expected statuses' do + expect(Status.tagged_with([tag1.id]).reorder(:id).pluck(:id).uniq).to eq [status1.id, status5.id] + expect(Status.tagged_with([tag2.id]).reorder(:id).pluck(:id).uniq).to eq [status2.id, status5.id] + expect(Status.tagged_with([tag3.id]).reorder(:id).pluck(:id).uniq).to eq [status3.id, status5.id] + end + end + + context 'when given multiple tags' do + it 'returns the expected statuses' do + expect(Status.tagged_with([tag1.id, tag2.id]).reorder(:id).pluck(:id).uniq).to eq [status1.id, status2.id, status5.id] + expect(Status.tagged_with([tag1.id, tag3.id]).reorder(:id).pluck(:id).uniq).to eq [status1.id, status3.id, status5.id] + expect(Status.tagged_with([tag2.id, tag3.id]).reorder(:id).pluck(:id).uniq).to eq [status2.id, status3.id, status5.id] + end + end + end + + describe '.tagged_with_all' do + let(:tag1) { Fabricate(:tag) } + let(:tag2) { Fabricate(:tag) } + let(:tag3) { Fabricate(:tag) } + let!(:status1) { Fabricate(:status, tags: [tag1]) } + let!(:status2) { Fabricate(:status, tags: [tag2]) } + let!(:status3) { Fabricate(:status, tags: [tag3]) } + let!(:status4) { Fabricate(:status, tags: []) } + let!(:status5) { Fabricate(:status, tags: [tag1, tag2]) } + + context 'when given one tag' do + it 'returns the expected statuses' do + expect(Status.tagged_with_all([tag1.id]).reorder(:id).pluck(:id).uniq).to eq [status1.id, status5.id] + expect(Status.tagged_with_all([tag2.id]).reorder(:id).pluck(:id).uniq).to eq [status2.id, status5.id] + expect(Status.tagged_with_all([tag3.id]).reorder(:id).pluck(:id).uniq).to eq [status3.id] + end + end + + context 'when given multiple tags' do + it 'returns the expected statuses' do + expect(Status.tagged_with_all([tag1.id, tag2.id]).reorder(:id).pluck(:id).uniq).to eq [status5.id] + expect(Status.tagged_with_all([tag1.id, tag3.id]).reorder(:id).pluck(:id).uniq).to eq [] + expect(Status.tagged_with_all([tag2.id, tag3.id]).reorder(:id).pluck(:id).uniq).to eq [] + end + end + end + + describe '.tagged_with_none' do + let(:tag1) { Fabricate(:tag) } + let(:tag2) { Fabricate(:tag) } + let(:tag3) { Fabricate(:tag) } + let!(:status1) { Fabricate(:status, tags: [tag1]) } + let!(:status2) { Fabricate(:status, tags: [tag2]) } + let!(:status3) { Fabricate(:status, tags: [tag3]) } + let!(:status4) { Fabricate(:status, tags: []) } + let!(:status5) { Fabricate(:status, tags: [tag1, tag2, tag3]) } + + context 'when given one tag' do + it 'returns the expected statuses' do + expect(Status.tagged_with_none([tag1.id]).reorder(:id).pluck(:id).uniq).to eq [status2.id, status3.id, status4.id] + expect(Status.tagged_with_none([tag2.id]).reorder(:id).pluck(:id).uniq).to eq [status1.id, status3.id, status4.id] + expect(Status.tagged_with_none([tag3.id]).reorder(:id).pluck(:id).uniq).to eq [status1.id, status2.id, status4.id] + end + end + + context 'when given multiple tags' do + it 'returns the expected statuses' do + expect(Status.tagged_with_none([tag1.id, tag2.id]).reorder(:id).pluck(:id).uniq).to eq [status3.id, status4.id] + expect(Status.tagged_with_none([tag1.id, tag3.id]).reorder(:id).pluck(:id).uniq).to eq [status2.id, status4.id] + expect(Status.tagged_with_none([tag2.id, tag3.id]).reorder(:id).pluck(:id).uniq).to eq [status1.id, status4.id] + end + end + end + describe '.permitted_for' do subject { described_class.permitted_for(target_account, account).pluck(:visibility) } From fe0210074ffebec0a424dc8be65dd8f32296a08c Mon Sep 17 00:00:00 2001 From: Puck Meerburg Date: Sat, 22 Jan 2022 13:48:27 +0000 Subject: [PATCH 63/81] Compact JSON-LD signed incoming activities --- app/helpers/context_helper.rb | 55 +++++++++++++++++++ app/helpers/jsonld_helper.rb | 8 +++ app/lib/activitypub/adapter.rb | 52 +----------------- .../activitypub/process_collection_service.rb | 2 + 4 files changed, 67 insertions(+), 50 deletions(-) create mode 100644 app/helpers/context_helper.rb diff --git a/app/helpers/context_helper.rb b/app/helpers/context_helper.rb new file mode 100644 index 00000000000000..2c7541ad4d9bba --- /dev/null +++ b/app/helpers/context_helper.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +module ContextHelper + NAMED_CONTEXT_MAP = { + activitystreams: 'https://www.w3.org/ns/activitystreams', + security: 'https://w3id.org/security/v1', + }.freeze + + CONTEXT_EXTENSION_MAP = { + manually_approves_followers: { 'manuallyApprovesFollowers' => 'as:manuallyApprovesFollowers' }, + sensitive: { 'sensitive' => 'as:sensitive' }, + hashtag: { 'Hashtag' => 'as:Hashtag' }, + moved_to: { 'movedTo' => { '@id' => 'as:movedTo', '@type' => '@id' } }, + also_known_as: { 'alsoKnownAs' => { '@id' => 'as:alsoKnownAs', '@type' => '@id' } }, + emoji: { 'toot' => 'http://joinmastodon.org/ns#', 'Emoji' => 'toot:Emoji' }, + featured: { 'toot' => 'http://joinmastodon.org/ns#', 'featured' => { '@id' => 'toot:featured', '@type' => '@id' }, 'featuredTags' => { '@id' => 'toot:featuredTags', '@type' => '@id' } }, + property_value: { 'schema' => 'http://schema.org#', 'PropertyValue' => 'schema:PropertyValue', 'value' => 'schema:value' }, + atom_uri: { 'ostatus' => 'http://ostatus.org#', 'atomUri' => 'ostatus:atomUri' }, + conversation: { 'ostatus' => 'http://ostatus.org#', 'inReplyToAtomUri' => 'ostatus:inReplyToAtomUri', 'conversation' => 'ostatus:conversation' }, + focal_point: { 'toot' => 'http://joinmastodon.org/ns#', 'focalPoint' => { '@container' => '@list', '@id' => 'toot:focalPoint' } }, + identity_proof: { 'toot' => 'http://joinmastodon.org/ns#', 'IdentityProof' => 'toot:IdentityProof' }, + blurhash: { 'toot' => 'http://joinmastodon.org/ns#', 'blurhash' => 'toot:blurhash' }, + discoverable: { 'toot' => 'http://joinmastodon.org/ns#', 'discoverable' => 'toot:discoverable' }, + voters_count: { 'toot' => 'http://joinmastodon.org/ns#', 'votersCount' => 'toot:votersCount' }, + olm: { 'toot' => 'http://joinmastodon.org/ns#', 'Device' => 'toot:Device', 'Ed25519Signature' => 'toot:Ed25519Signature', 'Ed25519Key' => 'toot:Ed25519Key', 'Curve25519Key' => 'toot:Curve25519Key', 'EncryptedMessage' => 'toot:EncryptedMessage', 'publicKeyBase64' => 'toot:publicKeyBase64', 'deviceId' => 'toot:deviceId', 'claim' => { '@type' => '@id', '@id' => 'toot:claim' }, 'fingerprintKey' => { '@type' => '@id', '@id' => 'toot:fingerprintKey' }, 'identityKey' => { '@type' => '@id', '@id' => 'toot:identityKey' }, 'devices' => { '@type' => '@id', '@id' => 'toot:devices' }, 'messageFranking' => 'toot:messageFranking', 'messageType' => 'toot:messageType', 'cipherText' => 'toot:cipherText' }, + suspended: { 'toot' => 'http://joinmastodon.org/ns#', 'suspended' => 'toot:suspended' }, + }.freeze + + def full_context + serialized_context(NAMED_CONTEXT_MAP, CONTEXT_EXTENSION_MAP) + end + + def serialized_context(named_contexts_map, context_extensions_map) + context_array = [] + + named_contexts = named_contexts_map.keys + context_extensions = context_extensions_map.keys + + named_contexts.each do |key| + context_array << NAMED_CONTEXT_MAP[key] + end + + extensions = context_extensions.each_with_object({}) do |key, h| + h.merge!(CONTEXT_EXTENSION_MAP[key]) + end + + context_array << extensions unless extensions.empty? + + if context_array.size == 1 + context_array.first + else + context_array + end + end +end diff --git a/app/helpers/jsonld_helper.rb b/app/helpers/jsonld_helper.rb index 62eb50f786c4a6..c290c77ce4aeb0 100644 --- a/app/helpers/jsonld_helper.rb +++ b/app/helpers/jsonld_helper.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true module JsonLdHelper + include ContextHelper + def equals_or_includes?(haystack, needle) haystack.is_a?(Array) ? haystack.include?(needle) : haystack == needle end @@ -63,6 +65,12 @@ def canonicalize(json) graph.dump(:normalize) end + def compact(json) + compacted = JSON::LD::API.compact(json.without('signature'), full_context, documentLoader: method(:load_jsonld_context)) + compacted['signature'] = json['signature'] + compacted + end + def fetch_resource(uri, id, on_behalf_of = nil) unless id json = fetch_resource_without_id_validation(uri, on_behalf_of) diff --git a/app/lib/activitypub/adapter.rb b/app/lib/activitypub/adapter.rb index 2d6b876593752e..098b6296fb053e 100644 --- a/app/lib/activitypub/adapter.rb +++ b/app/lib/activitypub/adapter.rb @@ -1,30 +1,7 @@ # frozen_string_literal: true class ActivityPub::Adapter < ActiveModelSerializers::Adapter::Base - NAMED_CONTEXT_MAP = { - activitystreams: 'https://www.w3.org/ns/activitystreams', - security: 'https://w3id.org/security/v1', - }.freeze - - CONTEXT_EXTENSION_MAP = { - manually_approves_followers: { 'manuallyApprovesFollowers' => 'as:manuallyApprovesFollowers' }, - sensitive: { 'sensitive' => 'as:sensitive' }, - hashtag: { 'Hashtag' => 'as:Hashtag' }, - moved_to: { 'movedTo' => { '@id' => 'as:movedTo', '@type' => '@id' } }, - also_known_as: { 'alsoKnownAs' => { '@id' => 'as:alsoKnownAs', '@type' => '@id' } }, - emoji: { 'toot' => 'http://joinmastodon.org/ns#', 'Emoji' => 'toot:Emoji' }, - featured: { 'toot' => 'http://joinmastodon.org/ns#', 'featured' => { '@id' => 'toot:featured', '@type' => '@id' }, 'featuredTags' => { '@id' => 'toot:featuredTags', '@type' => '@id' } }, - property_value: { 'schema' => 'http://schema.org#', 'PropertyValue' => 'schema:PropertyValue', 'value' => 'schema:value' }, - atom_uri: { 'ostatus' => 'http://ostatus.org#', 'atomUri' => 'ostatus:atomUri' }, - conversation: { 'ostatus' => 'http://ostatus.org#', 'inReplyToAtomUri' => 'ostatus:inReplyToAtomUri', 'conversation' => 'ostatus:conversation' }, - focal_point: { 'toot' => 'http://joinmastodon.org/ns#', 'focalPoint' => { '@container' => '@list', '@id' => 'toot:focalPoint' } }, - identity_proof: { 'toot' => 'http://joinmastodon.org/ns#', 'IdentityProof' => 'toot:IdentityProof' }, - blurhash: { 'toot' => 'http://joinmastodon.org/ns#', 'blurhash' => 'toot:blurhash' }, - discoverable: { 'toot' => 'http://joinmastodon.org/ns#', 'discoverable' => 'toot:discoverable' }, - voters_count: { 'toot' => 'http://joinmastodon.org/ns#', 'votersCount' => 'toot:votersCount' }, - olm: { 'toot' => 'http://joinmastodon.org/ns#', 'Device' => 'toot:Device', 'Ed25519Signature' => 'toot:Ed25519Signature', 'Ed25519Key' => 'toot:Ed25519Key', 'Curve25519Key' => 'toot:Curve25519Key', 'EncryptedMessage' => 'toot:EncryptedMessage', 'publicKeyBase64' => 'toot:publicKeyBase64', 'deviceId' => 'toot:deviceId', 'claim' => { '@type' => '@id', '@id' => 'toot:claim' }, 'fingerprintKey' => { '@type' => '@id', '@id' => 'toot:fingerprintKey' }, 'identityKey' => { '@type' => '@id', '@id' => 'toot:identityKey' }, 'devices' => { '@type' => '@id', '@id' => 'toot:devices' }, 'messageFranking' => 'toot:messageFranking', 'messageType' => 'toot:messageType', 'cipherText' => 'toot:cipherText' }, - suspended: { 'toot' => 'http://joinmastodon.org/ns#', 'suspended' => 'toot:suspended' }, - }.freeze + include ContextHelper def self.default_key_transform :camel_lower @@ -35,7 +12,7 @@ def self.transform_key_casing!(value, _options) end def serializable_hash(options = nil) - named_contexts = {} + named_contexts = { activitystreams: NAMED_CONTEXT_MAP['activitystreams'] } context_extensions = {} options = serialization_options(options) @@ -45,29 +22,4 @@ def serializable_hash(options = nil) { '@context' => serialized_context(named_contexts, context_extensions) }.merge(serialized_hash) end - - private - - def serialized_context(named_contexts_map, context_extensions_map) - context_array = [] - - named_contexts = [:activitystreams] + named_contexts_map.keys - context_extensions = context_extensions_map.keys - - named_contexts.each do |key| - context_array << NAMED_CONTEXT_MAP[key] - end - - extensions = context_extensions.each_with_object({}) do |key, h| - h.merge!(CONTEXT_EXTENSION_MAP[key]) - end - - context_array << extensions unless extensions.empty? - - if context_array.size == 1 - context_array.first - else - context_array - end - end end diff --git a/app/services/activitypub/process_collection_service.rb b/app/services/activitypub/process_collection_service.rb index 170e6709cdd7e3..5f3d63bb39cb89 100644 --- a/app/services/activitypub/process_collection_service.rb +++ b/app/services/activitypub/process_collection_service.rb @@ -8,6 +8,8 @@ def call(body, account, **options) @json = Oj.load(body, mode: :strict) @options = options + @json = compact(@json) if @json['signature'].is_a?(Hash) + return if !supported_context? || (different_actor? && verify_account!.nil?) || suspended_actor? || @account.local? case @json['type'] From e2009ced3ac2ff30ef050d3442b86776fb986a62 Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 2 Feb 2022 11:55:57 +0100 Subject: [PATCH 64/81] Fix compacted JSON-LD possibly causing compatibility issues on forwarding --- app/helpers/jsonld_helper.rb | 72 ++++++++++++++++ .../activitypub/process_collection_service.rb | 18 +++- spec/helpers/jsonld_helper_spec.rb | 82 +++++++++++++++++++ 3 files changed, 170 insertions(+), 2 deletions(-) diff --git a/app/helpers/jsonld_helper.rb b/app/helpers/jsonld_helper.rb index c290c77ce4aeb0..d9924be3bdac77 100644 --- a/app/helpers/jsonld_helper.rb +++ b/app/helpers/jsonld_helper.rb @@ -71,6 +71,78 @@ def compact(json) compacted end + # Patches a JSON-LD document to avoid compatibility issues on redistribution + # + # Since compacting a JSON-LD document against Mastodon's built-in vocabulary + # means other extension namespaces will be expanded, malformed JSON-LD + # attributes lost, and some values “unexpectedly” compacted this method + # patches the following likely sources of incompatibility: + # - 'https://www.w3.org/ns/activitystreams#Public' being compacted to + # 'as:Public' (for instance, pre-3.4.0 Mastodon does not understand + # 'as:Public') + # - single-item arrays being compacted to the item itself (`[foo]` being + # compacted to `foo`) + # + # It is not always possible for `patch_for_forwarding!` to produce a document + # deemed safe for forwarding. Use `safe_for_forwarding?` to check the status + # of the output document. + # + # @param original [Hash] The original JSON-LD document used as reference + # @param compacted [Hash] The compacted JSON-LD document to be patched + # @return [void] + def patch_for_forwarding!(original, compacted) + original.without('@context', 'signature').each do |key, value| + next if value.nil? || !compacted.key?(key) + + compacted_value = compacted[key] + if value.is_a?(Hash) && compacted_value.is_a?(Hash) + patch_for_forwarding!(value, compacted_value) + elsif value.is_a?(Array) + compacted_value = [compacted_value] unless compacted_value.is_a?(Array) + return if value.size != compacted_value.size + + compacted[key] = value.zip(compacted_value).map do |v, vc| + if v.is_a?(Hash) && vc.is_a?(Hash) + patch_for_forwarding!(v, vc) + vc + elsif v == 'https://www.w3.org/ns/activitystreams#Public' && vc == 'as:Public' + v + else + vc + end + end + elsif value == 'https://www.w3.org/ns/activitystreams#Public' && compacted_value == 'as:Public' + compacted[key] = value + end + end + end + + # Tests whether a JSON-LD compaction is deemed safe for redistribution, + # that is, if it doesn't change its meaning to consumers that do not actually + # handle JSON-LD, but rely on values being serialized in a certain way. + # + # See `patch_for_forwarding!` for details. + # + # @param original [Hash] The original JSON-LD document used as reference + # @param compacted [Hash] The compacted JSON-LD document to be patched + # @return [Boolean] Whether the patched document is deemed safe + def safe_for_forwarding?(original, compacted) + original.without('@context', 'signature').all? do |key, value| + compacted_value = compacted[key] + return false unless value.class == compacted_value.class + + if value.is_a?(Hash) + safe_for_forwarding?(value, compacted_value) + elsif value.is_a?(Array) + value.zip(compacted_value).all? do |v, vc| + v.is_a?(Hash) ? (vc.is_a?(Hash) && safe_for_forwarding?(v, vc)) : v == vc + end + else + value == compacted_value + end + end + end + def fetch_resource(uri, id, on_behalf_of = nil) unless id json = fetch_resource_without_id_validation(uri, on_behalf_of) diff --git a/app/services/activitypub/process_collection_service.rb b/app/services/activitypub/process_collection_service.rb index 5f3d63bb39cb89..eb008c40a26997 100644 --- a/app/services/activitypub/process_collection_service.rb +++ b/app/services/activitypub/process_collection_service.rb @@ -5,13 +5,27 @@ class ActivityPub::ProcessCollectionService < BaseService def call(body, account, **options) @account = account - @json = Oj.load(body, mode: :strict) + @json = original_json = Oj.load(body, mode: :strict) @options = options - @json = compact(@json) if @json['signature'].is_a?(Hash) + begin + @json = compact(@json) if @json['signature'].is_a?(Hash) + rescue JSON::LD::JsonLdError => e + Rails.logger.debug "Error when compacting JSON-LD document for #{value_or_id(@json['actor'])}: #{e.message}" + @json = original_json.without('signature') + end return if !supported_context? || (different_actor? && verify_account!.nil?) || suspended_actor? || @account.local? + if @json['signature'].present? + # We have verified the signature, but in the compaction step above, might + # have introduced incompatibilities with other servers that do not + # normalize the JSON-LD documents (for instance, previous Mastodon + # versions), so skip redistribution if we can't get a safe document. + patch_for_forwarding!(original_json, @json) + @json.delete('signature') unless safe_for_forwarding?(original_json, @json) + end + case @json['type'] when 'Collection', 'CollectionPage' process_items @json['items'] diff --git a/spec/helpers/jsonld_helper_spec.rb b/spec/helpers/jsonld_helper_spec.rb index 883a88b14df1f3..744a14f26096f7 100644 --- a/spec/helpers/jsonld_helper_spec.rb +++ b/spec/helpers/jsonld_helper_spec.rb @@ -89,4 +89,86 @@ expect(fetch_resource_without_id_validation('https://host.test/')).to eq({}) end end + + context 'compaction and forwarding' do + let(:json) do + { + '@context' => [ + 'https://www.w3.org/ns/activitystreams', + 'https://w3id.org/security/v1', + { + 'obsolete' => 'http://ostatus.org#', + 'convo' => 'obsolete:conversation', + 'new' => 'https://obscure-unreleased-test.joinmastodon.org/#', + }, + ], + 'type' => 'Create', + 'to' => ['https://www.w3.org/ns/activitystreams#Public'], + 'object' => { + 'id' => 'https://example.com/status', + 'type' => 'Note', + 'inReplyTo' => nil, + 'convo' => 'https://example.com/conversation', + 'tag' => [ + { + 'type' => 'Mention', + 'href' => ['foo'], + } + ], + }, + 'signature' => { + 'type' => 'RsaSignature2017', + 'created' => '2022-02-02T12:00:00Z', + 'creator' => 'https://example.com/actor#main-key', + 'signatureValue' => 'some-sig', + }, + } + end + + describe '#compact' do + it 'properly compacts JSON-LD with alternative context definitions' do + expect(compact(json).dig('object', 'conversation')).to eq 'https://example.com/conversation' + end + + it 'compacts single-item arrays' do + expect(compact(json).dig('object', 'tag', 'href')).to eq 'foo' + end + + it 'compacts the activistreams Public collection' do + expect(compact(json)['to']).to eq 'as:Public' + end + + it 'properly copies signature' do + expect(compact(json)['signature']).to eq json['signature'] + end + end + + describe 'patch_for_forwarding!' do + it 'properly patches incompatibilities' do + json['object'].delete('convo') + compacted = compact(json) + patch_for_forwarding!(json, compacted) + expect(compacted['to']).to eq ['https://www.w3.org/ns/activitystreams#Public'] + expect(compacted.dig('object', 'tag', 0, 'href')).to eq ['foo'] + expect(safe_for_forwarding?(json, compacted)).to eq true + end + end + + describe 'safe_for_forwarding?' do + it 'deems a safe compacting as such' do + json['object'].delete('convo') + compacted = compact(json) + deemed_compatible = patch_for_forwarding!(json, compacted) + expect(compacted['to']).to eq ['https://www.w3.org/ns/activitystreams#Public'] + expect(safe_for_forwarding?(json, compacted)).to eq true + end + + it 'deems an unsafe compacting as such' do + compacted = compact(json) + deemed_compatible = patch_for_forwarding!(json, compacted) + expect(compacted['to']).to eq ['https://www.w3.org/ns/activitystreams#Public'] + expect(safe_for_forwarding?(json, compacted)).to eq false + end + end + end end From b27f50da5ac6c01c1b88df196e4ea6b81e472547 Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 2 Feb 2022 13:30:02 +0100 Subject: [PATCH 65/81] Fix insufficient sanitization of report comments --- app/views/admin/reports/show.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/admin/reports/show.html.haml b/app/views/admin/reports/show.html.haml index b060c553f09df0..e8294d812eff92 100644 --- a/app/views/admin/reports/show.html.haml +++ b/app/views/admin/reports/show.html.haml @@ -92,7 +92,7 @@ %hr.spacer .speech-bubble - .speech-bubble__bubble= simple_format(@report.comment.presence || t('admin.reports.comment.none')) + .speech-bubble__bubble= simple_format(h(@report.comment.presence || t('admin.reports.comment.none'))) .speech-bubble__owner - if @report.account.local? = admin_account_link_to @report.account From df68d2eab8dd88b85e32b26f70ee1074a5b01093 Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 6 Dec 2021 08:32:31 +0100 Subject: [PATCH 66/81] Fix response_to_recipient? CTE --- app/services/notify_service.rb | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/app/services/notify_service.rb b/app/services/notify_service.rb index e78c74d1edd9a3..b9ec0f2b8fa94b 100644 --- a/app/services/notify_service.rb +++ b/app/services/notify_service.rb @@ -73,9 +73,11 @@ def response_to_recipient? # Using an SQL CTE to avoid unneeded back-and-forth with SQL server in case of long threads !Status.count_by_sql([<<-SQL.squish, id: @notification.target_status.in_reply_to_id, recipient_id: @recipient.id, sender_id: @notification.from_account.id]).zero? - WITH RECURSIVE ancestors(id, in_reply_to_id, replying_to_sender) AS ( + WITH RECURSIVE ancestors(id, in_reply_to_id, replying_to_sender, path) AS ( SELECT - s.id, s.in_reply_to_id, (CASE + s.id, + s.in_reply_to_id, + (CASE WHEN s.account_id = :recipient_id THEN EXISTS ( SELECT * @@ -84,7 +86,8 @@ def response_to_recipient? ) ELSE FALSE - END) + END), + ARRAY[s.id] FROM statuses s WHERE s.id = :id UNION ALL @@ -100,10 +103,11 @@ def response_to_recipient? ) ELSE FALSE - END) + END), + st.path || s.id FROM ancestors st JOIN statuses s ON s.id = st.in_reply_to_id - WHERE st.replying_to_sender IS FALSE + WHERE st.replying_to_sender IS FALSE AND NOT s.id = ANY(path) ) SELECT COUNT(*) FROM ancestors st From bf005edd30dd7650c7947283e14e85b8461b38f2 Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 20 Jan 2022 14:51:23 +0100 Subject: [PATCH 67/81] Change mastodon:webpush:generate_vapid_key task to not require functional env (#17338) Fixes #17297 --- lib/tasks/mastodon.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tasks/mastodon.rake b/lib/tasks/mastodon.rake index d905a07dad42a3..a89af677807439 100644 --- a/lib/tasks/mastodon.rake +++ b/lib/tasks/mastodon.rake @@ -441,7 +441,7 @@ namespace :mastodon do namespace :webpush do desc 'Generate VAPID key' - task generate_vapid_key: :environment do + task :generate_vapid_key do vapid_key = Webpush.generate_key puts "VAPID_PRIVATE_KEY=#{vapid_key.private_key}" puts "VAPID_PUBLIC_KEY=#{vapid_key.public_key}" From a06dda41d04b073a9cc41930a9c80bd91c2111e5 Mon Sep 17 00:00:00 2001 From: Wonderfall Date: Mon, 24 Jan 2022 13:14:26 +0100 Subject: [PATCH 68/81] disable legacy XSS filtering (#17289) Browsers are phasing out X-XSS-Protection, but Safari and IE still support it. --- config/environments/production.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/environments/production.rb b/config/environments/production.rb index df6b07d77df7ae..81580a268a127c 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -118,7 +118,7 @@ 'Server' => 'Mastodon', 'X-Frame-Options' => 'DENY', 'X-Content-Type-Options' => 'nosniff', - 'X-XSS-Protection' => '1; mode=block', + 'X-XSS-Protection' => '0', 'Permissions-Policy' => 'interest-cohort=()', } From bb7b2868a0d52a5e656c46642f1563c0b952f6f7 Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 2 Feb 2022 11:49:22 +0100 Subject: [PATCH 69/81] Bump version to 3.4.6 --- CHANGELOG.md | 12 ++++++++++++ chart/values.yaml | 2 +- docker-compose.yml | 6 +++--- lib/mastodon/version.rb | 2 +- 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9deff5a0d1f090..c2eff7fa3146c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,18 @@ Changelog All notable changes to this project will be documented in this file. +## [3.4.6] - 2022-02-03 +### Fixed +- Fix `mastodon:webpush:generate_vapid_key` task requiring a functional environment ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17338)) +- Fix spurious errors when receiving an Add activity for a private post ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17425)) + +### Security +- Fix error-prone SQL queries ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15828)) +- Fix not compacting incoming signed JSON-LD activities ([puckipedia](https://github.com/mastodon/mastodon/pull/17426), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17428)) (CVE-2022-24307) +- Fix insufficient sanitization of report comments ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17430)) +- Fix stop condition of a Common Table Expression ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17427)) +- Disable legacy XSS filtering ([Wonderfall](https://github.com/mastodon/mastodon/pull/17289)) + ## [3.4.5] - 2022-01-31 ### Added - Add more advanced migration tests ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17393)) diff --git a/chart/values.yaml b/chart/values.yaml index a638f4a7d6606f..d3036bb4a200bc 100644 --- a/chart/values.yaml +++ b/chart/values.yaml @@ -8,7 +8,7 @@ image: # built from the most recent commit # # tag: latest - tag: v3.3.0 + tag: v3.4.6 # use `Always` when using `latest` tag pullPolicy: IfNotPresent diff --git a/docker-compose.yml b/docker-compose.yml index 288a4fdf697dbc..97b6f16552ea29 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -43,7 +43,7 @@ services: web: build: . - image: tootsuite/mastodon:v3.4.5 + image: tootsuite/mastodon:v3.4.6 restart: always env_file: .env.production command: bash -c "rm -f /mastodon/tmp/pids/server.pid; bundle exec rails s -p 3000" @@ -63,7 +63,7 @@ services: streaming: build: . - image: tootsuite/mastodon:v3.4.5 + image: tootsuite/mastodon:v3.4.6 restart: always env_file: .env.production command: node ./streaming @@ -80,7 +80,7 @@ services: sidekiq: build: . - image: tootsuite/mastodon:v3.4.5 + image: tootsuite/mastodon:v3.4.6 restart: always env_file: .env.production command: bundle exec sidekiq diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 1ba45aeca33099..d71b5b4ac199cf 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -13,7 +13,7 @@ def minor end def patch - 5 + 6 end def flags From 93a6c143af894d478a282b96a8b8e826efd1f43a Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 3 Feb 2022 14:08:24 +0100 Subject: [PATCH 70/81] Fix insufficient sanitization of report comments (#17430) From ec1bb2a4fec0acb447c99ba73bb32dcfbfdf858a Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 28 Feb 2022 12:15:44 +0100 Subject: [PATCH 71/81] Fix being able to bypass e-mail restrictions --- app/models/user.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index b3fb55dfc018a1..b7cb7d8897356c 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -86,11 +86,11 @@ class User < ApplicationRecord validates :invite_request, presence: true, on: :create, if: :invite_text_required? validates :locale, inclusion: I18n.available_locales.map(&:to_s), if: :locale? - validates_with BlacklistedEmailValidator, on: :create + validates_with BlacklistedEmailValidator, if: -> { !confirmed? } validates_with EmailMxValidator, if: :validate_email_dns? validates :agreement, acceptance: { allow_nil: false, accept: [true, 'true', '1'] }, on: :create - # Those are honeypot/antispam fields + # Honeypot/anti-spam fields attr_accessor :registration_form_time, :website, :confirm_password validates_with RegistrationFormTimeValidator, on: :create From cc706b1191df5f272ee5e53e69860062d8a2b989 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 22 Mar 2022 09:51:46 +0100 Subject: [PATCH 72/81] Fix being able to post URLs longer than 4096 characters --- app/lib/extractor.rb | 7 +++ app/validators/status_length_validator.rb | 50 +++++++++++++------ .../status_length_validator_spec.rb | 15 ++++++ 3 files changed, 58 insertions(+), 14 deletions(-) diff --git a/app/lib/extractor.rb b/app/lib/extractor.rb index 8020aa916605b0..a8fb77e9476c8d 100644 --- a/app/lib/extractor.rb +++ b/app/lib/extractor.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true module Extractor + MAX_DOMAIN_LENGTH = 253 + extend Twitter::TwitterText::Extractor module_function @@ -14,7 +16,12 @@ def extract_mentions_or_lists_with_indices(text) text.to_s.scan(Account::MENTION_RE) do |screen_name, _| match_data = $LAST_MATCH_INFO after = $' + unless Twitter::TwitterText::Regex[:end_mention_match].match?(after) + username, domain = screen_name.split('@') + + next if domain.present? && domain.length > MAX_DOMAIN_LENGTH + start_position = match_data.char_begin(1) - 1 end_position = match_data.char_end(1) possible_entries << { diff --git a/app/validators/status_length_validator.rb b/app/validators/status_length_validator.rb index 4c258ec16ef8dd..e107912b77df63 100644 --- a/app/validators/status_length_validator.rb +++ b/app/validators/status_length_validator.rb @@ -3,35 +3,57 @@ class StatusLengthValidator < ActiveModel::Validator MAX_CHARS = 500 URL_PLACEHOLDER_CHARS = 23 - URL_PLACEHOLDER = "\1#{'x' * URL_PLACEHOLDER_CHARS}" + URL_PLACEHOLDER = 'x' * 23 def validate(status) return unless status.local? && !status.reblog? - @status = status - status.errors.add(:text, I18n.t('statuses.over_character_limit', max: MAX_CHARS)) if too_long? + status.errors.add(:text, I18n.t('statuses.over_character_limit', max: MAX_CHARS)) if too_long?(status) end private - def too_long? - countable_length > MAX_CHARS + def too_long?(status) + countable_length(combined_text(status)) > MAX_CHARS end - def countable_length - total_text.mb_chars.grapheme_length + def countable_length(str) + str.mb_chars.grapheme_length end - def total_text - [@status.spoiler_text, countable_text].join + def combined_text(status) + [status.spoiler_text, countable_text(status.text)].join end - def countable_text - return '' if @status.text.nil? + def countable_text(str) + return '' if str.blank? - @status.text.dup.tap do |new_text| - new_text.gsub!(FetchLinkCardService::URL_PATTERN, URL_PLACEHOLDER) - new_text.gsub!(Account::MENTION_RE, '@\2') + # To ensure that we only give length concessions to entities that + # will be correctly parsed during formatting, we go through full + # entity extraction + + entities = Extractor.remove_overlapping_entities(Extractor.extract_urls_with_indices(str, extract_url_without_protocol: false) + Extractor.extract_mentions_or_lists_with_indices(str)) + + rewrite_entities(str, entities) do |entity| + if entity[:url] + URL_PLACEHOLDER + elsif entity[:screen_name] + "@#{entity[:screen_name].split('@').first}" + end end end + + def rewrite_entities(str, entities) + entities.sort_by! { |entity| entity[:indices].first } + result = ''.dup + + last_index = entities.reduce(0) do |index, entity| + result << str[index...entity[:indices].first] + result << yield(entity) + entity[:indices].last + end + + result << str[last_index..-1] + result + end end diff --git a/spec/validators/status_length_validator_spec.rb b/spec/validators/status_length_validator_spec.rb index bef3f29f50c915..db9c728a8b698f 100644 --- a/spec/validators/status_length_validator_spec.rb +++ b/spec/validators/status_length_validator_spec.rb @@ -50,6 +50,13 @@ expect(status.errors).to have_received(:add) end + it 'does not count overly long URLs as 23 characters flat' do + text = "http://example.com/valid?#{'#foo?' * 1000}" + status = double(spoiler_text: '', text: text, errors: double(add: nil), local?: true, reblog?: false) + subject.validate(status) + expect(status.errors).to have_received(:add) + end + it 'counts only the front part of remote usernames' do text = ('a' * 475) + " @alice@#{'b' * 30}.com" status = double(spoiler_text: '', text: text, errors: double(add: nil), local?: true, reblog?: false) @@ -57,5 +64,13 @@ subject.validate(status) expect(status.errors).to_not have_received(:add) end + + it 'does count both parts of remote usernames for overly long domains' do + text = "@alice@#{'b' * 500}.com" + status = double(spoiler_text: '', text: text, errors: double(add: nil), local?: true, reblog?: false) + + subject.validate(status) + expect(status.errors).to have_received(:add) + end end end From 2a28247f22852c6441221f348dac7cc23fcb3e02 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 30 Mar 2022 14:30:57 +0200 Subject: [PATCH 73/81] Bump version to 3.4.7 --- CHANGELOG.md | 6 ++++++ lib/mastodon/version.rb | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c2eff7fa3146c5..3359b26324e230 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,12 @@ Changelog All notable changes to this project will be documented in this file. +## [3.4.7] - 2022-03-30 +### Security + +- Fix being able to post URLs longer than 4096 characters ([Gargron](https://github.com/mastodon/mastodon/pull/17908)) +- Fix being able to bypass e-mail restrictions ([Gargron](https://github.com/mastodon/mastodon/pull/17909)) + ## [3.4.6] - 2022-02-03 ### Fixed - Fix `mastodon:webpush:generate_vapid_key` task requiring a functional environment ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17338)) diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index d71b5b4ac199cf..29775f750c9fbf 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -13,7 +13,7 @@ def minor end def patch - 6 + 7 end def flags From 6cd204532f2db7e7793abae0435d61003427f489 Mon Sep 17 00:00:00 2001 From: Gyuhwan Park Date: Fri, 15 Apr 2022 17:38:11 +0900 Subject: [PATCH 74/81] Merge tag 'v3.5.1' into twingyeo-kr --- .circleci/config.yml | 374 +- .codeclimate.yml | 9 +- .devcontainer/Dockerfile | 24 + .devcontainer/devcontainer.json | 26 + .devcontainer/docker-compose.yml | 83 + .dockerignore | 1 + .env.nanobox | 7 +- .env.production.sample | 7 +- .eslintrc.js | 5 + .github/CODEOWNERS | 32 - .github/FUNDING.yml | 2 +- .github/ISSUE_TEMPLATE/1.bug_report.yml | 42 + .github/ISSUE_TEMPLATE/2.feature_request.yml | 22 + .github/ISSUE_TEMPLATE/bug_report.md | 27 - .github/ISSUE_TEMPLATE/config.yml | 7 +- .github/ISSUE_TEMPLATE/feature_request.md | 16 - .github/ISSUE_TEMPLATE/support.md | 10 - .github/dependabot.yml | 4 +- .github/workflows/build-image.yml | 16 +- .github/workflows/check-i18n.yml | 40 +- .gitignore | 1 + .nvmrc | 2 +- .prettierignore | 78 + .prettierrc.js | 3 + .rubocop.yml | 30 +- .ruby-version | 2 +- AUTHORS.md | 639 ++- Aptfile | 2 - CHANGELOG.md | 272 +- Dockerfile | 22 +- FEDERATION.md | 30 + Gemfile | 96 +- Gemfile.lock | 661 ++- README.md | 44 +- SECURITY.md | 19 +- Vagrantfile | 16 +- app.json | 5 +- app/chewy/accounts_index.rb | 24 +- app/chewy/statuses_index.rb | 50 +- app/chewy/tags_index.rb | 16 +- app/controllers/accounts_controller.rb | 13 +- .../activitypub/base_controller.rb | 1 + .../activitypub/collections_controller.rb | 1 + .../activitypub/outboxes_controller.rb | 2 +- .../activitypub/replies_controller.rb | 32 +- .../account_moderation_notes_controller.rb | 2 +- app/controllers/admin/accounts_controller.rb | 47 +- app/controllers/admin/dashboard_controller.rb | 37 +- .../admin/disputes/appeals_controller.rb | 40 + .../admin/domain_blocks_controller.rb | 4 - .../admin/email_domain_blocks_controller.rb | 72 +- app/controllers/admin/instances_controller.rb | 31 +- .../admin/pending_accounts_controller.rb | 52 - .../admin/relationships_controller.rb | 3 +- .../admin/report_notes_controller.rb | 23 +- .../admin/reported_statuses_controller.rb | 44 - .../admin/reports/actions_controller.rb | 52 + app/controllers/admin/reports_controller.rb | 6 +- app/controllers/admin/resets_controller.rb | 4 +- app/controllers/admin/statuses_controller.rb | 71 +- app/controllers/admin/tags_controller.rb | 76 +- .../preview_card_providers_controller.rb | 41 + .../admin/trends/links_controller.rb | 45 + .../admin/trends/statuses_controller.rb | 45 + .../admin/trends/tags_controller.rb | 41 + .../two_factor_authentications_controller.rb | 2 +- app/controllers/api/base_controller.rb | 10 +- app/controllers/api/proofs_controller.rb | 23 - .../accounts/familiar_followers_controller.rb | 25 + .../v1/accounts/identity_proofs_controller.rb | 3 +- .../api/v1/accounts/statuses_controller.rb | 43 +- app/controllers/api/v1/accounts_controller.rb | 19 +- .../v1/admin/account_actions_controller.rb | 2 +- .../api/v1/admin/accounts_controller.rb | 25 +- .../api/v1/admin/dimensions_controller.rb | 23 + .../api/v1/admin/measures_controller.rb | 22 + .../api/v1/admin/reports_controller.rb | 14 +- .../api/v1/admin/retention_controller.rb | 21 + .../api/v1/admin/trends/links_controller.rb | 17 + .../v1/admin/trends/statuses_controller.rb | 17 + .../api/v1/admin/trends/tags_controller.rb | 17 + app/controllers/api/v1/blocks_controller.rb | 2 +- .../api/v1/domain_blocks_controller.rb | 4 +- .../api/v1/emails/confirmations_controller.rb | 4 +- .../api/v1/follow_requests_controller.rb | 6 +- .../api/v1/instances/activity_controller.rb | 27 +- app/controllers/api/v1/media_controller.rb | 8 +- app/controllers/api/v1/mutes_controller.rb | 2 +- .../api/v1/notifications_controller.rb | 19 +- app/controllers/api/v1/reports_controller.rb | 14 +- .../api/v1/statuses/histories_controller.rb | 21 + .../api/v1/statuses/sources_controller.rb | 21 + app/controllers/api/v1/statuses_controller.rb | 63 +- .../api/v1/trends/links_controller.rb | 53 + .../api/v1/trends/statuses_controller.rb | 53 + .../api/v1/trends/tags_controller.rb | 49 + app/controllers/api/v1/trends_controller.rb | 15 - .../api/v2/admin/accounts_controller.rb | 31 + app/controllers/api/v2/search_controller.rb | 4 + app/controllers/api/web/embeds_controller.rb | 2 +- .../api/web/push_subscriptions_controller.rb | 13 +- app/controllers/application_controller.rb | 7 +- .../auth/omniauth_callbacks_controller.rb | 15 +- .../auth/registrations_controller.rb | 17 +- app/controllers/auth/sessions_controller.rb | 66 +- .../concerns/access_token_tracking_concern.rb | 21 + .../concerns/account_owned_concern.rb | 5 + .../concerns/accountable_concern.rb | 4 +- app/controllers/concerns/authorization.rb | 2 +- app/controllers/concerns/localized.rb | 33 +- .../concerns/session_tracking_concern.rb | 4 +- .../sign_in_token_authentication_concern.rb | 56 - .../two_factor_authentication_concern.rb | 10 +- .../concerns/user_tracking_concern.rb | 6 +- .../disputes/appeals_controller.rb | 26 + app/controllers/disputes/base_controller.rb | 18 + .../disputes/strikes_controller.rb | 21 + .../follower_accounts_controller.rb | 6 +- .../following_accounts_controller.rb | 6 +- app/controllers/home_controller.rb | 25 +- app/controllers/instance_actors_controller.rb | 1 + app/controllers/media_controller.rb | 7 +- .../settings/identity_proofs_controller.rb | 60 - .../settings/login_activities_controller.rb | 7 + .../settings/preferences_controller.rb | 4 +- .../settings/profiles_controller.rb | 2 +- .../webauthn_credentials_controller.rb | 3 +- .../statuses_cleanup_controller.rb | 35 + .../keybase_proof_config_controller.rb | 9 - app/helpers/accounts_helper.rb | 8 +- .../admin/account_moderation_notes_helper.rb | 4 +- app/helpers/admin/action_logs_helper.rb | 8 + app/helpers/admin/dashboard_helper.rb | 39 +- app/helpers/admin/filter_helper.rb | 6 +- app/helpers/admin/trends/statuses_helper.rb | 17 + app/helpers/application_helper.rb | 60 +- app/helpers/context_helper.rb | 1 - app/helpers/formatting_helper.rb | 28 + app/helpers/jsonld_helper.rb | 18 +- app/helpers/languages_helper.rb | 257 + app/helpers/routing_helper.rb | 3 +- app/helpers/settings_helper.rb | 88 +- app/helpers/statuses_helper.rb | 26 +- app/javascript/mastodon/actions/accounts.js | 32 + app/javascript/mastodon/actions/compose.js | 109 +- app/javascript/mastodon/actions/history.js | 37 + .../mastodon/actions/identity_proofs.js | 31 - .../mastodon/actions/importer/normalizer.js | 9 +- app/javascript/mastodon/actions/modal.js | 3 +- .../mastodon/actions/notifications.js | 22 +- app/javascript/mastodon/actions/reports.js | 115 +- app/javascript/mastodon/actions/rules.js | 27 + app/javascript/mastodon/actions/statuses.js | 40 +- app/javascript/mastodon/actions/streaming.js | 21 +- app/javascript/mastodon/actions/timelines.js | 22 + app/javascript/mastodon/actions/trends.js | 137 +- app/javascript/mastodon/api.js | 26 +- app/javascript/mastodon/components/account.js | 2 +- .../mastodon/components/admin/Counter.js | 117 + .../mastodon/components/admin/Dimension.js | 93 + .../components/admin/ReportReasonSelector.js | 159 + .../mastodon/components/admin/Retention.js | 151 + .../mastodon/components/admin/Trends.js | 73 + .../mastodon/components/attachment_list.js | 36 +- app/javascript/mastodon/components/check.js | 9 + .../mastodon/components/column_header.js | 6 +- .../mastodon/components/dropdown_menu.js | 149 +- .../containers/dropdown_menu_container.js | 27 + .../components/edited_timestamp/index.js | 70 + app/javascript/mastodon/components/hashtag.js | 63 +- .../mastodon/components/icon_button.js | 18 +- .../mastodon/components/inline_account.js | 34 + .../mastodon/components/loading_indicator.js | 27 +- .../mastodon/components/media_attachments.js | 116 + .../mastodon/components/modal_root.js | 5 +- app/javascript/mastodon/components/poll.js | 27 +- .../mastodon/components/relative_timestamp.js | 23 +- .../mastodon/components/scrollable_list.js | 2 +- .../mastodon/components/skeleton.js | 11 + app/javascript/mastodon/components/status.js | 77 +- .../mastodon/components/status_action_bar.js | 20 +- .../mastodon/components/status_content.js | 6 +- .../mastodon/components/status_list.js | 3 + .../mastodon/containers/admin_component.js | 26 + .../mastodon/containers/mastodon.js | 26 +- .../mastodon/containers/media_container.js | 6 +- .../mastodon/containers/status_container.js | 5 + .../features/account/components/header.js | 22 +- .../features/account_gallery/index.js | 66 +- .../account_timeline/components/header.js | 10 +- .../account_timeline/components/moved_note.js | 2 +- .../containers/header_container.js | 2 - .../features/account_timeline/index.js | 58 +- .../compose/components/compose_form.js | 20 +- .../components/emoji_picker_dropdown.js | 15 +- .../compose/components/navigation_bar.js | 4 +- .../compose/components/privacy_dropdown.js | 4 +- .../compose/components/reply_indicator.js | 2 +- .../compose/components/search_results.js | 2 +- .../features/compose/components/upload.js | 14 +- .../containers/compose_form_container.js | 2 + .../containers/reply_indicator_container.js | 17 +- .../compose/containers/upload_container.js | 6 +- .../mastodon/features/compose/index.js | 6 +- .../components/conversation.js | 4 +- .../directory/components/account_card.js | 204 +- .../mastodon/features/directory/index.js | 10 +- .../features/emoji/emoji_compressed.js | 2 +- .../mastodon/features/emoji/emoji_map.json | 2 +- .../features/emoji/emoji_mart_search_light.js | 2 +- .../features/explore/components/story.js | 51 + .../mastodon/features/explore/index.js | 91 + .../mastodon/features/explore/links.js | 48 + .../mastodon/features/explore/results.js | 113 + .../mastodon/features/explore/statuses.js | 57 + .../mastodon/features/explore/suggestions.js | 40 + .../mastodon/features/explore/tags.js | 40 + .../components/account.js | 2 +- .../features/follow_recommendations/index.js | 2 +- .../components/account_authorize.js | 2 +- .../mastodon/features/followers/index.js | 69 +- .../mastodon/features/following/index.js | 69 +- .../components/announcements.js | 6 +- .../getting_started/components/trends.js | 2 +- .../containers/trends_container.js | 6 +- .../features/getting_started/index.js | 37 +- .../components/column_settings.js | 16 +- .../containers/column_settings_container.js | 19 +- .../mastodon/features/lists/index.js | 2 +- .../components/column_settings.js | 40 +- .../components/follow_request.js | 2 +- .../notifications/components/notification.js | 66 +- .../picture_in_picture/components/footer.js | 6 +- .../picture_in_picture/components/header.js | 2 +- .../mastodon/features/report/category.js | 93 + .../mastodon/features/report/comment.js | 83 + .../features/report/components/option.js | 60 + .../report/components/status_check_box.js | 83 +- .../containers/status_check_box_container.js | 22 +- .../mastodon/features/report/rules.js | 64 + .../mastodon/features/report/statuses.js | 61 + .../mastodon/features/report/thanks.js | 84 + .../mastodon/features/search/index.js | 17 - .../features/status/components/action_bar.js | 12 +- .../features/status/components/card.js | 2 +- .../status/components/detailed_status.js | 19 +- .../mastodon/features/status/index.js | 8 +- .../features/ui/components/boost_modal.js | 2 +- .../features/ui/components/columns_area.js | 4 +- .../ui/components/compare_history_modal.js | 99 + .../ui/components/focal_point_modal.js | 123 +- .../features/ui/components/link_footer.js | 5 +- .../features/ui/components/list_panel.js | 2 +- .../features/ui/components/media_modal.js | 7 - .../features/ui/components/modal_root.js | 30 +- .../ui/components/navigation_panel.js | 12 +- .../features/ui/components/report_modal.js | 241 +- .../features/ui/components/tabs_bar.js | 8 +- .../features/ui/components/video_modal.js | 2 +- .../features/ui/containers/modal_container.js | 21 +- app/javascript/mastodon/features/ui/index.js | 53 +- .../features/ui/util/async-components.js | 12 +- .../mastodon/features/video/index.js | 6 +- app/javascript/mastodon/initial_state.js | 1 + app/javascript/mastodon/locales/af.json | 83 +- app/javascript/mastodon/locales/ar.json | 179 +- app/javascript/mastodon/locales/ast.json | 179 +- app/javascript/mastodon/locales/bg.json | 79 +- app/javascript/mastodon/locales/bn.json | 113 +- app/javascript/mastodon/locales/br.json | 337 +- app/javascript/mastodon/locales/ca.json | 307 +- app/javascript/mastodon/locales/ckb.json | 544 ++ app/javascript/mastodon/locales/co.json | 79 +- app/javascript/mastodon/locales/cs.json | 105 +- app/javascript/mastodon/locales/cy.json | 119 +- app/javascript/mastodon/locales/da.json | 407 +- app/javascript/mastodon/locales/de.json | 89 +- .../mastodon/locales/defaultMessages.json | 510 +- app/javascript/mastodon/locales/el.json | 79 +- app/javascript/mastodon/locales/en.json | 79 +- app/javascript/mastodon/locales/eo.json | 115 +- app/javascript/mastodon/locales/es-AR.json | 83 +- app/javascript/mastodon/locales/es-MX.json | 95 +- app/javascript/mastodon/locales/es.json | 107 +- app/javascript/mastodon/locales/et.json | 79 +- app/javascript/mastodon/locales/eu.json | 79 +- app/javascript/mastodon/locales/fa.json | 543 +- app/javascript/mastodon/locales/fi.json | 333 +- app/javascript/mastodon/locales/fr.json | 125 +- app/javascript/mastodon/locales/ga.json | 79 +- app/javascript/mastodon/locales/gd.json | 89 +- app/javascript/mastodon/locales/gl.json | 85 +- app/javascript/mastodon/locales/he.json | 167 +- app/javascript/mastodon/locales/hi.json | 81 +- app/javascript/mastodon/locales/hr.json | 105 +- app/javascript/mastodon/locales/hu.json | 91 +- app/javascript/mastodon/locales/hy.json | 179 +- app/javascript/mastodon/locales/id.json | 117 +- app/javascript/mastodon/locales/io.json | 79 +- app/javascript/mastodon/locales/is.json | 237 +- app/javascript/mastodon/locales/it.json | 165 +- app/javascript/mastodon/locales/ja.json | 81 +- app/javascript/mastodon/locales/ka.json | 79 +- app/javascript/mastodon/locales/kab.json | 109 +- app/javascript/mastodon/locales/kk.json | 79 +- app/javascript/mastodon/locales/kn.json | 83 +- app/javascript/mastodon/locales/ko.json | 175 +- app/javascript/mastodon/locales/ku.json | 1013 ++-- app/javascript/mastodon/locales/kw.json | 79 +- .../mastodon/locales/locale-data/sa.js | 97 + app/javascript/mastodon/locales/lt.json | 83 +- app/javascript/mastodon/locales/lv.json | 821 +-- app/javascript/mastodon/locales/mk.json | 79 +- app/javascript/mastodon/locales/ml.json | 81 +- app/javascript/mastodon/locales/mr.json | 83 +- app/javascript/mastodon/locales/ms.json | 553 +- app/javascript/mastodon/locales/nl.json | 177 +- app/javascript/mastodon/locales/nn.json | 119 +- app/javascript/mastodon/locales/no.json | 125 +- app/javascript/mastodon/locales/oc.json | 79 +- app/javascript/mastodon/locales/pa.json | 83 +- app/javascript/mastodon/locales/pl.json | 81 +- app/javascript/mastodon/locales/pt-BR.json | 467 +- app/javascript/mastodon/locales/pt-PT.json | 87 +- app/javascript/mastodon/locales/ro.json | 517 +- app/javascript/mastodon/locales/ru.json | 141 +- app/javascript/mastodon/locales/sa.json | 83 +- app/javascript/mastodon/locales/sc.json | 81 +- app/javascript/mastodon/locales/si.json | 183 +- app/javascript/mastodon/locales/sk.json | 203 +- app/javascript/mastodon/locales/sl.json | 283 +- app/javascript/mastodon/locales/sq.json | 79 +- app/javascript/mastodon/locales/sr-Latn.json | 79 +- app/javascript/mastodon/locales/sr.json | 109 +- app/javascript/mastodon/locales/sv.json | 103 +- app/javascript/mastodon/locales/szl.json | 83 +- app/javascript/mastodon/locales/ta.json | 79 +- app/javascript/mastodon/locales/tai.json | 83 +- app/javascript/mastodon/locales/te.json | 79 +- app/javascript/mastodon/locales/th.json | 109 +- app/javascript/mastodon/locales/tr.json | 173 +- app/javascript/mastodon/locales/tt.json | 85 +- app/javascript/mastodon/locales/ug.json | 83 +- app/javascript/mastodon/locales/uk.json | 87 +- app/javascript/mastodon/locales/ur.json | 249 +- app/javascript/mastodon/locales/vi.json | 163 +- .../mastodon/locales/whitelist_ckb.json | 2 + app/javascript/mastodon/locales/zgh.json | 81 +- app/javascript/mastodon/locales/zh-CN.json | 95 +- app/javascript/mastodon/locales/zh-HK.json | 93 +- app/javascript/mastodon/locales/zh-TW.json | 247 +- .../mastodon/reducers/accounts_map.js | 15 + app/javascript/mastodon/reducers/compose.js | 83 +- app/javascript/mastodon/reducers/history.js | 28 + .../mastodon/reducers/identity_proofs.js | 25 - app/javascript/mastodon/reducers/index.js | 10 +- app/javascript/mastodon/reducers/modal.js | 31 +- app/javascript/mastodon/reducers/reports.js | 64 - app/javascript/mastodon/reducers/rules.js | 13 + app/javascript/mastodon/reducers/search.js | 25 +- app/javascript/mastodon/reducers/settings.js | 6 + .../mastodon/reducers/status_lists.js | 30 + app/javascript/mastodon/reducers/timelines.js | 59 +- app/javascript/mastodon/reducers/trends.js | 43 +- .../service_worker/web_push_locales.js | 3 + .../service_worker/web_push_notifications.js | 4 +- app/javascript/mastodon/utils/numbers.js | 8 + app/javascript/packs/admin.js | 20 + app/javascript/packs/public.js | 16 +- app/javascript/styles/fonts/montserrat.scss | 2 + app/javascript/styles/fonts/roboto-mono.scss | 1 + app/javascript/styles/fonts/roboto.scss | 4 + app/javascript/styles/mailer.scss | 8 + .../styles/mastodon-light/diff.scss | 33 +- app/javascript/styles/mastodon/accounts.scss | 48 + app/javascript/styles/mastodon/admin.scss | 769 ++- app/javascript/styles/mastodon/boost.scss | 12 + .../styles/mastodon/components.scss | 971 ++-- .../styles/mastodon/containers.scss | 37 +- app/javascript/styles/mastodon/dashboard.scss | 71 +- .../styles/mastodon/emoji_picker.scss | 2 +- app/javascript/styles/mastodon/footer.scss | 17 +- app/javascript/styles/mastodon/forms.scss | 142 +- app/javascript/styles/mastodon/polls.scss | 17 +- app/javascript/styles/mastodon/rtl.scss | 24 +- app/javascript/styles/mastodon/tables.scss | 30 + app/javascript/styles/mastodon/widgets.scss | 18 + app/lib/access_token_extension.rb | 4 + app/lib/activity_tracker.rb | 70 +- app/lib/activitypub/activity.rb | 55 +- app/lib/activitypub/activity/accept.rb | 13 +- app/lib/activitypub/activity/add.rb | 5 +- app/lib/activitypub/activity/announce.rb | 23 +- app/lib/activitypub/activity/create.rb | 266 +- app/lib/activitypub/activity/delete.rb | 40 +- app/lib/activitypub/activity/follow.rb | 4 +- app/lib/activitypub/activity/like.rb | 4 +- app/lib/activitypub/activity/update.rb | 23 +- app/lib/activitypub/forwarder.rb | 65 + .../activitypub/parser/custom_emoji_parser.rb | 27 + .../parser/media_attachment_parser.rb | 60 + app/lib/activitypub/parser/poll_parser.rb | 53 + app/lib/activitypub/parser/status_parser.rb | 118 + app/lib/admin/metrics/dimension.rb | 22 + .../admin/metrics/dimension/base_dimension.rb | 68 + .../dimension/instance_accounts_dimension.rb | 35 + .../dimension/instance_languages_dimension.rb | 37 + .../metrics/dimension/languages_dimension.rb | 27 + .../metrics/dimension/servers_dimension.rb | 25 + .../dimension/software_versions_dimension.rb | 69 + .../metrics/dimension/sources_dimension.rb | 25 + .../dimension/space_usage_dimension.rb | 70 + .../dimension/tag_languages_dimension.rb | 36 + .../dimension/tag_servers_dimension.rb | 35 + app/lib/admin/metrics/measure.rb | 27 + .../metrics/measure/active_users_measure.rb | 33 + app/lib/admin/metrics/measure/base_measure.rb | 107 + .../measure/instance_accounts_measure.rb | 58 + .../measure/instance_followers_measure.rb | 59 + .../measure/instance_follows_measure.rb | 59 + .../instance_media_attachments_measure.rb | 69 + .../measure/instance_reports_measure.rb | 59 + .../measure/instance_statuses_measure.rb | 60 + .../metrics/measure/interactions_measure.rb | 33 + .../metrics/measure/new_users_measure.rb | 37 + .../metrics/measure/opened_reports_measure.rb | 37 + .../measure/resolved_reports_measure.rb | 37 + .../metrics/measure/tag_accounts_measure.rb | 41 + .../metrics/measure/tag_servers_measure.rb | 50 + .../admin/metrics/measure/tag_uses_measure.rb | 41 + app/lib/admin/metrics/retention.rb | 93 + app/lib/admin/system_check.rb | 1 + .../admin/system_check/elasticsearch_check.rb | 39 + app/lib/application_extension.rb | 4 + app/lib/delivery_failure_tracker.rb | 2 +- app/lib/emoji_formatter.rb | 98 + app/lib/extractor.rb | 83 +- app/lib/fast_geometry_parser.rb | 2 +- app/lib/feed_manager.rb | 34 +- app/lib/formatter.rb | 292 - app/lib/html_aware_formatter.rb | 38 + app/lib/language_detector.rb | 101 - app/lib/link_details_extractor.rb | 277 + app/lib/permalink_redirector.rb | 63 + app/lib/plain_text_formatter.rb | 30 + app/lib/proof_provider.rb | 12 - app/lib/proof_provider/keybase.rb | 69 - app/lib/proof_provider/keybase/badge.rb | 45 - .../keybase/config_serializer.rb | 76 - app/lib/proof_provider/keybase/serializer.rb | 25 - app/lib/proof_provider/keybase/verifier.rb | 59 - app/lib/proof_provider/keybase/worker.rb | 32 - app/lib/request.rb | 2 +- app/lib/rss/serializer.rb | 25 +- app/lib/scope_parser.rb | 10 + app/lib/scope_transformer.rb | 40 + app/lib/search_query_transformer.rb | 37 +- app/lib/status_reach_finder.rb | 31 +- app/lib/suspicious_sign_in_detector.rb | 42 + app/lib/text_formatter.rb | 158 + app/lib/user_settings_decorator.rb | 10 +- app/lib/video_metadata_extractor.rb | 3 +- app/mailers/admin_mailer.rb | 25 +- app/mailers/application_mailer.rb | 1 + app/mailers/user_mailer.rb | 37 +- app/models/account.rb | 47 +- app/models/account_filter.rb | 99 +- app/models/account_identity_proof.rb | 46 - app/models/account_stat.rb | 3 +- app/models/account_statuses_cleanup_policy.rb | 172 + app/models/account_statuses_filter.rb | 134 + .../account_suggestions/global_source.rb | 2 +- app/models/account_warning.rb | 29 +- app/models/admin/account_action.rb | 40 +- app/models/admin/action_log.rb | 2 +- app/models/admin/action_log_filter.rb | 8 +- app/models/admin/appeal_filter.rb | 49 + app/models/admin/status_batch_action.rb | 138 + app/models/admin/status_filter.rb | 41 + app/models/appeal.rb | 60 + app/models/bookmark.rb | 10 +- app/models/canonical_email_block.rb | 4 + app/models/concerns/account_associations.rb | 8 +- app/models/concerns/account_interactions.rb | 7 + app/models/concerns/account_merging.rb | 9 +- app/models/concerns/attachmentable.rb | 61 +- app/models/concerns/ldap_authenticable.rb | 2 +- app/models/concerns/omniauthable.rb | 23 +- .../concerns/status_snapshot_concern.rb | 35 + app/models/custom_emoji.rb | 6 +- app/models/domain_block.rb | 8 + app/models/email_domain_block.rb | 55 +- app/models/favourite.rb | 9 +- app/models/featured_tag.rb | 4 +- app/models/form/account_batch.rb | 63 +- app/models/form/email_domain_block_batch.rb | 30 + app/models/form/status_batch.rb | 45 - app/models/instance.rb | 36 +- app/models/instance_filter.rb | 16 +- app/models/login_activity.rb | 35 + app/models/media_attachment.rb | 50 +- app/models/notification.rb | 36 +- app/models/poll.rb | 7 + app/models/preview_card.rb | 56 +- app/models/preview_card_provider.rb | 57 + app/models/report.rb | 91 +- app/models/report_filter.rb | 16 +- app/models/status.rb | 166 +- app/models/status_edit.rb | 62 + app/models/status_pin.rb | 8 + app/models/tag.rb | 25 +- app/models/tag_filter.rb | 52 - app/models/trending_tags.rb | 128 - app/models/trends.rb | 49 + app/models/trends/base.rb | 96 + app/models/trends/history.rb | 98 + app/models/trends/links.rb | 120 + app/models/trends/preview_card_batch.rb | 65 + app/models/trends/preview_card_filter.rb | 46 + .../trends/preview_card_provider_batch.rb | 33 + .../trends/preview_card_provider_filter.rb | 49 + app/models/trends/query.rb | 109 + app/models/trends/status_batch.rb | 65 + app/models/trends/status_filter.rb | 46 + app/models/trends/statuses.rb | 129 + app/models/{form => trends}/tag_batch.rb | 14 +- app/models/trends/tag_filter.rb | 60 + app/models/trends/tags.rb | 92 + app/models/user.rb | 123 +- app/models/user_ip.rb | 19 + app/policies/account_policy.rb | 8 + app/policies/account_warning_policy.rb | 17 + app/policies/appeal_policy.rb | 13 + app/policies/instance_policy.rb | 4 + app/policies/preview_card_policy.rb | 11 + app/policies/preview_card_provider_policy.rb | 11 + app/policies/status_policy.rb | 4 + app/policies/tag_policy.rb | 4 + app/policies/user_policy.rb | 4 +- .../activitypub/activity_presenter.rb | 4 +- .../familiar_followers_presenter.rb | 17 + app/presenters/instance_presenter.rb | 4 +- .../status_relationships_presenter.rb | 2 +- .../activitypub/actor_serializer.rb | 12 +- .../activitypub/note_serializer.rb | 19 +- .../activitypub/undo_announce_serializer.rb | 2 +- app/serializers/initial_state_serializer.rb | 1 + app/serializers/manifest_serializer.rb | 6 +- app/serializers/rest/account_serializer.rb | 7 +- .../rest/admin/account_serializer.rb | 13 +- .../rest/admin/cohort_serializer.rb | 19 + .../rest/admin/dimension_serializer.rb | 5 + app/serializers/rest/admin/ip_serializer.rb | 5 + .../rest/admin/measure_serializer.rb | 21 + .../rest/admin/report_serializer.rb | 7 +- app/serializers/rest/admin/tag_serializer.rb | 13 + .../rest/announcement_serializer.rb | 4 +- .../rest/familiar_followers_serializer.rb | 11 + .../rest/identity_proof_serializer.rb | 17 - .../rest/media_attachment_serializer.rb | 2 +- .../rest/notification_serializer.rb | 2 +- .../rest/status_edit_serializer.rb | 22 + app/serializers/rest/status_serializer.rb | 14 +- .../rest/status_source_serializer.rb | 9 + .../rest/trends/link_serializer.rb | 5 + .../account_statuses_cleanup_service.rb | 27 + .../fetch_featured_collection_service.rb | 20 +- .../activitypub/fetch_remote_poll_service.rb | 2 +- .../fetch_remote_status_service.rb | 47 +- .../activitypub/process_account_service.rb | 26 - .../activitypub/process_poll_service.rb | 64 - .../process_status_update_service.rb | 311 ++ app/services/appeal_service.rb | 28 + app/services/approve_appeal_service.rb | 82 + app/services/backup_service.rb | 4 +- app/services/batched_remove_status_service.rb | 2 +- app/services/bootstrap_timeline_service.rb | 7 + app/services/concerns/payloadable.rb | 18 +- app/services/delete_account_service.rb | 42 +- app/services/fan_out_on_write_service.rb | 158 +- app/services/favourite_service.rb | 4 +- app/services/fetch_link_card_service.rb | 84 +- app/services/follow_service.rb | 4 +- app/services/import_service.rb | 4 +- app/services/notify_service.rb | 143 +- app/services/post_status_service.rb | 16 +- app/services/process_hashtags_service.rb | 42 +- app/services/process_mentions_service.rb | 65 +- app/services/purge_domain_service.rb | 11 + app/services/reblog_service.rb | 14 +- app/services/remove_from_followers_service.rb | 25 + app/services/remove_status_service.rb | 21 +- app/services/report_service.rb | 22 +- app/services/resolve_account_service.rb | 2 +- app/services/unsuspend_account_service.rb | 1 + app/services/update_status_service.rb | 160 + app/validators/blacklisted_email_validator.rb | 26 +- app/validators/email_mx_validator.rb | 20 +- app/validators/import_validator.rb | 2 + app/validators/reaction_validator.rb | 2 +- app/validators/status_pin_validator.rb | 2 +- app/views/about/_logged_in.html.haml | 10 + app/views/about/_login.html.haml | 29 +- app/views/about/_registration.html.haml | 28 +- app/views/about/show.html.haml | 5 +- app/views/accounts/_bio.html.haml | 16 +- app/views/accounts/_moved.html.haml | 4 +- app/views/admin/account_actions/new.html.haml | 4 +- .../_account_moderation_note.html.haml | 7 - .../_account_warning.html.haml | 31 +- app/views/admin/accounts/_account.html.haml | 59 +- app/views/admin/accounts/index.html.haml | 58 +- app/views/admin/accounts/show.html.haml | 75 +- app/views/admin/action_logs/index.html.haml | 2 +- app/views/admin/change_emails/show.html.haml | 2 +- .../custom_emojis/_custom_emoji.html.haml | 2 +- app/views/admin/custom_emojis/new.html.haml | 2 +- app/views/admin/dashboard/index.html.haml | 188 +- .../admin/disputes/appeals/_appeal.html.haml | 21 + .../admin/disputes/appeals/index.html.haml | 22 + app/views/admin/domain_blocks/edit.html.haml | 4 +- app/views/admin/domain_blocks/new.html.haml | 4 +- app/views/admin/domain_blocks/show.html.haml | 25 - .../_email_domain_block.html.haml | 27 +- .../admin/email_domain_blocks/index.html.haml | 28 +- .../admin/email_domain_blocks/new.html.haml | 32 +- .../follow_recommendations/show.html.haml | 6 +- .../instances/_exhausted_deliveries_days.haml | 2 - app/views/admin/instances/_instance.html.haml | 28 +- app/views/admin/instances/index.html.haml | 19 +- app/views/admin/instances/show.html.haml | 168 +- app/views/admin/ip_blocks/_ip_block.html.haml | 6 +- .../admin/pending_accounts/_account.html.haml | 16 - .../admin/pending_accounts/index.html.haml | 33 - app/views/admin/relationships/index.html.haml | 25 +- .../admin/report_notes/_report_note.html.haml | 23 +- app/views/admin/reports/_action_log.html.haml | 6 - app/views/admin/reports/_actions.html.haml | 33 + app/views/admin/reports/_status.html.haml | 24 +- app/views/admin/reports/index.html.haml | 10 +- app/views/admin/reports/show.html.haml | 276 +- app/views/admin/statuses/index.html.haml | 35 +- app/views/admin/statuses/show.html.haml | 27 - app/views/admin/tags/_tag.html.haml | 19 - app/views/admin/tags/index.html.haml | 74 - app/views/admin/tags/show.html.haml | 68 +- .../trends/links/_preview_card.html.haml | 30 + app/views/admin/trends/links/index.html.haml | 51 + .../_preview_card_provider.html.haml | 16 + .../preview_card_providers/index.html.haml | 46 + .../admin/trends/statuses/_status.html.haml | 33 + .../admin/trends/statuses/index.html.haml | 47 + app/views/admin/trends/tags/_tag.html.haml | 24 + app/views/admin/trends/tags/index.html.haml | 40 + .../admin_mailer/_new_trending_links.text.erb | 14 + .../_new_trending_statuses.text.erb | 14 + .../admin_mailer/_new_trending_tags.text.erb | 14 + app/views/admin_mailer/new_appeal.text.erb | 9 + .../admin_mailer/new_pending_account.text.erb | 4 +- app/views/admin_mailer/new_report.text.erb | 2 +- .../admin_mailer/new_trending_tag.text.erb | 5 - app/views/admin_mailer/new_trends.text.erb | 13 + app/views/application/_sidebar.html.haml | 2 +- .../registrations/_account_warning.html.haml | 20 + .../auth/registrations/_sessions.html.haml | 5 +- .../auth/registrations/_status.html.haml | 47 +- app/views/auth/sessions/new.html.haml | 25 +- .../auth/sessions/sign_in_token.html.haml | 14 - app/views/auth/shared/_links.html.haml | 2 +- .../authorize_interactions/show.html.haml | 2 +- .../authorize_interactions/success.html.haml | 2 +- app/views/directories/index.html.haml | 57 +- app/views/disputes/strikes/index.html.haml | 6 + app/views/disputes/strikes/show.html.haml | 128 + app/views/follower_accounts/index.html.haml | 2 +- app/views/following_accounts/index.html.haml | 2 +- app/views/layouts/modal.html.haml | 3 +- app/views/layouts/public.html.haml | 9 +- .../notification_mailer/_status.html.haml | 12 +- .../notification_mailer/_status.text.erb | 8 +- app/views/notification_mailer/digest.text.erb | 4 +- .../notification_mailer/favourite.html.haml | 2 +- .../notification_mailer/favourite.text.erb | 2 +- .../notification_mailer/follow.html.haml | 2 +- app/views/notification_mailer/follow.text.erb | 2 +- .../follow_request.html.haml | 2 +- .../follow_request.text.erb | 2 +- .../notification_mailer/mention.html.haml | 2 +- .../notification_mailer/mention.text.erb | 2 +- .../notification_mailer/reblog.html.haml | 2 +- app/views/notification_mailer/reblog.text.erb | 2 +- app/views/oauth/authorizations/new.html.haml | 50 +- .../authorized_applications/index.html.haml | 62 +- app/views/settings/aliases/index.html.haml | 2 +- .../settings/identity_proofs/_proof.html.haml | 21 - .../settings/identity_proofs/index.html.haml | 17 - .../settings/identity_proofs/new.html.haml | 36 - .../_login_activity.html.haml | 17 + .../settings/login_activities/index.html.haml | 15 + app/views/settings/migrations/show.html.haml | 4 +- .../preferences/appearance/show.html.haml | 2 +- .../preferences/notifications/show.html.haml | 4 + .../settings/preferences/other/show.html.haml | 7 +- app/views/settings/profiles/show.html.haml | 8 +- app/views/statuses/_detailed_status.html.haml | 18 +- app/views/statuses/_og_image.html.haml | 2 +- app/views/statuses/_poll.html.haml | 4 +- app/views/statuses/_simple_status.html.haml | 14 +- app/views/statuses/_status.html.haml | 2 +- app/views/statuses_cleanup/show.html.haml | 45 + .../user_mailer/appeal_approved.html.haml | 59 + .../user_mailer/appeal_approved.text.erb | 7 + .../user_mailer/appeal_rejected.html.haml | 59 + .../user_mailer/appeal_rejected.text.erb | 7 + ...html.haml => suspicious_sign_in.html.haml} | 50 +- ...n.text.erb => suspicious_sign_in.text.erb} | 10 +- app/views/user_mailer/warning.html.haml | 24 +- app/views/user_mailer/warning.text.erb | 20 +- .../activitypub/distribution_worker.rb | 48 +- app/workers/activitypub/processing_worker.rb | 5 +- .../activitypub/raw_distribution_worker.rb | 37 +- .../activitypub/reply_distribution_worker.rb | 34 - .../status_update_distribution_worker.rb | 29 + .../activitypub/update_distribution_worker.rb | 25 +- app/workers/admin/domain_purge_worker.rb | 11 + app/workers/distribution_worker.rb | 4 +- app/workers/feed_insert_worker.rb | 36 +- app/workers/local_notification_worker.rb | 9 + app/workers/poll_expiration_notify_worker.rb | 47 +- app/workers/push_update_worker.rb | 35 +- app/workers/remote_account_refresh_worker.rb | 24 + .../accounts_statuses_cleanup_scheduler.rb | 96 + .../email_domain_block_refresh_scheduler.rb | 31 + .../follow_recommendations_scheduler.rb | 33 +- app/workers/scheduler/ip_cleanup_scheduler.rb | 4 +- .../refresh_scheduler.rb} | 4 +- .../trends/review_notifications_scheduler.rb | 11 + .../scheduler/user_cleanup_scheduler.rb | 9 + boxfile.yml | 32 +- chart/Chart.lock | 6 +- chart/Chart.yaml | 4 +- chart/readme.md | 2 - chart/templates/configmap-env.yaml | 224 + chart/templates/ingress.yaml | 24 +- chart/values.yaml | 110 +- config/application.rb | 16 +- config/boot.rb | 2 - config/brakeman.ignore | 158 +- config/database.yml | 7 +- config/deploy.rb | 2 +- config/environments/development.rb | 2 +- config/environments/production.rb | 17 +- config/environments/test.rb | 3 + config/i18n-tasks.yml | 6 +- config/initializers/chewy.rb | 30 +- .../initializers/content_security_policy.rb | 16 + config/initializers/devise.rb | 19 +- config/initializers/omniauth.rb | 46 +- config/initializers/paperclip.rb | 20 + config/initializers/pghero.rb | 1 + config/initializers/preload_link_headers.rb | 2 +- config/initializers/rack_attack.rb | 13 +- config/initializers/session_store.rb | 2 +- config/initializers/sidekiq.rb | 2 + config/initializers/twitter_regex.rb | 57 +- config/locales/activerecord.ast.yml | 15 +- config/locales/activerecord.br.yml | 15 + config/locales/activerecord.ca.yml | 4 +- config/locales/activerecord.ckb.yml | 17 + config/locales/activerecord.cy.yml | 15 + config/locales/activerecord.da.yml | 4 +- config/locales/activerecord.es-MX.yml | 2 +- config/locales/activerecord.fi.yml | 2 +- config/locales/activerecord.fr.yml | 2 +- config/locales/activerecord.hy.yml | 14 +- config/locales/activerecord.id.yml | 2 +- config/locales/activerecord.is.yml | 2 +- config/locales/activerecord.it.yml | 2 +- config/locales/activerecord.kab.yml | 10 + config/locales/activerecord.ku.yml | 23 +- config/locales/activerecord.lv.yml | 31 + config/locales/activerecord.nl.yml | 2 +- config/locales/activerecord.nn.yml | 1 + config/locales/activerecord.no.yml | 1 + config/locales/activerecord.pt-BR.yml | 20 +- config/locales/activerecord.ro.yml | 17 +- config/locales/activerecord.si.yml | 14 + config/locales/activerecord.sl.yml | 15 + config/locales/activerecord.sr.yml | 19 +- config/locales/activerecord.th.yml | 3 + config/locales/activerecord.tt.yml | 2 + config/locales/ar.yml | 476 +- config/locales/ast.yml | 172 +- config/locales/bg.yml | 9 +- config/locales/bn.yml | 32 - config/locales/br.yml | 59 +- config/locales/ca.yml | 545 +- config/locales/ckb.yml | 1204 ++++ config/locales/co.yml | 205 +- config/locales/cs.yml | 590 +- config/locales/cy.yml | 137 +- config/locales/da.yml | 864 +-- config/locales/de.yml | 475 +- config/locales/devise.ast.yml | 27 +- config/locales/devise.br.yml | 74 +- config/locales/devise.ca.yml | 32 +- config/locales/devise.ckb.yml | 115 + config/locales/devise.da.yml | 62 +- config/locales/devise.eo.yml | 10 +- config/locales/devise.fi.yml | 17 + config/locales/devise.fr.yml | 14 +- config/locales/devise.gl.yml | 2 +- config/locales/devise.hu.yml | 2 +- config/locales/devise.hy.yml | 2 +- config/locales/devise.it.yml | 2 +- config/locales/devise.kab.yml | 4 + config/locales/devise.ko.yml | 2 +- config/locales/devise.ku.yml | 178 +- config/locales/devise.lv.yml | 115 + config/locales/devise.pt-BR.yml | 64 +- config/locales/devise.pt-PT.yml | 46 +- config/locales/devise.ro.yml | 17 + config/locales/devise.si.yml | 19 +- config/locales/devise.sl.yml | 29 + config/locales/devise.th.yml | 18 +- config/locales/devise.zh-CN.yml | 50 +- config/locales/doorkeeper.ar.yml | 36 +- config/locales/doorkeeper.ast.yml | 15 +- config/locales/doorkeeper.bg.yml | 6 - config/locales/doorkeeper.br.yml | 88 +- config/locales/doorkeeper.ca.yml | 67 +- config/locales/doorkeeper.ckb.yml | 143 + config/locales/doorkeeper.co.yml | 6 - config/locales/doorkeeper.cs.yml | 51 +- config/locales/doorkeeper.cy.yml | 6 - config/locales/doorkeeper.da.yml | 103 +- config/locales/doorkeeper.de.yml | 43 +- config/locales/doorkeeper.el.yml | 33 +- config/locales/doorkeeper.en.yml | 43 +- config/locales/doorkeeper.eo.yml | 6 - config/locales/doorkeeper.es-AR.yml | 43 +- config/locales/doorkeeper.es-MX.yml | 43 +- config/locales/doorkeeper.es.yml | 43 +- config/locales/doorkeeper.et.yml | 5 - config/locales/doorkeeper.eu.yml | 43 +- config/locales/doorkeeper.fa.yml | 41 +- config/locales/doorkeeper.fi.yml | 59 +- config/locales/doorkeeper.fr.yml | 53 +- config/locales/doorkeeper.gd.yml | 45 +- config/locales/doorkeeper.gl.yml | 51 +- config/locales/doorkeeper.he.yml | 5 - config/locales/doorkeeper.hr.yml | 4 - config/locales/doorkeeper.hu.yml | 43 +- config/locales/doorkeeper.hy.yml | 16 +- config/locales/doorkeeper.id.yml | 43 +- config/locales/doorkeeper.is.yml | 51 +- config/locales/doorkeeper.it.yml | 57 +- config/locales/doorkeeper.ja.yml | 33 +- config/locales/doorkeeper.ka.yml | 5 - config/locales/doorkeeper.kab.yml | 8 +- config/locales/doorkeeper.kk.yml | 5 - config/locales/doorkeeper.ko.yml | 51 +- config/locales/doorkeeper.ku.yml | 245 +- config/locales/doorkeeper.lv.yml | 184 + config/locales/doorkeeper.ml.yml | 3 - config/locales/doorkeeper.nl.yml | 50 +- config/locales/doorkeeper.nn.yml | 9 +- config/locales/doorkeeper.no.yml | 9 +- config/locales/doorkeeper.oc.yml | 15 +- config/locales/doorkeeper.pl.yml | 6 - config/locales/doorkeeper.pt-BR.yml | 56 +- config/locales/doorkeeper.pt-PT.yml | 49 +- config/locales/doorkeeper.ro.yml | 9 +- config/locales/doorkeeper.ru.yml | 36 +- config/locales/doorkeeper.sc.yml | 6 - config/locales/doorkeeper.si.yml | 27 +- config/locales/doorkeeper.sk.yml | 10 +- config/locales/doorkeeper.sl.yml | 49 +- config/locales/doorkeeper.sq.yml | 43 +- config/locales/doorkeeper.sr-Latn.yml | 6 - config/locales/doorkeeper.sr.yml | 6 - config/locales/doorkeeper.sv.yml | 32 +- config/locales/doorkeeper.ta.yml | 1 - config/locales/doorkeeper.th.yml | 46 +- config/locales/doorkeeper.tr.yml | 43 +- config/locales/doorkeeper.tt.yml | 3 - config/locales/doorkeeper.uk.yml | 39 +- config/locales/doorkeeper.vi.yml | 49 +- config/locales/doorkeeper.zgh.yml | 5 - config/locales/doorkeeper.zh-CN.yml | 59 +- config/locales/doorkeeper.zh-HK.yml | 6 - config/locales/doorkeeper.zh-TW.yml | 43 +- config/locales/el.yml | 199 +- config/locales/en.yml | 516 +- config/locales/en_GB.yml | 7 +- config/locales/eo.yml | 188 +- config/locales/es-AR.yml | 512 +- config/locales/es-MX.yml | 483 +- config/locales/es.yml | 500 +- config/locales/et.yml | 112 +- config/locales/eu.yml | 363 +- config/locales/fa.yml | 471 +- config/locales/fi.yml | 891 ++- config/locales/fr.yml | 643 ++- config/locales/gd.yml | 560 +- config/locales/gl.yml | 554 +- config/locales/he.yml | 53 +- config/locales/hr.yml | 6 - config/locales/hu.yml | 552 +- config/locales/hy.yml | 284 +- config/locales/id.yml | 463 +- config/locales/io.yml | 8 +- config/locales/is.yml | 582 +- config/locales/it.yml | 538 +- config/locales/ja.yml | 367 +- config/locales/ka.yml | 42 +- config/locales/kab.yml | 255 +- config/locales/kk.yml | 106 +- config/locales/ko.yml | 532 +- config/locales/ku.yml | 2568 +++++---- config/locales/kw.yml | 12 + config/locales/lt.yml | 48 - config/locales/lv.yml | 1760 +++++- config/locales/ml.yml | 7 - config/locales/ms.yml | 457 +- config/locales/nl.yml | 233 +- config/locales/nn.yml | 267 +- config/locales/no.yml | 271 +- config/locales/oc.yml | 160 +- config/locales/pl.yml | 426 +- config/locales/pt-BR.yml | 380 +- config/locales/pt-PT.yml | 593 +- config/locales/ro.yml | 75 +- config/locales/ru.yml | 432 +- config/locales/sc.yml | 134 +- config/locales/si.yml | 163 +- config/locales/simple_form.ar.yml | 33 +- config/locales/simple_form.ast.yml | 9 +- config/locales/simple_form.br.yml | 32 + config/locales/simple_form.ca.yml | 83 +- config/locales/simple_form.ckb.yml | 206 + config/locales/simple_form.co.yml | 4 - config/locales/simple_form.cs.yml | 19 +- config/locales/simple_form.cy.yml | 5 +- config/locales/simple_form.da.yml | 107 +- config/locales/simple_form.de.yml | 13 +- config/locales/simple_form.el.yml | 4 - config/locales/simple_form.en.yml | 13 +- config/locales/simple_form.eo.yml | 39 +- config/locales/simple_form.es-AR.yml | 13 +- config/locales/simple_form.es-MX.yml | 17 +- config/locales/simple_form.es.yml | 15 +- config/locales/simple_form.et.yml | 4 - config/locales/simple_form.eu.yml | 13 +- config/locales/simple_form.fa.yml | 57 +- config/locales/simple_form.fi.yml | 109 +- config/locales/simple_form.fr.yml | 17 +- config/locales/simple_form.gd.yml | 27 +- config/locales/simple_form.gl.yml | 23 +- config/locales/simple_form.hu.yml | 23 +- config/locales/simple_form.hy.yml | 38 +- config/locales/simple_form.id.yml | 13 +- config/locales/simple_form.is.yml | 47 +- config/locales/simple_form.it.yml | 41 +- config/locales/simple_form.ja.yml | 11 +- config/locales/simple_form.kab.yml | 8 +- config/locales/simple_form.kk.yml | 2 - config/locales/simple_form.ko.yml | 33 +- config/locales/simple_form.ku.yml | 354 +- config/locales/simple_form.lv.yml | 227 + config/locales/simple_form.nl.yml | 52 +- config/locales/simple_form.nn.yml | 18 +- config/locales/simple_form.no.yml | 18 +- config/locales/simple_form.oc.yml | 4 - config/locales/simple_form.pl.yml | 11 +- config/locales/simple_form.pt-BR.yml | 79 +- config/locales/simple_form.pt-PT.yml | 41 +- config/locales/simple_form.ro.yml | 4 - config/locales/simple_form.ru.yml | 21 +- config/locales/simple_form.sc.yml | 4 - config/locales/simple_form.si.yml | 1 + config/locales/simple_form.sk.yml | 10 +- config/locales/simple_form.sl.yml | 57 +- config/locales/simple_form.sq.yml | 11 +- config/locales/simple_form.sr.yml | 9 +- config/locales/simple_form.sv.yml | 28 +- config/locales/simple_form.ta.yml | 1 - config/locales/simple_form.th.yml | 31 +- config/locales/simple_form.tr.yml | 23 +- config/locales/simple_form.tt.yml | 2 + config/locales/simple_form.uk.yml | 9 +- config/locales/simple_form.vi.yml | 59 +- config/locales/simple_form.zh-CN.yml | 27 +- config/locales/simple_form.zh-HK.yml | 4 - config/locales/simple_form.zh-TW.yml | 40 +- config/locales/sk.yml | 273 +- config/locales/sl.yml | 356 +- config/locales/sq.yml | 474 +- config/locales/sr-Latn.yml | 27 +- config/locales/sr.yml | 88 +- config/locales/sv.yml | 403 +- config/locales/ta.yml | 32 - config/locales/te.yml | 2 - config/locales/th.yml | 561 +- config/locales/tr.yml | 501 +- config/locales/tt.yml | 41 +- config/locales/uk.yml | 408 +- config/locales/vi.yml | 682 ++- config/locales/zgh.yml | 11 +- config/locales/zh-CN.yml | 592 +- config/locales/zh-HK.yml | 266 +- config/locales/zh-TW.yml | 747 ++- config/navigation.rb | 13 +- config/routes.rb | 123 +- config/settings.yml | 7 +- config/sidekiq.yml | 16 +- config/webpack/production.js | 2 +- config/webpack/shared.js | 1 + config/webpack/tests.js | 2 +- crowdin.yml | 6 - .../20170920032311_fix_reblogs_in_feeds.rb | 4 +- ...0180528141303_fix_accounts_unique_index.rb | 15 + ...08213548_reject_following_blocked_users.rb | 6 +- ...3_add_fixed_lowercase_index_to_accounts.rb | 2 +- ...dd_case_insensitive_btree_index_to_tags.rb | 2 +- .../20210609202149_create_login_activities.rb | 14 + db/migrate/20210616214526_create_user_ips.rb | 5 + ...1221010_add_skip_sign_in_token_to_users.rb | 5 + ...reate_account_statuses_cleanup_policies.rb | 20 + ...0210904215403_add_edited_at_to_statuses.rb | 5 + .../20210908220918_create_status_edits.rb | 13 + ...031031021_create_preview_card_providers.rb | 12 + ...112011713_add_language_to_preview_cards.rb | 7 + ...15032527_add_trendable_to_preview_cards.rb | 5 + ...23212714_add_link_type_to_preview_cards.rb | 5 + ...6_update_account_summaries_to_version_2.rb | 24 + .../20211231080958_add_category_to_reports.rb | 21 + ...5163928_remove_mentions_status_id_index.rb | 9 + ...25126_add_report_id_to_account_warnings.rb | 6 + ...20115125341_fix_account_warning_actions.rb | 21 + ...202951_add_deleted_at_index_on_statuses.rb | 7 + db/migrate/20220124141035_create_appeals.rb | 14 + ...0220202200743_add_trendable_to_accounts.rb | 7 + ...0220202200926_add_trendable_to_statuses.rb | 5 + ...19_add_overruled_at_to_account_warnings.rb | 5 + ...24010024_add_ips_to_email_domain_blocks.rb | 6 + ...add_last_used_at_to_oauth_access_tokens.rb | 6 + ...rdered_media_attachment_ids_to_statuses.rb | 5 + ...ed_media_attachment_ids_to_status_edits.rb | 8 + ...4195405_migrate_hide_network_preference.rb | 37 + ...307094650_fix_featured_tags_constraints.rb | 17 + .../20220309213005_fix_reblog_deleted_at.rb | 9 + .../20220316233212_update_kurdish_locales.rb | 17 + ...35_remove_current_sign_in_ip_from_users.rb | 12 + ...0808071221_clear_orphaned_account_notes.rb | 21 + ...1126000907_drop_account_identity_proofs.rb | 13 + ...213908_remove_action_taken_from_reports.rb | 9 + ...10_remove_index_users_on_remember_token.rb | 13 + ...18183123_remove_rememberable_from_users.rb | 8 + ...201015_remove_trust_level_from_accounts.rb | 9 + ...a_attachments_changed_from_status_edits.rb | 7 + ...3_optimize_null_index_conversations_uri.rb | 17 + ...l_index_statuses_in_reply_to_account_id.rb | 17 + ...mize_null_index_statuses_in_reply_to_id.rb | 17 + ...x_media_attachments_scheduled_status_id.rb | 17 + ..._null_index_media_attachments_shortcode.rb | 17 + ...e_null_index_users_reset_password_token.rb | 17 + ...l_index_users_created_by_application_id.rb | 17 + ...060706_optimize_null_index_statuses_uri.rb | 17 + ...null_index_accounts_moved_to_account_id.rb | 17 + ...index_oauth_access_tokens_refresh_token.rb | 17 + ...060750_optimize_null_index_accounts_url.rb | 17 + ...x_oauth_access_tokens_resource_owner_id.rb | 17 + ..._announcement_reactions_custom_emoji_id.rb | 17 + ...ll_index_appeals_approved_by_account_id.rb | 17 + ...ex_account_migrations_target_account_id.rb | 17 + ...ll_index_appeals_rejected_by_account_id.rb | 17 + ...mize_null_index_list_accounts_follow_id.rb | 17 + ..._web_push_subscriptions_access_token_id.rb | 17 + db/schema.rb | 189 +- db/views/account_summaries_v02.sql | 23 + db/views/user_ips_v01.sql | 26 + dist/mastodon-sidekiq.service | 11 +- dist/mastodon-streaming.service | 9 +- dist/mastodon-web.service | 11 +- dist/nginx.conf | 11 +- docker-compose.yml | 102 +- lib/action_dispatch/cookie_jar_extensions.rb | 25 - lib/cli.rb | 4 + lib/exceptions.rb | 1 + lib/mastodon/accounts_cli.rb | 15 +- lib/mastodon/canonical_email_blocks_cli.rb | 64 + lib/mastodon/domains_cli.rb | 13 +- lib/mastodon/email_domain_blocks_cli.rb | 33 +- lib/mastodon/emoji_cli.rb | 2 +- lib/mastodon/maintenance_cli.rb | 73 +- lib/mastodon/migration_helpers.rb | 31 +- lib/mastodon/search_cli.rb | 111 +- lib/mastodon/snowflake.rb | 12 +- lib/mastodon/statuses_cli.rb | 218 +- lib/mastodon/version.rb | 4 +- lib/paperclip/attachment_extensions.rb | 29 + .../media_type_spoof_detector_extensions.rb | 35 - lib/paperclip/schema_extensions.rb | 37 - lib/paperclip/transcoder.rb | 18 +- lib/paperclip/url_generator_extensions.rb | 10 - lib/paperclip/validation_extensions.rb | 58 - {app/lib => lib}/sidekiq_error_handler.rb | 8 +- lib/tasks/db.rake | 15 +- lib/tasks/emojis.rake | 2 +- lib/tasks/repo.rake | 5 +- lib/tasks/tests.rake | 92 +- package.json | 110 +- public/embed.js | 2 +- scalingo.json | 5 +- spec/controllers/accounts_controller_spec.rb | 11 +- .../collections_controller_spec.rb | 29 +- ...lowers_synchronizations_controller_spec.rb | 2 +- .../activitypub/outboxes_controller_spec.rb | 6 +- .../activitypub/replies_controller_spec.rb | 295 +- .../admin/accounts_controller_spec.rb | 50 +- .../admin/change_email_controller_spec.rb | 12 +- .../admin/confirmations_controller_spec.rb | 10 +- .../admin/disputes/appeals_controller_spec.rb | 53 + .../admin/domain_blocks_controller_spec.rb | 9 - .../email_domain_blocks_controller_spec.rb | 42 +- .../admin/instances_controller_spec.rb | 35 +- .../admin/report_notes_controller_spec.rb | 8 +- .../reported_statuses_controller_spec.rb | 59 - .../admin/reports_controller_spec.rb | 22 +- .../admin/resets_controller_spec.rb | 4 +- .../admin/statuses_controller_spec.rb | 70 +- .../controllers/admin/tags_controller_spec.rb | 12 - ..._factor_authentications_controller_spec.rb | 8 +- spec/controllers/api/base_controller_spec.rb | 2 +- .../controllers/api/proofs_controller_spec.rb | 93 - .../accounts/credentials_controller_spec.rb | 2 +- .../follower_accounts_controller_spec.rb | 6 +- .../following_accounts_controller_spec.rb | 6 +- .../api/v1/accounts/lists_controller_spec.rb | 2 +- .../api/v1/accounts/notes_controller_spec.rb | 4 +- .../api/v1/accounts/pins_controller_spec.rb | 4 +- .../accounts/relationships_controller_spec.rb | 6 +- .../api/v1/accounts/search_controller_spec.rb | 2 +- .../v1/accounts/statuses_controller_spec.rb | 37 +- .../api/v1/accounts_controller_spec.rb | 38 +- .../admin/account_actions_controller_spec.rb | 4 +- .../api/v1/admin/accounts_controller_spec.rb | 41 +- .../api/v1/admin/reports_controller_spec.rb | 2 +- .../api/v1/blocks_controller_spec.rb | 2 +- .../api/v1/conversations_controller_spec.rb | 4 +- .../api/v1/domain_blocks_controller_spec.rb | 2 +- .../emails/confirmations_controller_spec.rb | 64 + .../api/v1/follow_requests_controller_spec.rb | 4 +- .../api/v1/instances_controller_spec.rb | 2 +- .../api/v1/lists/accounts_controller_spec.rb | 2 +- .../api/v1/lists_controller_spec.rb | 2 +- .../api/v1/markers_controller_spec.rb | 2 +- .../api/v1/media_controller_spec.rb | 23 +- .../api/v1/mutes_controller_spec.rb | 2 +- .../api/v1/notifications_controller_spec.rb | 85 +- .../api/v1/polls/votes_controller_spec.rb | 2 +- .../api/v1/polls_controller_spec.rb | 2 +- .../api/v1/reports_controller_spec.rb | 56 +- .../v1/statuses/bookmarks_controller_spec.rb | 2 +- .../favourited_by_accounts_controller_spec.rb | 4 +- .../v1/statuses/favourites_controller_spec.rb | 2 +- .../v1/statuses/histories_controller_spec.rb | 29 + .../api/v1/statuses/mutes_controller_spec.rb | 2 +- .../api/v1/statuses/pins_controller_spec.rb | 2 +- .../reblogged_by_accounts_controller_spec.rb | 4 +- .../v1/statuses/reblogs_controller_spec.rb | 2 +- .../v1/statuses/sources_controller_spec.rb | 29 + .../api/v1/statuses_controller_spec.rb | 23 +- .../api/v1/timelines/home_controller_spec.rb | 2 +- .../api/v1/timelines/list_controller_spec.rb | 4 +- .../v1/timelines/public_controller_spec.rb | 2 +- .../api/v1/timelines/tag_controller_spec.rb | 2 +- .../tags_controller_spec.rb} | 7 +- .../api/v2/admin/accounts_controller_spec.rb | 73 + .../api/v2/search_controller_spec.rb | 2 +- .../application_controller_spec.rb | 26 +- .../auth/registrations_controller_spec.rb | 2 +- .../auth/sessions_controller_spec.rb | 160 +- .../authorize_interactions_controller_spec.rb | 3 +- .../account_controller_concern_spec.rb | 31 +- .../concerns/accountable_concern_spec.rb | 8 +- .../disputes/appeals_controller_spec.rb | 27 + .../disputes/strikes_controller_spec.rb | 30 + .../follower_accounts_controller_spec.rb | 4 +- .../following_accounts_controller_spec.rb | 4 +- spec/controllers/home_controller_spec.rb | 6 +- .../instance_actors_controller_spec.rb | 55 + spec/controllers/media_controller_spec.rb | 63 +- .../settings/deletes_controller_spec.rb | 4 +- ..._specs.rb => bookmarks_controller_spec.rb} | 11 +- .../identity_proofs_controller_spec.rb | 186 - .../settings/migrations_controller_spec.rb | 3 +- .../settings/profiles_controller_spec.rb | 14 +- .../otp_authentication_controller_spec.rb | 2 +- .../statuses_cleanup_controller_spec.rb | 27 + spec/controllers/statuses_controller_spec.rb | 6 +- .../keybase_proof_config_controller_spec.rb | 15 - spec/fabricators/account_fabricator.rb | 1 + .../account_identity_proof_fabricator.rb | 8 - ...ount_statuses_cleanup_policy_fabricator.rb | 3 + .../fabricators/account_warning_fabricator.rb | 7 +- spec/fabricators/appeal_fabricator.rb | 5 + spec/fabricators/login_activity_fabricator.rb | 8 + spec/fabricators/preview_card_fabricator.rb | 6 + spec/fabricators/report_fabricator.rb | 6 +- spec/fabricators/rule_fabricator.rb | 8 +- spec/fabricators/status_edit_fabricator.rb | 7 + spec/fabricators/user_fabricator.rb | 2 +- spec/fixtures/files/boop.ogg | Bin 0 -> 11379 bytes spec/helpers/application_helper_spec.rb | 2 +- spec/helpers/languages_helper_spec.rb | 23 + spec/helpers/settings_helper_spec.rb | 22 - spec/lib/activitypub/activity/accept_spec.rb | 5 + spec/lib/activitypub/activity/add_spec.rb | 42 +- .../lib/activitypub/activity/announce_spec.rb | 32 +- spec/lib/activitypub/activity/update_spec.rb | 110 +- spec/lib/emoji_formatter_spec.rb | 55 + spec/lib/formatter_spec.rb | 626 --- spec/lib/html_aware_formatter_spec.rb | 44 + spec/lib/language_detector_spec.rb | 134 - spec/lib/link_details_extractor_spec.rb | 159 + spec/lib/permalink_redirector_spec.rb | 42 + spec/lib/plain_text_formatter_spec.rb | 24 + .../proof_provider/keybase/verifier_spec.rb | 82 - spec/lib/rss/serializer_spec.rb | 7 - spec/lib/scope_transformer_spec.rb | 89 + spec/lib/status_reach_finder_spec.rb | 109 + spec/lib/suspicious_sign_in_detector_spec.rb | 57 + spec/lib/tag_manager_spec.rb | 26 +- spec/lib/text_formatter_spec.rb | 313 ++ spec/mailers/admin_mailer_spec.rb | 8 +- spec/mailers/notification_mailer_spec.rb | 2 +- spec/mailers/previews/admin_mailer_preview.rb | 10 + spec/mailers/previews/user_mailer_preview.rb | 13 +- spec/mailers/user_mailer_spec.rb | 11 + spec/models/account_filter_spec.rb | 42 +- spec/models/account_spec.rb | 2 +- .../account_statuses_cleanup_policy_spec.rb | 546 ++ spec/models/account_statuses_filter_spec.rb | 229 + spec/models/admin/account_action_spec.rb | 4 +- spec/models/appeal_spec.rb | 5 + .../concerns/account_interactions_spec.rb | 17 + spec/models/email_domain_block_spec.rb | 27 +- spec/models/form/status_batch_spec.rb | 52 - spec/models/login_activity_spec.rb | 5 + spec/models/media_attachment_spec.rb | 72 +- spec/models/public_feed_spec.rb | 10 +- spec/models/report_spec.rb | 31 +- spec/models/status_edit_spec.rb | 5 + spec/models/status_pin_spec.rb | 4 +- spec/models/status_spec.rb | 85 +- spec/models/tag_feed_spec.rb | 2 +- spec/models/trending_tags_spec.rb | 68 - spec/models/trends/statuses_spec.rb | 110 + spec/models/trends/tags_spec.rb | 67 + spec/models/user_spec.rb | 47 +- spec/models/web/push_subscription_spec.rb | 2 +- .../account_moderation_note_policy_spec.rb | 4 +- spec/policies/account_policy_spec.rb | 6 +- spec/policies/backup_policy_spec.rb | 2 +- spec/policies/custom_emoji_policy_spec.rb | 2 +- spec/policies/domain_block_policy_spec.rb | 2 +- .../email_domain_block_policy_spec.rb | 2 +- spec/policies/instance_policy_spec.rb | 4 +- spec/policies/invite_policy_spec.rb | 2 +- spec/policies/relay_policy_spec.rb | 2 +- spec/policies/report_note_policy_spec.rb | 2 +- spec/policies/report_policy_spec.rb | 2 +- spec/policies/settings_policy_spec.rb | 2 +- spec/policies/status_policy_spec.rb | 16 +- spec/policies/tag_policy_spec.rb | 2 +- spec/policies/user_policy_spec.rb | 6 +- .../familiar_followers_presenter_spec.rb | 58 + spec/rails_helper.rb | 4 +- .../account_statuses_cleanup_service_spec.rb | 101 + .../fetch_remote_status_service_spec.rb | 95 +- .../activitypub/fetch_replies_service_spec.rb | 18 +- .../process_account_service_spec.rb | 45 - .../process_collection_service_spec.rb | 140 + .../process_status_update_service_spec.rb | 468 ++ spec/services/after_block_service_spec.rb | 8 +- .../services/authorize_follow_service_spec.rb | 4 +- .../batched_remove_status_service_spec.rb | 2 +- spec/services/block_domain_service_spec.rb | 2 +- spec/services/block_service_spec.rb | 4 +- .../bootstrap_timeline_service_spec.rb | 33 + .../clear_domain_media_service_spec.rb | 2 +- spec/services/delete_account_service_spec.rb | 21 +- .../services/fan_out_on_write_service_spec.rb | 107 +- spec/services/favourite_service_spec.rb | 4 +- spec/services/fetch_link_card_service_spec.rb | 2 +- spec/services/follow_service_spec.rb | 20 +- spec/services/mute_service_spec.rb | 22 +- spec/services/notify_service_spec.rb | 46 +- .../services/process_mentions_service_spec.rb | 32 +- spec/services/purge_domain_service_spec.rb | 27 + spec/services/reblog_service_spec.rb | 12 + spec/services/reject_follow_service_spec.rb | 4 +- .../remove_from_follwers_service_spec.rb | 38 + spec/services/remove_status_service_spec.rb | 108 +- spec/services/report_service_spec.rb | 2 +- spec/services/suspend_account_service_spec.rb | 83 + spec/services/unblock_service_spec.rb | 4 +- spec/services/unfollow_service_spec.rb | 6 +- .../unsuspend_account_service_spec.rb | 133 + spec/services/update_account_service_spec.rb | 6 +- spec/services/update_status_service_spec.rb | 157 + spec/spec_helper.rb | 7 + .../model/model_have_error_on_field.rb | 2 +- spec/support/stories/profile_stories.rb | 2 +- .../blacklisted_email_validator_spec.rb | 2 +- spec/validators/email_mx_validator_spec.rb | 56 +- spec/validators/status_pin_validator_spec.rb | 8 +- .../unreserved_username_validator_spec.rb | 4 +- spec/views/about/show.html.haml_spec.rb | 1 + spec/views/statuses/show.html.haml_spec.rb | 44 +- .../distribute_poll_update_worker_spec.rb | 3 +- .../activitypub/distribution_worker_spec.rb | 12 +- .../move_distribution_worker_spec.rb | 6 +- .../status_update_distribution_worker_spec.rb | 44 + .../update_distribution_worker_spec.rb | 3 +- .../workers/admin/domain_purge_worker_spec.rb | 18 + spec/workers/feed_insert_worker_spec.rb | 2 +- spec/workers/move_worker_spec.rb | 19 +- ...ccounts_statuses_cleanup_scheduler_spec.rb | 127 + spec/workers/unfollow_follow_worker_spec.rb | 2 +- streaming/index.js | 215 +- yarn.lock | 4922 ++++++++--------- 1334 files changed, 62899 insertions(+), 26903 deletions(-) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json create mode 100644 .devcontainer/docker-compose.yml delete mode 100644 .github/CODEOWNERS create mode 100644 .github/ISSUE_TEMPLATE/1.bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/2.feature_request.yml delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.md delete mode 100644 .github/ISSUE_TEMPLATE/feature_request.md delete mode 100644 .github/ISSUE_TEMPLATE/support.md create mode 100644 .prettierignore create mode 100644 .prettierrc.js create mode 100644 FEDERATION.md create mode 100644 app/controllers/admin/disputes/appeals_controller.rb delete mode 100644 app/controllers/admin/pending_accounts_controller.rb delete mode 100644 app/controllers/admin/reported_statuses_controller.rb create mode 100644 app/controllers/admin/reports/actions_controller.rb create mode 100644 app/controllers/admin/trends/links/preview_card_providers_controller.rb create mode 100644 app/controllers/admin/trends/links_controller.rb create mode 100644 app/controllers/admin/trends/statuses_controller.rb create mode 100644 app/controllers/admin/trends/tags_controller.rb delete mode 100644 app/controllers/api/proofs_controller.rb create mode 100644 app/controllers/api/v1/accounts/familiar_followers_controller.rb create mode 100644 app/controllers/api/v1/admin/dimensions_controller.rb create mode 100644 app/controllers/api/v1/admin/measures_controller.rb create mode 100644 app/controllers/api/v1/admin/retention_controller.rb create mode 100644 app/controllers/api/v1/admin/trends/links_controller.rb create mode 100644 app/controllers/api/v1/admin/trends/statuses_controller.rb create mode 100644 app/controllers/api/v1/admin/trends/tags_controller.rb create mode 100644 app/controllers/api/v1/statuses/histories_controller.rb create mode 100644 app/controllers/api/v1/statuses/sources_controller.rb create mode 100644 app/controllers/api/v1/trends/links_controller.rb create mode 100644 app/controllers/api/v1/trends/statuses_controller.rb create mode 100644 app/controllers/api/v1/trends/tags_controller.rb delete mode 100644 app/controllers/api/v1/trends_controller.rb create mode 100644 app/controllers/api/v2/admin/accounts_controller.rb create mode 100644 app/controllers/concerns/access_token_tracking_concern.rb delete mode 100644 app/controllers/concerns/sign_in_token_authentication_concern.rb create mode 100644 app/controllers/disputes/appeals_controller.rb create mode 100644 app/controllers/disputes/base_controller.rb create mode 100644 app/controllers/disputes/strikes_controller.rb delete mode 100644 app/controllers/settings/identity_proofs_controller.rb create mode 100644 app/controllers/settings/login_activities_controller.rb create mode 100644 app/controllers/statuses_cleanup_controller.rb delete mode 100644 app/controllers/well_known/keybase_proof_config_controller.rb create mode 100644 app/helpers/admin/trends/statuses_helper.rb create mode 100644 app/helpers/formatting_helper.rb create mode 100644 app/helpers/languages_helper.rb create mode 100644 app/javascript/mastodon/actions/history.js delete mode 100644 app/javascript/mastodon/actions/identity_proofs.js create mode 100644 app/javascript/mastodon/actions/rules.js create mode 100644 app/javascript/mastodon/components/admin/Counter.js create mode 100644 app/javascript/mastodon/components/admin/Dimension.js create mode 100644 app/javascript/mastodon/components/admin/ReportReasonSelector.js create mode 100644 app/javascript/mastodon/components/admin/Retention.js create mode 100644 app/javascript/mastodon/components/admin/Trends.js create mode 100644 app/javascript/mastodon/components/check.js create mode 100644 app/javascript/mastodon/components/edited_timestamp/containers/dropdown_menu_container.js create mode 100644 app/javascript/mastodon/components/edited_timestamp/index.js create mode 100644 app/javascript/mastodon/components/inline_account.js create mode 100644 app/javascript/mastodon/components/media_attachments.js create mode 100644 app/javascript/mastodon/components/skeleton.js create mode 100644 app/javascript/mastodon/containers/admin_component.js create mode 100644 app/javascript/mastodon/features/explore/components/story.js create mode 100644 app/javascript/mastodon/features/explore/index.js create mode 100644 app/javascript/mastodon/features/explore/links.js create mode 100644 app/javascript/mastodon/features/explore/results.js create mode 100644 app/javascript/mastodon/features/explore/statuses.js create mode 100644 app/javascript/mastodon/features/explore/suggestions.js create mode 100644 app/javascript/mastodon/features/explore/tags.js create mode 100644 app/javascript/mastodon/features/report/category.js create mode 100644 app/javascript/mastodon/features/report/comment.js create mode 100644 app/javascript/mastodon/features/report/components/option.js create mode 100644 app/javascript/mastodon/features/report/rules.js create mode 100644 app/javascript/mastodon/features/report/statuses.js create mode 100644 app/javascript/mastodon/features/report/thanks.js delete mode 100644 app/javascript/mastodon/features/search/index.js create mode 100644 app/javascript/mastodon/features/ui/components/compare_history_modal.js create mode 100644 app/javascript/mastodon/locales/ckb.json create mode 100644 app/javascript/mastodon/locales/locale-data/sa.js create mode 100644 app/javascript/mastodon/locales/whitelist_ckb.json create mode 100644 app/javascript/mastodon/reducers/accounts_map.js create mode 100644 app/javascript/mastodon/reducers/history.js delete mode 100644 app/javascript/mastodon/reducers/identity_proofs.js delete mode 100644 app/javascript/mastodon/reducers/reports.js create mode 100644 app/javascript/mastodon/reducers/rules.js create mode 100644 app/lib/activitypub/forwarder.rb create mode 100644 app/lib/activitypub/parser/custom_emoji_parser.rb create mode 100644 app/lib/activitypub/parser/media_attachment_parser.rb create mode 100644 app/lib/activitypub/parser/poll_parser.rb create mode 100644 app/lib/activitypub/parser/status_parser.rb create mode 100644 app/lib/admin/metrics/dimension.rb create mode 100644 app/lib/admin/metrics/dimension/base_dimension.rb create mode 100644 app/lib/admin/metrics/dimension/instance_accounts_dimension.rb create mode 100644 app/lib/admin/metrics/dimension/instance_languages_dimension.rb create mode 100644 app/lib/admin/metrics/dimension/languages_dimension.rb create mode 100644 app/lib/admin/metrics/dimension/servers_dimension.rb create mode 100644 app/lib/admin/metrics/dimension/software_versions_dimension.rb create mode 100644 app/lib/admin/metrics/dimension/sources_dimension.rb create mode 100644 app/lib/admin/metrics/dimension/space_usage_dimension.rb create mode 100644 app/lib/admin/metrics/dimension/tag_languages_dimension.rb create mode 100644 app/lib/admin/metrics/dimension/tag_servers_dimension.rb create mode 100644 app/lib/admin/metrics/measure.rb create mode 100644 app/lib/admin/metrics/measure/active_users_measure.rb create mode 100644 app/lib/admin/metrics/measure/base_measure.rb create mode 100644 app/lib/admin/metrics/measure/instance_accounts_measure.rb create mode 100644 app/lib/admin/metrics/measure/instance_followers_measure.rb create mode 100644 app/lib/admin/metrics/measure/instance_follows_measure.rb create mode 100644 app/lib/admin/metrics/measure/instance_media_attachments_measure.rb create mode 100644 app/lib/admin/metrics/measure/instance_reports_measure.rb create mode 100644 app/lib/admin/metrics/measure/instance_statuses_measure.rb create mode 100644 app/lib/admin/metrics/measure/interactions_measure.rb create mode 100644 app/lib/admin/metrics/measure/new_users_measure.rb create mode 100644 app/lib/admin/metrics/measure/opened_reports_measure.rb create mode 100644 app/lib/admin/metrics/measure/resolved_reports_measure.rb create mode 100644 app/lib/admin/metrics/measure/tag_accounts_measure.rb create mode 100644 app/lib/admin/metrics/measure/tag_servers_measure.rb create mode 100644 app/lib/admin/metrics/measure/tag_uses_measure.rb create mode 100644 app/lib/admin/metrics/retention.rb create mode 100644 app/lib/admin/system_check/elasticsearch_check.rb create mode 100644 app/lib/emoji_formatter.rb delete mode 100644 app/lib/formatter.rb create mode 100644 app/lib/html_aware_formatter.rb delete mode 100644 app/lib/language_detector.rb create mode 100644 app/lib/link_details_extractor.rb create mode 100644 app/lib/permalink_redirector.rb create mode 100644 app/lib/plain_text_formatter.rb delete mode 100644 app/lib/proof_provider.rb delete mode 100644 app/lib/proof_provider/keybase.rb delete mode 100644 app/lib/proof_provider/keybase/badge.rb delete mode 100644 app/lib/proof_provider/keybase/config_serializer.rb delete mode 100644 app/lib/proof_provider/keybase/serializer.rb delete mode 100644 app/lib/proof_provider/keybase/verifier.rb delete mode 100644 app/lib/proof_provider/keybase/worker.rb create mode 100644 app/lib/scope_parser.rb create mode 100644 app/lib/scope_transformer.rb create mode 100644 app/lib/suspicious_sign_in_detector.rb create mode 100644 app/lib/text_formatter.rb delete mode 100644 app/models/account_identity_proof.rb create mode 100644 app/models/account_statuses_cleanup_policy.rb create mode 100644 app/models/account_statuses_filter.rb create mode 100644 app/models/admin/appeal_filter.rb create mode 100644 app/models/admin/status_batch_action.rb create mode 100644 app/models/admin/status_filter.rb create mode 100644 app/models/appeal.rb create mode 100644 app/models/concerns/status_snapshot_concern.rb create mode 100644 app/models/form/email_domain_block_batch.rb delete mode 100644 app/models/form/status_batch.rb create mode 100644 app/models/login_activity.rb create mode 100644 app/models/preview_card_provider.rb create mode 100644 app/models/status_edit.rb delete mode 100644 app/models/tag_filter.rb delete mode 100644 app/models/trending_tags.rb create mode 100644 app/models/trends.rb create mode 100644 app/models/trends/base.rb create mode 100644 app/models/trends/history.rb create mode 100644 app/models/trends/links.rb create mode 100644 app/models/trends/preview_card_batch.rb create mode 100644 app/models/trends/preview_card_filter.rb create mode 100644 app/models/trends/preview_card_provider_batch.rb create mode 100644 app/models/trends/preview_card_provider_filter.rb create mode 100644 app/models/trends/query.rb create mode 100644 app/models/trends/status_batch.rb create mode 100644 app/models/trends/status_filter.rb create mode 100644 app/models/trends/statuses.rb rename app/models/{form => trends}/tag_batch.rb (53%) create mode 100644 app/models/trends/tag_filter.rb create mode 100644 app/models/trends/tags.rb create mode 100644 app/models/user_ip.rb create mode 100644 app/policies/account_warning_policy.rb create mode 100644 app/policies/appeal_policy.rb create mode 100644 app/policies/preview_card_policy.rb create mode 100644 app/policies/preview_card_provider_policy.rb create mode 100644 app/presenters/familiar_followers_presenter.rb create mode 100644 app/serializers/rest/admin/cohort_serializer.rb create mode 100644 app/serializers/rest/admin/dimension_serializer.rb create mode 100644 app/serializers/rest/admin/ip_serializer.rb create mode 100644 app/serializers/rest/admin/measure_serializer.rb create mode 100644 app/serializers/rest/admin/tag_serializer.rb create mode 100644 app/serializers/rest/familiar_followers_serializer.rb delete mode 100644 app/serializers/rest/identity_proof_serializer.rb create mode 100644 app/serializers/rest/status_edit_serializer.rb create mode 100644 app/serializers/rest/status_source_serializer.rb create mode 100644 app/serializers/rest/trends/link_serializer.rb create mode 100644 app/services/account_statuses_cleanup_service.rb delete mode 100644 app/services/activitypub/process_poll_service.rb create mode 100644 app/services/activitypub/process_status_update_service.rb create mode 100644 app/services/appeal_service.rb create mode 100644 app/services/approve_appeal_service.rb create mode 100644 app/services/purge_domain_service.rb create mode 100644 app/services/remove_from_followers_service.rb create mode 100644 app/services/update_status_service.rb create mode 100644 app/views/about/_logged_in.html.haml delete mode 100644 app/views/admin/account_moderation_notes/_account_moderation_note.html.haml create mode 100644 app/views/admin/disputes/appeals/_appeal.html.haml create mode 100644 app/views/admin/disputes/appeals/index.html.haml delete mode 100644 app/views/admin/domain_blocks/show.html.haml delete mode 100644 app/views/admin/instances/_exhausted_deliveries_days.haml delete mode 100644 app/views/admin/pending_accounts/_account.html.haml delete mode 100644 app/views/admin/pending_accounts/index.html.haml delete mode 100644 app/views/admin/reports/_action_log.html.haml create mode 100644 app/views/admin/reports/_actions.html.haml delete mode 100644 app/views/admin/statuses/show.html.haml delete mode 100644 app/views/admin/tags/_tag.html.haml delete mode 100644 app/views/admin/tags/index.html.haml create mode 100644 app/views/admin/trends/links/_preview_card.html.haml create mode 100644 app/views/admin/trends/links/index.html.haml create mode 100644 app/views/admin/trends/links/preview_card_providers/_preview_card_provider.html.haml create mode 100644 app/views/admin/trends/links/preview_card_providers/index.html.haml create mode 100644 app/views/admin/trends/statuses/_status.html.haml create mode 100644 app/views/admin/trends/statuses/index.html.haml create mode 100644 app/views/admin/trends/tags/_tag.html.haml create mode 100644 app/views/admin/trends/tags/index.html.haml create mode 100644 app/views/admin_mailer/_new_trending_links.text.erb create mode 100644 app/views/admin_mailer/_new_trending_statuses.text.erb create mode 100644 app/views/admin_mailer/_new_trending_tags.text.erb create mode 100644 app/views/admin_mailer/new_appeal.text.erb delete mode 100644 app/views/admin_mailer/new_trending_tag.text.erb create mode 100644 app/views/admin_mailer/new_trends.text.erb create mode 100644 app/views/auth/registrations/_account_warning.html.haml delete mode 100644 app/views/auth/sessions/sign_in_token.html.haml create mode 100644 app/views/disputes/strikes/index.html.haml create mode 100644 app/views/disputes/strikes/show.html.haml delete mode 100644 app/views/settings/identity_proofs/_proof.html.haml delete mode 100644 app/views/settings/identity_proofs/index.html.haml delete mode 100644 app/views/settings/identity_proofs/new.html.haml create mode 100644 app/views/settings/login_activities/_login_activity.html.haml create mode 100644 app/views/settings/login_activities/index.html.haml create mode 100644 app/views/statuses_cleanup/show.html.haml create mode 100644 app/views/user_mailer/appeal_approved.html.haml create mode 100644 app/views/user_mailer/appeal_approved.text.erb create mode 100644 app/views/user_mailer/appeal_rejected.html.haml create mode 100644 app/views/user_mailer/appeal_rejected.text.erb rename app/views/user_mailer/{sign_in_token.html.haml => suspicious_sign_in.html.haml} (55%) rename app/views/user_mailer/{sign_in_token.text.erb => suspicious_sign_in.text.erb} (56%) delete mode 100644 app/workers/activitypub/reply_distribution_worker.rb create mode 100644 app/workers/activitypub/status_update_distribution_worker.rb create mode 100644 app/workers/admin/domain_purge_worker.rb create mode 100644 app/workers/remote_account_refresh_worker.rb create mode 100644 app/workers/scheduler/accounts_statuses_cleanup_scheduler.rb create mode 100644 app/workers/scheduler/email_domain_block_refresh_scheduler.rb rename app/workers/scheduler/{trending_tags_scheduler.rb => trends/refresh_scheduler.rb} (57%) create mode 100644 app/workers/scheduler/trends/review_notifications_scheduler.rb create mode 100644 config/initializers/pghero.rb create mode 100644 config/locales/activerecord.ckb.yml create mode 100644 config/locales/ckb.yml create mode 100644 config/locales/devise.ckb.yml create mode 100644 config/locales/doorkeeper.ckb.yml create mode 100644 config/locales/simple_form.ckb.yml create mode 100644 db/migrate/20210609202149_create_login_activities.rb create mode 100644 db/migrate/20210616214526_create_user_ips.rb create mode 100644 db/migrate/20210621221010_add_skip_sign_in_token_to_users.rb create mode 100644 db/migrate/20210722120340_create_account_statuses_cleanup_policies.rb create mode 100644 db/migrate/20210904215403_add_edited_at_to_statuses.rb create mode 100644 db/migrate/20210908220918_create_status_edits.rb create mode 100644 db/migrate/20211031031021_create_preview_card_providers.rb create mode 100644 db/migrate/20211112011713_add_language_to_preview_cards.rb create mode 100644 db/migrate/20211115032527_add_trendable_to_preview_cards.rb create mode 100644 db/migrate/20211123212714_add_link_type_to_preview_cards.rb create mode 100644 db/migrate/20211213040746_update_account_summaries_to_version_2.rb create mode 100644 db/migrate/20211231080958_add_category_to_reports.rb create mode 100644 db/migrate/20220105163928_remove_mentions_status_id_index.rb create mode 100644 db/migrate/20220115125126_add_report_id_to_account_warnings.rb create mode 100644 db/migrate/20220115125341_fix_account_warning_actions.rb create mode 100644 db/migrate/20220116202951_add_deleted_at_index_on_statuses.rb create mode 100644 db/migrate/20220124141035_create_appeals.rb create mode 100644 db/migrate/20220202200743_add_trendable_to_accounts.rb create mode 100644 db/migrate/20220202200926_add_trendable_to_statuses.rb create mode 100644 db/migrate/20220210153119_add_overruled_at_to_account_warnings.rb create mode 100644 db/migrate/20220224010024_add_ips_to_email_domain_blocks.rb create mode 100644 db/migrate/20220227041951_add_last_used_at_to_oauth_access_tokens.rb create mode 100644 db/migrate/20220302232632_add_ordered_media_attachment_ids_to_statuses.rb create mode 100644 db/migrate/20220303000827_add_ordered_media_attachment_ids_to_status_edits.rb create mode 100644 db/migrate/20220304195405_migrate_hide_network_preference.rb create mode 100644 db/migrate/20220307094650_fix_featured_tags_constraints.rb create mode 100644 db/migrate/20220309213005_fix_reblog_deleted_at.rb create mode 100644 db/migrate/20220316233212_update_kurdish_locales.rb create mode 100644 db/post_migrate/20210616214135_remove_current_sign_in_ip_from_users.rb create mode 100644 db/post_migrate/20210808071221_clear_orphaned_account_notes.rb create mode 100644 db/post_migrate/20211126000907_drop_account_identity_proofs.rb create mode 100644 db/post_migrate/20220109213908_remove_action_taken_from_reports.rb create mode 100644 db/post_migrate/20220118183010_remove_index_users_on_remember_token.rb create mode 100644 db/post_migrate/20220118183123_remove_rememberable_from_users.rb create mode 100644 db/post_migrate/20220202201015_remove_trust_level_from_accounts.rb create mode 100644 db/post_migrate/20220303203437_remove_media_attachments_changed_from_status_edits.rb create mode 100644 db/post_migrate/20220307083603_optimize_null_index_conversations_uri.rb create mode 100644 db/post_migrate/20220310060545_optimize_null_index_statuses_in_reply_to_account_id.rb create mode 100644 db/post_migrate/20220310060556_optimize_null_index_statuses_in_reply_to_id.rb create mode 100644 db/post_migrate/20220310060614_optimize_null_index_media_attachments_scheduled_status_id.rb create mode 100644 db/post_migrate/20220310060626_optimize_null_index_media_attachments_shortcode.rb create mode 100644 db/post_migrate/20220310060641_optimize_null_index_users_reset_password_token.rb create mode 100644 db/post_migrate/20220310060653_optimize_null_index_users_created_by_application_id.rb create mode 100644 db/post_migrate/20220310060706_optimize_null_index_statuses_uri.rb create mode 100644 db/post_migrate/20220310060722_optimize_null_index_accounts_moved_to_account_id.rb create mode 100644 db/post_migrate/20220310060740_optimize_null_index_oauth_access_tokens_refresh_token.rb create mode 100644 db/post_migrate/20220310060750_optimize_null_index_accounts_url.rb create mode 100644 db/post_migrate/20220310060809_optimize_null_index_oauth_access_tokens_resource_owner_id.rb create mode 100644 db/post_migrate/20220310060833_optimize_null_index_announcement_reactions_custom_emoji_id.rb create mode 100644 db/post_migrate/20220310060854_optimize_null_index_appeals_approved_by_account_id.rb create mode 100644 db/post_migrate/20220310060913_optimize_null_index_account_migrations_target_account_id.rb create mode 100644 db/post_migrate/20220310060926_optimize_null_index_appeals_rejected_by_account_id.rb create mode 100644 db/post_migrate/20220310060939_optimize_null_index_list_accounts_follow_id.rb create mode 100644 db/post_migrate/20220310060959_optimize_null_index_web_push_subscriptions_access_token_id.rb create mode 100644 db/views/account_summaries_v02.sql create mode 100644 db/views/user_ips_v01.sql delete mode 100644 lib/action_dispatch/cookie_jar_extensions.rb create mode 100644 lib/mastodon/canonical_email_blocks_cli.rb delete mode 100644 lib/paperclip/media_type_spoof_detector_extensions.rb delete mode 100644 lib/paperclip/schema_extensions.rb delete mode 100644 lib/paperclip/validation_extensions.rb rename {app/lib => lib}/sidekiq_error_handler.rb (64%) create mode 100644 spec/controllers/admin/disputes/appeals_controller_spec.rb delete mode 100644 spec/controllers/admin/reported_statuses_controller_spec.rb delete mode 100644 spec/controllers/api/proofs_controller_spec.rb create mode 100644 spec/controllers/api/v1/emails/confirmations_controller_spec.rb create mode 100644 spec/controllers/api/v1/statuses/histories_controller_spec.rb create mode 100644 spec/controllers/api/v1/statuses/sources_controller_spec.rb rename spec/controllers/api/v1/{trends_controller_spec.rb => trends/tags_controller_spec.rb} (57%) create mode 100644 spec/controllers/api/v2/admin/accounts_controller_spec.rb create mode 100644 spec/controllers/disputes/appeals_controller_spec.rb create mode 100644 spec/controllers/disputes/strikes_controller_spec.rb create mode 100644 spec/controllers/instance_actors_controller_spec.rb rename spec/controllers/settings/exports/{bookmarks_controller_specs.rb => bookmarks_controller_spec.rb} (54%) delete mode 100644 spec/controllers/settings/identity_proofs_controller_spec.rb create mode 100644 spec/controllers/statuses_cleanup_controller_spec.rb delete mode 100644 spec/controllers/well_known/keybase_proof_config_controller_spec.rb delete mode 100644 spec/fabricators/account_identity_proof_fabricator.rb create mode 100644 spec/fabricators/account_statuses_cleanup_policy_fabricator.rb create mode 100644 spec/fabricators/appeal_fabricator.rb create mode 100644 spec/fabricators/login_activity_fabricator.rb create mode 100644 spec/fabricators/preview_card_fabricator.rb create mode 100644 spec/fabricators/status_edit_fabricator.rb create mode 100644 spec/fixtures/files/boop.ogg create mode 100644 spec/helpers/languages_helper_spec.rb delete mode 100644 spec/helpers/settings_helper_spec.rb create mode 100644 spec/lib/emoji_formatter_spec.rb delete mode 100644 spec/lib/formatter_spec.rb create mode 100644 spec/lib/html_aware_formatter_spec.rb delete mode 100644 spec/lib/language_detector_spec.rb create mode 100644 spec/lib/link_details_extractor_spec.rb create mode 100644 spec/lib/permalink_redirector_spec.rb create mode 100644 spec/lib/plain_text_formatter_spec.rb delete mode 100644 spec/lib/proof_provider/keybase/verifier_spec.rb create mode 100644 spec/lib/scope_transformer_spec.rb create mode 100644 spec/lib/status_reach_finder_spec.rb create mode 100644 spec/lib/suspicious_sign_in_detector_spec.rb create mode 100644 spec/lib/text_formatter_spec.rb create mode 100644 spec/models/account_statuses_cleanup_policy_spec.rb create mode 100644 spec/models/account_statuses_filter_spec.rb create mode 100644 spec/models/appeal_spec.rb delete mode 100644 spec/models/form/status_batch_spec.rb create mode 100644 spec/models/login_activity_spec.rb create mode 100644 spec/models/status_edit_spec.rb delete mode 100644 spec/models/trending_tags_spec.rb create mode 100644 spec/models/trends/statuses_spec.rb create mode 100644 spec/models/trends/tags_spec.rb create mode 100644 spec/presenters/familiar_followers_presenter_spec.rb create mode 100644 spec/services/account_statuses_cleanup_service_spec.rb create mode 100644 spec/services/activitypub/process_status_update_service_spec.rb create mode 100644 spec/services/purge_domain_service_spec.rb create mode 100644 spec/services/remove_from_follwers_service_spec.rb create mode 100644 spec/services/suspend_account_service_spec.rb create mode 100644 spec/services/unsuspend_account_service_spec.rb create mode 100644 spec/services/update_status_service_spec.rb create mode 100644 spec/workers/activitypub/status_update_distribution_worker_spec.rb create mode 100644 spec/workers/admin/domain_purge_worker_spec.rb create mode 100644 spec/workers/scheduler/accounts_statuses_cleanup_scheduler_spec.rb diff --git a/.circleci/config.yml b/.circleci/config.yml index e1c43ff806ae87..b9228f996cf140 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,194 +1,155 @@ -version: 2 +version: 2.1 -aliases: - - &defaults +orbs: + ruby: circleci/ruby@1.4.1 + node: circleci/node@5.0.1 + +executors: + default: + parameters: + ruby-version: + type: string docker: - - image: circleci/ruby:2.7-buster-node - environment: &ruby_environment + - image: cimg/ruby:<< parameters.ruby-version >> + environment: BUNDLE_JOBS: 3 BUNDLE_RETRY: 3 - BUNDLE_APP_CONFIG: ./.bundle/ - BUNDLE_PATH: ./vendor/bundle/ + CONTINUOUS_INTEGRATION: true DB_HOST: localhost DB_USER: root - RAILS_ENV: test - ALLOW_NOPAM: true - CONTINUOUS_INTEGRATION: true DISABLE_SIMPLECOV: true - PAM_ENABLED: true - PAM_DEFAULT_SERVICE: pam_test - PAM_CONTROLLED_SERVICE: pam_test_controlled - working_directory: ~/projects/mastodon/ - - - &attach_workspace - attach_workspace: - at: ~/projects/ - - - &persist_to_workspace - persist_to_workspace: - root: ~/projects/ - paths: - - ./mastodon/ - - - &restore_ruby_dependencies - restore_cache: - keys: - - v3-ruby-dependencies-{{ checksum "/tmp/.ruby-version" }}-{{ checksum "Gemfile.lock" }} - - v3-ruby-dependencies-{{ checksum "/tmp/.ruby-version" }}- - - v3-ruby-dependencies- + RAILS_ENV: test + - image: cimg/postgres:14.0 + environment: + POSTGRES_USER: root + POSTGRES_HOST_AUTH_METHOD: trust + - image: cimg/redis:6.2 - - &install_steps +commands: + install-system-dependencies: steps: - - checkout - - *attach_workspace - - restore_cache: - keys: - - v2-node-dependencies-{{ checksum "yarn.lock" }} - - v2-node-dependencies- - run: - name: Install yarn dependencies - command: yarn install --frozen-lockfile - - save_cache: - key: v2-node-dependencies-{{ checksum "yarn.lock" }} - paths: - - ./node_modules/ - - *persist_to_workspace - - - &install_system_dependencies - run: - name: Install system dependencies - command: | - sudo apt-get update - sudo apt-get install -y libicu-dev libidn11-dev libprotobuf-dev protobuf-compiler - - - &install_ruby_dependencies - steps: - - *attach_workspace - - *install_system_dependencies - - run: - name: Set Ruby version - command: ruby -e 'puts RUBY_VERSION' | tee /tmp/.ruby-version - - *restore_ruby_dependencies - - run: - name: Set bundler settings - command: | - bundle config --local clean 'true' - bundle config --local deployment 'true' - bundle config --local with 'pam_authentication' - bundle config --local without 'development production' - bundle config --local frozen 'true' - bundle config --local path $BUNDLE_PATH - - run: - name: Install bundler dependencies - command: bundle check || (bundle install && bundle clean) - - save_cache: - key: v3-ruby-dependencies-{{ checksum "/tmp/.ruby-version" }}-{{ checksum "Gemfile.lock" }} - paths: - - ./.bundle/ - - ./vendor/bundle/ - - persist_to_workspace: - root: ~/projects/ - paths: - - ./mastodon/.bundle/ - - ./mastodon/vendor/bundle/ + name: Install system dependencies + command: | + sudo apt-get update + sudo apt-get install -y libicu-dev libidn11-dev + install-ruby-dependencies: + parameters: + ruby-version: + type: string + steps: + - run: + command: | + bundle config clean 'true' + bundle config frozen 'true' + bundle config without 'development production' + name: Set bundler settings + - ruby/install-deps: + bundler-version: '2.3.8' + key: ruby<< parameters.ruby-version >>-gems-v1 + wait-db: + steps: + - run: + command: dockerize -wait tcp://localhost:5432 -wait tcp://localhost:6379 -timeout 1m + name: Wait for PostgreSQL and Redis - - &test_steps - parallelism: 4 - steps: - - *attach_workspace - - *install_system_dependencies - - run: - name: Install FFMPEG - command: sudo apt-get install -y ffmpeg - - run: - name: Load database schema - command: ./bin/rails db:create db:schema:load db:seed - - run: - name: Run rspec in parallel - command: | - bundle exec rspec --profile 10 \ - --format RspecJunitFormatter \ - --out test_results/rspec.xml \ - --format progress \ - $(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings) - - store_test_results: - path: test_results jobs: - install: - <<: *defaults - <<: *install_steps - - install-ruby2.7: - <<: *defaults - <<: *install_ruby_dependencies - - install-ruby2.6: - <<: *defaults - docker: - - image: circleci/ruby:2.6-buster-node - environment: *ruby_environment - <<: *install_ruby_dependencies - - install-ruby3.0: - <<: *defaults - docker: - - image: circleci/ruby:3.0-buster-node - environment: *ruby_environment - <<: *install_ruby_dependencies - build: - <<: *defaults + docker: + - image: cimg/ruby:3.0-node + environment: + RAILS_ENV: test steps: - - *attach_workspace - - *install_system_dependencies + - checkout + - install-system-dependencies + - install-ruby-dependencies: + ruby-version: '3.0' + - node/install-packages: + cache-version: v1 + pkg-manager: yarn - run: - name: Precompile assets command: ./bin/rails assets:precompile + name: Precompile assets - persist_to_workspace: - root: ~/projects/ paths: - - ./mastodon/public/assets - - ./mastodon/public/packs-test/ + - public/assets + - public/packs-test + root: . + + test: + parameters: + ruby-version: + type: string + executor: + name: default + ruby-version: << parameters.ruby-version >> + environment: + ALLOW_NOPAM: true + PAM_ENABLED: true + PAM_DEFAULT_SERVICE: pam_test + PAM_CONTROLLED_SERVICE: pam_test_controlled + parallelism: 4 + steps: + - checkout + - install-system-dependencies + - run: + command: sudo apt-get install -y ffmpeg imagemagick libpam-dev + name: Install additional system dependencies + - run: + command: bundle config with 'pam_authentication' + name: Enable PAM authentication + - install-ruby-dependencies: + ruby-version: << parameters.ruby-version >> + - attach_workspace: + at: . + - wait-db + - run: + command: ./bin/rails db:create db:schema:load db:seed + name: Load database schema + - ruby/rspec-test test-migrations: - <<: *defaults - docker: - - image: circleci/ruby:2.7-buster-node - environment: *ruby_environment - - image: circleci/postgres:12.2 - environment: - POSTGRES_USER: root - POSTGRES_HOST_AUTH_METHOD: trust - - image: circleci/redis:5-alpine + executor: + name: default + ruby-version: '3.0' steps: - - *attach_workspace - - *install_system_dependencies + - checkout + - install-system-dependencies + - install-ruby-dependencies: + ruby-version: '3.0' + - wait-db - run: - name: Create database command: ./bin/rails db:create + name: Create database - run: command: ./bin/rails db:migrate VERSION=20171010025614 name: Run migrations up to v2.0.0 - run: command: ./bin/rails tests:migrations:populate_v2 name: Populate database with test data + - run: + command: ./bin/rails db:migrate VERSION=20180514140000 + name: Run migrations up to v2.4.0 + - run: + command: ./bin/rails tests:migrations:populate_v2_4 + name: Populate database with test data - run: command: ./bin/rails db:migrate name: Run all remaining migrations + - run: + command: ./bin/rails tests:migrations:check_database + name: Check migration result test-two-step-migrations: - <<: *defaults - docker: - - image: circleci/ruby:2.7-buster-node - environment: *ruby_environment - - image: circleci/postgres:12.2 - environment: - POSTGRES_USER: root - POSTGRES_HOST_AUTH_METHOD: trust - - image: circleci/redis:5-alpine + executor: + name: default + ruby-version: '3.0' steps: - - *attach_workspace - - *install_system_dependencies + - checkout + - install-system-dependencies + - install-ruby-dependencies: + ruby-version: '3.0' + - wait-db - run: command: ./bin/rails db:create name: Create database @@ -198,98 +159,51 @@ jobs: - run: command: ./bin/rails tests:migrations:populate_v2 name: Populate database with test data + - run: + command: ./bin/rails db:migrate VERSION=20180514140000 + name: Run pre-deployment migrations up to v2.4.0 + environment: + SKIP_POST_DEPLOYMENT_MIGRATIONS: true + - run: + command: ./bin/rails tests:migrations:populate_v2_4 + name: Populate database with test data - run: command: ./bin/rails db:migrate name: Run all pre-deployment migrations - evironment: + environment: SKIP_POST_DEPLOYMENT_MIGRATIONS: true - run: command: ./bin/rails db:migrate name: Run all post-deployment remaining migrations - - test-ruby2.7: - <<: *defaults - docker: - - image: circleci/ruby:2.7-buster-node - environment: *ruby_environment - - image: circleci/postgres:12.2 - environment: - POSTGRES_USER: root - POSTGRES_HOST_AUTH_METHOD: trust - - image: circleci/redis:5-alpine - <<: *test_steps - - test-ruby2.6: - <<: *defaults - docker: - - image: circleci/ruby:2.6-buster-node - environment: *ruby_environment - - image: circleci/postgres:12.2 - environment: - POSTGRES_USER: root - POSTGRES_HOST_AUTH_METHOD: trust - - image: circleci/redis:5-alpine - <<: *test_steps - - test-ruby3.0: - <<: *defaults - docker: - - image: circleci/ruby:3.0-buster-node - environment: *ruby_environment - - image: circleci/postgres:12.2 - environment: - POSTGRES_USER: root - POSTGRES_HOST_AUTH_METHOD: trust - - image: circleci/redis:5-alpine - <<: *test_steps - - test-webui: - <<: *defaults - docker: - - image: circleci/node:12-buster - steps: - - *attach_workspace - run: - name: Run jest - command: yarn test:jest + command: ./bin/rails tests:migrations:check_database + name: Check migration result workflows: version: 2 build-and-test: jobs: - - install - - install-ruby2.7: - requires: - - install - - install-ruby2.6: - requires: - - install - - install-ruby2.7 - - install-ruby3.0: + - build + - test: + matrix: + parameters: + ruby-version: + - '2.7' + - '3.0' + name: test-ruby<< matrix.ruby-version >> requires: - - install - - install-ruby2.7 - - build: - requires: - - install-ruby2.7 + - build - test-migrations: requires: - - install-ruby2.7 - - test-two-step-migrations: - requires: - - install-ruby2.7 - - test-ruby2.7: - requires: - - install-ruby2.7 - build - - test-ruby2.6: + - test-two-step-migrations: requires: - - install-ruby2.6 - build - - test-ruby3.0: + - node/run: + cache-version: v1 + name: test-webui + pkg-manager: yarn requires: - - install-ruby3.0 - build - - test-webui: - requires: - - install + version: lts + yarn-run: test:jest diff --git a/.codeclimate.yml b/.codeclimate.yml index 8701e5f3d202a0..ee9022cdaf839f 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -1,4 +1,4 @@ -version: "2" +version: '2' checks: argument-count: enabled: false @@ -34,5 +34,8 @@ plugins: sass-lint: enabled: true exclude_patterns: -- spec/ -- vendor/asset + - spec/ + - vendor/asset/ + + - app/javascript/mastodon/locales/**/*.json + - config/locales/**/*.yml diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 00000000000000..ac495e1c910766 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,24 @@ +# [Choice] Ruby version (use -bullseye variants on local arm64/Apple Silicon): 3, 3.1, 3.0, 2, 2.7, 2.6, 3-bullseye, 3.1-bullseye, 3.0-bullseye, 2-bullseye, 2.7-bullseye, 2.6-bullseye, 3-buster, 3.1-buster, 3.0-buster, 2-buster, 2.7-buster, 2.6-buster +ARG VARIANT=3.1-bullseye +FROM mcr.microsoft.com/vscode/devcontainers/ruby:${VARIANT} + +# Install Rails +# RUN gem install rails webdrivers + +# Default value to allow debug server to serve content over GitHub Codespace's port forwarding service +# The value is a comma-separated list of allowed domains +ENV RAILS_DEVELOPMENT_HOSTS=".githubpreview.dev" + +# [Choice] Node.js version: lts/*, 16, 14, 12, 10 +ARG NODE_VERSION="lts/*" +RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1" + +# [Optional] Uncomment this section to install additional OS packages. +RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ + && apt-get -y install --no-install-recommends libicu-dev libidn11-dev ffmpeg imagemagick libpam-dev + +# [Optional] Uncomment this line to install additional gems. +RUN gem install foreman + +# [Optional] Uncomment this line to install global node packages. +RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g yarn" 2>&1 diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000000000..628efc8ec5c21f --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,26 @@ +{ + "name": "Mastodon", + "dockerComposeFile": "docker-compose.yml", + "service": "app", + "workspaceFolder": "/workspaces/mastodon", + + // Set *default* container specific settings.json values on container create. + "settings": {}, + + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "EditorConfig.EditorConfig", + "dbaeumer.vscode-eslint", + "rebornix.Ruby" + ], + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // This can be used to network with other containers or the host. + "forwardPorts": [3000, 4000], + + // Use 'postCreateCommand' to run commands after the container is created. + "postCreateCommand": "bundle install --path vendor/bundle && yarn install && ./bin/rails db:setup", + + // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. + "remoteUser": "vscode" +} diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml new file mode 100644 index 00000000000000..538f6cccde8c20 --- /dev/null +++ b/.devcontainer/docker-compose.yml @@ -0,0 +1,83 @@ +version: '3' + +services: + app: + build: + context: . + dockerfile: Dockerfile + args: + # Update 'VARIANT' to pick a version of Ruby: 3, 3.1, 3.0, 2, 2.7, 2.6 + # Append -bullseye or -buster to pin to an OS version. + # Use -bullseye variants on local arm64/Apple Silicon. + VARIANT: '3.0-bullseye' + # Optional Node.js version to install + NODE_VERSION: '14' + volumes: + - ..:/workspaces/mastodon:cached + environment: + RAILS_ENV: development + NODE_ENV: development + + REDIS_HOST: redis + REDIS_PORT: '6379' + DB_HOST: db + DB_USER: postgres + DB_PASS: postgres + DB_PORT: '5432' + ES_ENABLED: 'true' + ES_HOST: es + ES_PORT: '9200' + # Overrides default command so things don't shut down after the process ends. + command: sleep infinity + networks: + - external_network + - internal_network + user: vscode + + db: + image: postgres:14-alpine + restart: unless-stopped + volumes: + - postgres-data:/var/lib/postgresql/data + environment: + POSTGRES_USER: postgres + POSTGRES_DB: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_HOST_AUTH_METHOD: trust + networks: + - internal_network + + redis: + image: redis:6-alpine + restart: unless-stopped + volumes: + - redis-data:/data + networks: + - internal_network + + es: + image: docker.elastic.co/elasticsearch/elasticsearch-oss:7.10.2 + restart: unless-stopped + environment: + ES_JAVA_OPTS: -Xms512m -Xmx512m + cluster.name: es-mastodon + discovery.type: single-node + bootstrap.memory_lock: 'true' + volumes: + - es-data:/usr/share/elasticsearch/data + networks: + - internal_network + ulimits: + memlock: + soft: -1 + hard: -1 + +volumes: + postgres-data: + redis-data: + es-data: + +networks: + external_network: + internal_network: + internal: true diff --git a/.dockerignore b/.dockerignore index 52397e75dcfa20..fedbea236df0fb 100644 --- a/.dockerignore +++ b/.dockerignore @@ -15,6 +15,7 @@ vendor/bundle *.swp *~ postgres +postgres14 redis elasticsearch chart diff --git a/.env.nanobox b/.env.nanobox index 5951777a2d5369..51dfdbd58fb95d 100644 --- a/.env.nanobox +++ b/.env.nanobox @@ -13,7 +13,7 @@ DB_PORT=5432 # DATABASE_URL=postgresql://$DATA_DB_USER:$DATA_DB_PASS@$DATA_DB_HOST/gonano -# Optional ElasticSearch configuration +# Optional Elasticsearch configuration ES_ENABLED=true ES_HOST=$DATA_ELASTIC_HOST ES_PORT=9200 @@ -202,10 +202,6 @@ SMTP_FROM_ADDRESS=notifications@${APP_NAME}.nanoapp.io # Name of the pam service used for checking if an user can register (pam "account" section is evaluated) (nil (disabled) by default) # PAM_CONTROLLED_SERVICE=rpam -# Global OAuth settings (optional) : -# If you have only one strategy, you may want to enable this -# OAUTH_REDIRECT_AT_SIGN_IN=true - # Optional CAS authentication (cf. omniauth-cas) : # CAS_ENABLED=true # CAS_URL=https://sso.myserver.com/ @@ -228,6 +224,7 @@ SMTP_FROM_ADDRESS=notifications@${APP_NAME}.nanoapp.io # CAS_LOCATION_KEY='location' # CAS_IMAGE_KEY='image' # CAS_PHONE_KEY='phone' +# CAS_SECURITY_ASSUME_EMAIL_IS_VERIFIED=true # Optional SAML authentication (cf. omniauth-saml) # SAML_ENABLED=true diff --git a/.env.production.sample b/.env.production.sample index ff6db83caa13ea..4fc58072f1d455 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -29,11 +29,14 @@ DB_NAME=mastodon_production DB_PASS= DB_PORT=5432 -# ElasticSearch (optional) +# Elasticsearch (optional) # ------------------------ ES_ENABLED=true ES_HOST=localhost ES_PORT=9200 +# Authentication for ES (optional) +ES_USER=elastic +ES_PASS=password # Secrets # ------- @@ -55,7 +58,7 @@ SMTP_SERVER=smtp.mailgun.org SMTP_PORT=587 SMTP_LOGIN= SMTP_PASSWORD= -SMTP_FROM_ADDRESS=notificatons@example.com +SMTP_FROM_ADDRESS=notifications@example.com # File storage (optional) # ----------------------- diff --git a/.eslintrc.js b/.eslintrc.js index 7dda01108218e5..2a882f59c6ca7c 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -79,6 +79,11 @@ module.exports = { 'no-irregular-whitespace': 'error', 'no-mixed-spaces-and-tabs': 'warn', 'no-nested-ternary': 'warn', + 'no-restricted-properties': [ + 'error', + { property: 'substring', message: 'Use .slice instead of .substring.' }, + { property: 'substr', message: 'Use .slice instead of .substr.' }, + ], 'no-trailing-spaces': 'warn', 'no-undef': 'error', 'no-unreachable': 'error', diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS deleted file mode 100644 index fd6f74689e7139..00000000000000 --- a/.github/CODEOWNERS +++ /dev/null @@ -1,32 +0,0 @@ -# CODEOWNERS for mastodon/mastodon - -# Translators -# To add translator, copy these lines, replace `fr` with appropriate language code and replace `@żelipapą` with user's GitHub nickname preceded by `@` sign or e-mail address. -# /app/javascript/mastodon/locales/fr.json @żelipapą -# /app/views/user_mailer/*.fr.html.erb @żelipapą -# /app/views/user_mailer/*.fr.text.erb @żelipapą -# /config/locales/*.fr.yml @żelipapą -# /config/locales/fr.yml @żelipapą - -# Polish -/app/javascript/mastodon/locales/pl.json @m4sk1n -/app/views/user_mailer/*.pl.html.erb @m4sk1n -/app/views/user_mailer/*.pl.text.erb @m4sk1n -/config/locales/*.pl.yml @m4sk1n -/config/locales/pl.yml @m4sk1n - -# French -/app/javascript/mastodon/locales/fr.json @aldarone -/app/javascript/mastodon/locales/whitelist_fr.json @aldarone -/app/views/user_mailer/*.fr.html.erb @aldarone -/app/views/user_mailer/*.fr.text.erb @aldarone -/config/locales/*.fr.yml @aldarone -/config/locales/fr.yml @aldarone - -# Dutch -/app/javascript/mastodon/locales/nl.json @jeroenpraat -/app/javascript/mastodon/locales/whitelist_nl.json @jeroenpraat -/app/views/user_mailer/*.nl.html.erb @jeroenpraat -/app/views/user_mailer/*.nl.text.erb @jeroenpraat -/config/locales/*.nl.yml @jeroenpraat -/config/locales/nl.yml @jeroenpraat diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 9526e17db73554..be750a5e41033a 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,3 +1,3 @@ patreon: mastodon open_collective: mastodon -github: [Gargron] +custom: https://sponsor.joinmastodon.org diff --git a/.github/ISSUE_TEMPLATE/1.bug_report.yml b/.github/ISSUE_TEMPLATE/1.bug_report.yml new file mode 100644 index 00000000000000..9cdf813f7bad03 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/1.bug_report.yml @@ -0,0 +1,42 @@ +name: Bug Report +description: If something isn't working as expected +labels: bug +body: + - type: markdown + attributes: + value: | + Make sure that you are submitting a new bug that was not previously reported or already fixed. + + Please use a concise and distinct title for the issue. + - type: textarea + attributes: + label: Steps to reproduce the problem + description: What were you trying to do? + value: | + 1. + 2. + 3. + ... + validations: + required: true + - type: input + attributes: + label: Expected behaviour + description: What should have happened? + validations: + required: true + - type: input + attributes: + label: Actual behaviour + description: What happened? + validations: + required: true + - type: textarea + attributes: + label: Specifications + description: | + What version or commit hash of Mastodon did you find this bug in? + + If a front-end issue, what browser and operating systems were you using? + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/2.feature_request.yml b/.github/ISSUE_TEMPLATE/2.feature_request.yml new file mode 100644 index 00000000000000..6626c2876f3910 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/2.feature_request.yml @@ -0,0 +1,22 @@ +name: Feature Request +description: I have a suggestion +labels: suggestion +body: + - type: markdown + attributes: + value: | + Please use a concise and distinct title for the issue. + + Consider: Could it be implemented as a 3rd party app using the REST API instead? + - type: textarea + attributes: + label: Pitch + description: Describe your idea for a feature. Make sure it has not already been suggested/implemented/turned down before. + validations: + required: true + - type: textarea + attributes: + label: Motivation + description: Why do you think this feature is needed? Who would benefit from it? + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 8ae4bb8825335b..00000000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -name: Bug Report -about: If something isn't working as expected -labels: bug ---- - - - - - -### Expected behaviour - - - -### Actual behaviour - - - -### Steps to reproduce the problem - - - -### Specifications - - - - diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 768868516e7442..7c0dbaf6702ea1 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,5 +1,8 @@ blank_issues_enabled: false contact_links: - - name: Mastodon Meta Discussion Board - url: https://discourse.joinmastodon.org/ + - name: GitHub Discussions + url: https://github.com/mastodon/mastodon/discussions about: Please ask and answer questions here. + - name: Bug Bounty Program + url: https://app.intigriti.com/programs/mastodon/mastodonio/detail + about: Please report security vulnerabilities here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index ff92c0316e031a..00000000000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -name: Feature Request -about: I have a suggestion ---- - - - - - -### Pitch - - - -### Motivation - - diff --git a/.github/ISSUE_TEMPLATE/support.md b/.github/ISSUE_TEMPLATE/support.md deleted file mode 100644 index 7fbc86ff14f699..00000000000000 --- a/.github/ISSUE_TEMPLATE/support.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: Support -about: Ask for help with your deployment - ---- - -We primarily use GitHub as a bug and feature tracker. For usage questions, troubleshooting of deployments and other individual technical assistance, please use one of the resources below: - -- https://discourse.joinmastodon.org -- #mastodon on irc.freenode.net diff --git a/.github/dependabot.yml b/.github/dependabot.yml index c4cd4887876a0d..bb1fccf7ca4ddd 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -6,7 +6,7 @@ version: 2 updates: - package-ecosystem: npm - directory: "/" + directory: '/' schedule: interval: weekly open-pull-requests-limit: 99 @@ -14,7 +14,7 @@ updates: - dependency-type: direct - package-ecosystem: bundler - directory: "/" + directory: '/' schedule: interval: weekly open-pull-requests-limit: 99 diff --git a/.github/workflows/build-image.yml b/.github/workflows/build-image.yml index 58f2813d31bbb6..75c7b54a65db27 100644 --- a/.github/workflows/build-image.yml +++ b/.github/workflows/build-image.yml @@ -3,19 +3,25 @@ on: workflow_dispatch: push: branches: - - "main" + - 'main' tags: - - "*" + - '*' + pull_request: + paths: + - .github/workflows/build-image.yml + - Dockerfile jobs: build-image: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + - uses: docker/setup-qemu-action@v1 - uses: docker/setup-buildx-action@v1 - uses: docker/login-action@v1 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} + if: github.event_name != 'pull_request' - uses: docker/metadata-action@v3 id: meta with: @@ -24,11 +30,13 @@ jobs: latest=auto tags: | type=edge,branch=main - type=semver,pattern={{ raw }} + type=match,pattern=v(.*),group=0 + type=ref,event=pr - uses: docker/build-push-action@v2 with: context: . - push: true + platforms: linux/amd64,linux/arm64 + push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.meta.outputs.tags }} cache-from: type=registry,ref=tootsuite/mastodon:latest cache-to: type=inline diff --git a/.github/workflows/check-i18n.yml b/.github/workflows/check-i18n.yml index 398e78b0f046ff..be38a096de6f15 100644 --- a/.github/workflows/check-i18n.yml +++ b/.github/workflows/check-i18n.yml @@ -2,9 +2,9 @@ name: Check i18n on: push: - branches: [ main ] + branches: [main] pull_request: - branches: [ main ] + branches: [main] env: RAILS_ENV: test @@ -14,21 +14,21 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Install system dependencies - run: | - sudo apt-get update - sudo apt-get install -y libicu-dev libidn11-dev libprotobuf-dev protobuf-compiler - - name: Set up Ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: '2.7' - bundler-cache: true - - name: Check locale file normalization - run: bundle exec i18n-tasks check-normalized - - name: Check for unused strings - run: bundle exec i18n-tasks unused -l en - - name: Check for wrong string interpolations - run: bundle exec i18n-tasks check-consistent-interpolations - - name: Check that all required locale files exist - run: bundle exec rake repo:check_locales_files + - uses: actions/checkout@v2 + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install -y libicu-dev libidn11-dev + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.0' + bundler-cache: true + - name: Check locale file normalization + run: bundle exec i18n-tasks check-normalized + - name: Check for unused strings + run: bundle exec i18n-tasks unused -l en + - name: Check for wrong string interpolations + run: bundle exec i18n-tasks check-consistent-interpolations + - name: Check that all required locale files exist + run: bundle exec rake repo:check_locales_files diff --git a/.gitignore b/.gitignore index a8f964102a4989..35dff170a1c14a 100644 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,7 @@ # Ignore postgres + redis + elasticsearch volume optionally created by docker-compose /postgres +/postgres14 /redis /elasticsearch diff --git a/.nvmrc b/.nvmrc index 48082f72f087ce..8351c19397f4fc 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -12 +14 diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000000000..de7673eb6ad11e --- /dev/null +++ b/.prettierignore @@ -0,0 +1,78 @@ +# See https://help.github.com/articles/ignoring-files for more about ignoring files. +# +# If you find yourself ignoring temporary files generated by your text editor +# or operating system, you probably want to add a global ignore instead: +# git config --global core.excludesfile '~/.gitignore_global' + +# Ignore bundler config and downloaded libraries. +/.bundle +/vendor/bundle + +# Ignore the default SQLite database. +/db/*.sqlite3 +/db/*.sqlite3-journal + +# Ignore all logfiles and tempfiles. +.eslintcache +/log/* +!/log/.keep +/tmp +/coverage +/public/system +/public/assets +/public/packs +/public/packs-test +.env +.env.production +.env.development +/node_modules/ +/build/ + +# Ignore Vagrant files +.vagrant/ + +# Ignore Capistrano customizations +/config/deploy/* + +# Ignore IDE files +.vscode/ +.idea/ + +# Ignore postgres + redis + elasticsearch volume optionally created by docker-compose +/postgres +/postgres14 +/redis +/elasticsearch + +# ignore Helm dependency charts +/chart/charts/*.tgz + +# Ignore Apple files +.DS_Store + +# Ignore vim files +*~ +*.swp + +# Ignore npm debug log +npm-debug.log + +# Ignore yarn log files +yarn-error.log +yarn-debug.log + +# Ignore vagrant log files +*-cloudimg-console.log + +# Ignore Docker option files +docker-compose.override.yml + +# Ignore Helm files +/chart + +# Ignore emoji map file +/app/javascript/mastodon/features/emoji/emoji_map.json + +# Ignore locale files +/app/javascript/mastodon/locales +/config/locales diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 00000000000000..1d70813d51a788 --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,3 @@ +module.exports = { + singleQuote: true +} diff --git a/.rubocop.yml b/.rubocop.yml index 2af0f59bbe0c74..a7693742645609 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -5,17 +5,17 @@ AllCops: TargetRubyVersion: 2.5 NewCops: disable Exclude: - - 'spec/**/*' - - 'db/**/*' - - 'app/views/**/*' - - 'config/**/*' - - 'bin/*' - - 'Rakefile' - - 'node_modules/**/*' - - 'Vagrantfile' - - 'vendor/**/*' - - 'lib/json_ld/*' - - 'lib/templates/**/*' + - 'spec/**/*' + - 'db/**/*' + - 'app/views/**/*' + - 'config/**/*' + - 'bin/*' + - 'Rakefile' + - 'node_modules/**/*' + - 'Vagrantfile' + - 'vendor/**/*' + - 'lib/json_ld/*' + - 'lib/templates/**/*' Bundler/OrderedGems: Enabled: false @@ -29,13 +29,17 @@ Layout/EmptyLineAfterMagicComment: Layout/EmptyLineAfterGuardClause: Enabled: false +Layout/EmptyLineBetweenDefs: + AllowAdjacentOneLineDefs: true + Layout/EmptyLinesAroundAttributeAccessor: Enabled: true +Layout/FirstHashElementIndentation: + EnforcedStyle: consistent + Layout/HashAlignment: Enabled: false - # EnforcedHashRocketStyle: table - # EnforcedColonStyle: table Layout/SpaceAroundMethodCallOperator: Enabled: true diff --git a/.ruby-version b/.ruby-version index 37c2961c2430f3..75a22a26ac4a92 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.7.2 +3.0.3 diff --git a/AUTHORS.md b/AUTHORS.md index 596451737e903c..9fc5f44f18fdf5 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -12,31 +12,32 @@ and provided thanks to the work of the following contributors: * [akihikodaki](https://github.com/akihikodaki) * [mjankowski](https://github.com/mjankowski) * [unarist](https://github.com/unarist) +* [noellabo](https://github.com/noellabo) * [abcang](https://github.com/abcang) * [yiskah](https://github.com/yiskah) -* [noellabo](https://github.com/noellabo) -* [nolanlawson](https://github.com/nolanlawson) * [mayaeh](https://github.com/mayaeh) +* [nolanlawson](https://github.com/nolanlawson) * [ysksn](https://github.com/ysksn) +* [tribela](https://github.com/tribela) * [sorin-davidoi](https://github.com/sorin-davidoi) * [lynlynlynx](https://github.com/lynlynlynx) * [m4sk1n](mailto:me@m4sk.in) * [Marcin Mikołajczak](mailto:me@m4sk.in) -* [tribela](https://github.com/tribela) * [renatolond](https://github.com/renatolond) +* [shleeable](https://github.com/shleeable) * [alpaca-tc](https://github.com/alpaca-tc) * [zunda](https://github.com/zunda) * [nclm](https://github.com/nclm) * [ineffyble](https://github.com/ineffyble) -* [shleeable](https://github.com/shleeable) +* [ariasuni](https://github.com/ariasuni) * [Masoud Abkenar](mailto:ampbox@gmail.com) * [blackle](https://github.com/blackle) * [Quent-in](https://github.com/Quent-in) * [JantsoP](https://github.com/JantsoP) -* [ariasuni](https://github.com/ariasuni) +* [Brawaru](https://github.com/Brawaru) * [nullkal](https://github.com/nullkal) * [yookoala](https://github.com/yookoala) -* [Brawaru](https://github.com/Brawaru) +* [dunn](https://github.com/dunn) * [Aditoo17](https://github.com/Aditoo17) * [Quenty31](https://github.com/Quenty31) * [marek-lach](https://github.com/marek-lach) @@ -46,10 +47,9 @@ and provided thanks to the work of the following contributors: * [eramdam](https://github.com/eramdam) * [Jeroen](mailto:jeroenpraat@users.noreply.github.com) * [takayamaki](https://github.com/takayamaki) -* [dunn](https://github.com/dunn) * [masarakki](https://github.com/masarakki) -* [ticky](https://github.com/ticky) * [trwnh](https://github.com/trwnh) +* [ticky](https://github.com/ticky) * [ThisIsMissEm](https://github.com/ThisIsMissEm) * [hinaloe](https://github.com/hinaloe) * [hcmiya](https://github.com/hcmiya) @@ -73,13 +73,13 @@ and provided thanks to the work of the following contributors: * [MaciekBaron](https://github.com/MaciekBaron) * [SerCom_KC](mailto:sercom-kc@users.noreply.github.com) * [Sylvhem](https://github.com/Sylvhem) +* [koyuawsmbrtn](https://github.com/koyuawsmbrtn) * [MitarashiDango](https://github.com/MitarashiDango) * [rinsuki](https://github.com/rinsuki) * [angristan](https://github.com/angristan) * [JeanGauthier](https://github.com/JeanGauthier) * [kschaper](https://github.com/kschaper) * [beatrix-bitrot](https://github.com/beatrix-bitrot) -* [koyuawsmbrtn](https://github.com/koyuawsmbrtn) * [BenLubar](https://github.com/BenLubar) * [mkljczk](https://github.com/mkljczk) * [adbelle](https://github.com/adbelle) @@ -89,11 +89,12 @@ and provided thanks to the work of the following contributors: * [yhirano55](https://github.com/yhirano55) * [devkral](https://github.com/devkral) * [camponez](https://github.com/camponez) -* [hugogameiro](https://github.com/hugogameiro) +* [Hugo Gameiro](mailto:hmgameiro@gmail.com) * [SerCom_KC](mailto:szescxz@gmail.com) * [aschmitz](https://github.com/aschmitz) * [mfmfuyu](https://github.com/mfmfuyu) * [kedamaDQ](https://github.com/kedamaDQ) +* [mashirozx](https://github.com/mashirozx) * [fpiesche](https://github.com/fpiesche) * [gandaro](https://github.com/gandaro) * [johnsudaar](https://github.com/johnsudaar) @@ -115,41 +116,41 @@ and provided thanks to the work of the following contributors: * [pfm-eyesightjp](https://github.com/pfm-eyesightjp) * [fakenine](https://github.com/fakenine) * [tsuwatch](https://github.com/tsuwatch) +* [progval](https://github.com/progval) * [victorhck](https://github.com/victorhck) -* [manuelviens](https://github.com/manuelviens) +* [manuelviens](mailto:manuelviens@users.noreply.github.com) * [tateisu](https://github.com/tateisu) * [fvh-P](https://github.com/fvh-P) +* [lfuelling](https://github.com/lfuelling) * [rtucker](https://github.com/rtucker) * [Anna e só](mailto:contraexemplos@gmail.com) * [dariusk](https://github.com/dariusk) * [kazu9su](https://github.com/kazu9su) -* [Komic](https://github.com/Komic) +* [komic](https://github.com/komic) * [lmorchard](https://github.com/lmorchard) * [diomed](https://github.com/diomed) * [Neetshin](mailto:neetshin@neetsh.in) * [rainyday](https://github.com/rainyday) * [tcitworld](https://github.com/tcitworld) -* [ProgVal](https://github.com/ProgVal) * [valentin2105](https://github.com/valentin2105) * [yuntan](https://github.com/yuntan) * [goofy-bz](mailto:goofy@babelzilla.org) * [kadiix](https://github.com/kadiix) * [kodacs](https://github.com/kodacs) * [marcin mikołajczak](mailto:me@m4sk.in) +* [berkes](https://github.com/berkes) * [KScl](https://github.com/KScl) * [sterdev](https://github.com/sterdev) -* [mashirozx](https://github.com/mashirozx) * [TheKinrar](https://github.com/TheKinrar) -* [007lva](https://github.com/007lva) * [AA4ch1](https://github.com/AA4ch1) * [alexgleason](https://github.com/alexgleason) -* [Bèr Kessels](mailto:ber@berk.es) * [cpytel](https://github.com/cpytel) * [northerner](https://github.com/northerner) +* [weex](https://github.com/weex) * [fhemberger](https://github.com/fhemberger) * [Gomasy](https://github.com/Gomasy) * [greysteil](https://github.com/greysteil) -* [hendotcat](https://github.com/hendotcat) +* [henrycatalinismith](https://github.com/henrycatalinismith) * [d6rkaiz](https://github.com/d6rkaiz) * [ladyisatis](https://github.com/ladyisatis) * [JMendyk](https://github.com/JMendyk) @@ -160,6 +161,8 @@ and provided thanks to the work of the following contributors: * [pawelngei](https://github.com/pawelngei) * [reneklacan](https://github.com/reneklacan) * [ekiru](https://github.com/ekiru) +* [Izorkin](https://github.com/Izorkin) +* [unasuke](https://github.com/unasuke) * [geta6](https://github.com/geta6) * [happycoloredbanana](https://github.com/happycoloredbanana) * [joenepraat](https://github.com/joenepraat) @@ -168,11 +171,11 @@ and provided thanks to the work of the following contributors: * [spla](mailto:sp@mastodont.cat) * [tomfhowe](https://github.com/tomfhowe) * [noraworld](https://github.com/noraworld) -* [lfuelling](https://github.com/lfuelling) * [aji-su](https://github.com/aji-su) +* [ikuradon](https://github.com/ikuradon) * [nzws](https://github.com/nzws) * [duxovni](https://github.com/duxovni) -* [smorimoto](https://github.com/smorimoto) +* [SuperSandro2000](https://github.com/SuperSandro2000) * [178inaba](https://github.com/178inaba) * [acid-chicken](https://github.com/acid-chicken) * [xgess](https://github.com/xgess) @@ -194,37 +197,37 @@ and provided thanks to the work of the following contributors: * [Dar13](https://github.com/Dar13) * [nevillepark](https://github.com/nevillepark) * [ornithocoder](https://github.com/ornithocoder) -* [pwoolcoc](https://github.com/pwoolcoc) +* [Paul Woolcock](mailto:paul@woolcock.us) * [pierreozoux](https://github.com/pierreozoux) * [qguv](https://github.com/qguv) * [Ram Lmn](mailto:ramlmn@users.noreply.github.com) +* [rgroothuijsen](https://github.com/rgroothuijsen) * [Sascha](mailto:sascha@serenitylabs.cloud) * [harukasan](https://github.com/harukasan) * [stamak](https://github.com/stamak) * [Technowix](https://github.com/Technowix) * [Zoeille](https://github.com/Zoeille) -* [Thor Harald Johansen](mailto:thj@thj.no) +* [Thorwegian](https://github.com/Thorwegian) * [0x70b1a5](https://github.com/0x70b1a5) * [gled-rs](https://github.com/gled-rs) * [Valentin_NC](mailto:valentin.ouvrard@nautile.sarl) * [R0ckweb](https://github.com/R0ckweb) -* [Izorkin](https://github.com/Izorkin) -* [unasuke](https://github.com/unasuke) * [caasi](https://github.com/caasi) +* [chandrn7](https://github.com/chandrn7) * [chr-1x](https://github.com/chr-1x) * [esetomo](https://github.com/esetomo) * [foxiehkins](https://github.com/foxiehkins) * [highemerly](https://github.com/highemerly) * [hoodie](mailto:hoodiekitten@outlook.com) * [kaiyou](https://github.com/kaiyou) +* [007lva](https://github.com/007lva) * [luzi82](https://github.com/luzi82) * [slice](https://github.com/slice) * [tmm576](https://github.com/tmm576) * [unsmell](mailto:unsmell@users.noreply.github.com) * [valerauko](https://github.com/valerauko) +* [Grawl](https://github.com/Grawl) * [chriswmartin](https://github.com/chriswmartin) -* [SuperSandro2000](https://github.com/SuperSandro2000) -* [ikuradon](https://github.com/ikuradon) * [AndreLewin](https://github.com/AndreLewin) * [0xflotus](https://github.com/0xflotus) * [redtachyons](https://github.com/redtachyons) @@ -234,18 +237,21 @@ and provided thanks to the work of the following contributors: * [Andrew](mailto:andrewlchronister@gmail.com) * [arielrodrigues](https://github.com/arielrodrigues) * [aurelien-reeves](https://github.com/aurelien-reeves) +* [BSKY](mailto:git@bsky.moe) * [elegaanz](https://github.com/elegaanz) * [estuans](https://github.com/estuans) * [dissolve](https://github.com/dissolve) * [PurpleBooth](https://github.com/PurpleBooth) * [bradurani](https://github.com/bradurani) * [wavebeem](https://github.com/wavebeem) -* [bruwalfas](https://github.com/bruwalfas) +* [thermosflasche](https://github.com/thermosflasche) * [LottieVixen](https://github.com/LottieVixen) * [wchristian](https://github.com/wchristian) * [muffinista](https://github.com/muffinista) * [cdutson](https://github.com/cdutson) * [farlistener](https://github.com/farlistener) +* [baby-gnu](https://github.com/baby-gnu) +* [danieljakots](https://github.com/danieljakots) * [divergentdave](https://github.com/divergentdave) * [DavidLibeau](https://github.com/DavidLibeau) * [dmerejkowsky](https://github.com/dmerejkowsky) @@ -256,8 +262,10 @@ and provided thanks to the work of the following contributors: * [unstabler](https://github.com/unstabler) * [potato4d](https://github.com/potato4d) * [h-izumi](https://github.com/h-izumi) +* [HolgerHuo](https://github.com/HolgerHuo) * [ErikXXon](https://github.com/ErikXXon) * [ian-kelling](https://github.com/ian-kelling) +* [eltociear](https://github.com/eltociear) * [immae](https://github.com/immae) * [J0WI](https://github.com/J0WI) * [vahnj](https://github.com/vahnj) @@ -283,10 +291,12 @@ and provided thanks to the work of the following contributors: * [Nathaniel Suchy](mailto:me@lunorian.is) * [ndarville](https://github.com/ndarville) * [NimaBoscarino](https://github.com/NimaBoscarino) +* [aquarla](https://github.com/aquarla) * [Abzol](https://github.com/Abzol) * [PatOnTheBack](https://github.com/PatOnTheBack) * [xPaw](https://github.com/xPaw) * [petzah](https://github.com/petzah) +* [PeterDaveHello](https://github.com/PeterDaveHello) * [ignisf](https://github.com/ignisf) * [lumenwrites](https://github.com/lumenwrites) * [remram44](https://github.com/remram44) @@ -310,25 +320,30 @@ and provided thanks to the work of the following contributors: * [yannicka](https://github.com/yannicka) * [ikasoumen](https://github.com/ikasoumen) * [zacanger](https://github.com/zacanger) +* [l2dy](https://github.com/l2dy) * [amazedkoumei](https://github.com/amazedkoumei) * [anon5r](https://github.com/anon5r) * [aus-social](https://github.com/aus-social) +* [bsky](mailto:git@bsky.moe) * [bsky](mailto:me@imbsky.net) -* [chandrn7](https://github.com/chandrn7) * [codl](https://github.com/codl) * [cpsdqs](https://github.com/cpsdqs) * [barzamin](https://github.com/barzamin) * [gol-cha](https://github.com/gol-cha) +* [gunchleoc](https://github.com/gunchleoc) * [fhalna](https://github.com/fhalna) * [haoyayoi](https://github.com/haoyayoi) * [ik11235](https://github.com/ik11235) * [kawax](https://github.com/kawax) * [shrft](https://github.com/shrft) +* [luigi](mailto:lvargas@rankia.com) +* [luzpaz](https://github.com/luzpaz) * [mbajur](https://github.com/mbajur) * [matsurai25](https://github.com/matsurai25) * [mecab](https://github.com/mecab) * [nicobz25](https://github.com/nicobz25) * [niwatori24](https://github.com/niwatori24) +* [noiob](https://github.com/noiob) * [oliverkeeble](https://github.com/oliverkeeble) * [partev](https://github.com/partev) * [pinfort](https://github.com/pinfort) @@ -341,7 +356,6 @@ and provided thanks to the work of the following contributors: * [vidarlee](https://github.com/vidarlee) * [vjackson725](https://github.com/vjackson725) * [wxcafe](https://github.com/wxcafe) -* [Grawl](https://github.com/Grawl) * [新都心(Neet Shin)](mailto:nucx@dio-vox.com) * [clarfonthey](https://github.com/clarfonthey) * [cygnan](https://github.com/cygnan) @@ -390,7 +404,6 @@ and provided thanks to the work of the following contributors: * [Brad Janke](mailto:brad.janke@gmail.com) * [bclindner](https://github.com/bclindner) * [brycied00d](https://github.com/brycied00d) -* [berkes](https://github.com/berkes) * [carlosjs23](https://github.com/carlosjs23) * [cgxxx](https://github.com/cgxxx) * [kibitan](https://github.com/kibitan) @@ -411,7 +424,6 @@ and provided thanks to the work of the following contributors: * [dalehenries](https://github.com/dalehenries) * [daprice](https://github.com/daprice) * [da2x](https://github.com/da2x) -* [danieljakots](https://github.com/danieljakots) * [codesections](https://github.com/codesections) * [dar5hak](https://github.com/dar5hak) * [kant](https://github.com/kant) @@ -420,7 +432,7 @@ and provided thanks to the work of the following contributors: * [caldwell](https://github.com/caldwell) * [davidcelis](https://github.com/davidcelis) * [davefp](https://github.com/davefp) -* [yipdw](https://github.com/yipdw) +* [hannahwhy](https://github.com/hannahwhy) * [debanshuk](https://github.com/debanshuk) * [mascali33](https://github.com/mascali33) * [DerekNonGeneric](https://github.com/DerekNonGeneric) @@ -445,20 +457,20 @@ and provided thanks to the work of the following contributors: * [GenbuHase](https://github.com/GenbuHase) * [nilsding](https://github.com/nilsding) * [hattori6789](https://github.com/hattori6789) -* [algernon](https://github.com/algernon) -* [Fastbyte01](https://github.com/Fastbyte01) -* [unrelentingtech](https://github.com/unrelentingtech) -* [gfaivre](https://github.com/gfaivre) -* [Fiaxhs](https://github.com/Fiaxhs) -* [rasjonell](https://github.com/rasjonell) -* [reedcourty](https://github.com/reedcourty) -* [anneau](https://github.com/anneau) -* [lanodan](https://github.com/lanodan) -* [Harmon758](https://github.com/Harmon758) -* [HellPie](https://github.com/HellPie) -* [Habu-Kagumba](https://github.com/Habu-Kagumba) -* [suzukaze](https://github.com/suzukaze) -* [Hiromi-Kai](https://github.com/Hiromi-Kai) +* [Gergely Nagy](mailto:algernon@users.noreply.github.com) +* [Giuseppe Pignataro](mailto:rogepix@gmail.com) +* [Greg V](mailto:greg@unrelenting.technology) +* [Guewen FAIVRE](mailto:guewen.faivre@elao.com) +* [Guillaume Lo Re](mailto:lowreg@gmail.com) +* [Gurgen Hayrapetyan](mailto:info.gurgen@gmail.com) +* [György Nádudvari](mailto:reedcourty@users.noreply.github.com) +* [HIKARU KOBORI](mailto:hk.uec.univ@gmail.com) +* [Haelwenn Monnier](mailto:lanodan@users.noreply.github.com) +* [Harmon](mailto:harmon758@gmail.com) +* [HellPie](mailto:hellpie@users.noreply.github.com) +* [Herbert Kagumba](mailto:habukagumba@gmail.com) +* [Hiroe Jun](mailto:jun.hiroe@gmail.com) +* [Hiromi Kai](mailto:pie05041008@gmail.com) * [Hisham Muhammad](mailto:hisham@gobolinux.org) * [Hugo "Slaynash" Flores](mailto:hugoflores@hotmail.fr) * [INAGAKI Hiroshi](mailto:musashino205@users.noreply.github.com) @@ -466,7 +478,6 @@ and provided thanks to the work of the following contributors: * [Ian McCowan](mailto:imccowan@gmail.com) * [Ian McDowell](mailto:me@ianmcdowell.net) * [Iijima Yasushi](mailto:kurage.cc@gmail.com) -* [Ikko Ashimine](mailto:eltociear@gmail.com) * [Ingo Blechschmidt](mailto:iblech@web.de) * [J Yeary](mailto:usbsnowcrash@users.noreply.github.com) * [Jack Michaud](mailto:jack-michaud@users.noreply.github.com) @@ -486,6 +497,7 @@ and provided thanks to the work of the following contributors: * [Jordan Guerder](mailto:jguerder@fr.pulseheberg.net) * [Joseph Mingrone](mailto:jehops@users.noreply.github.com) * [Josh Leeb-du Toit](mailto:mail@joshleeb.com) +* [Josh Soref](mailto:2119212+jsoref@users.noreply.github.com) * [Joshua Wood](mailto:josh@joshuawood.net) * [Julien](mailto:tiwy57@users.noreply.github.com) * [Julien Deswaef](mailto:juego@requiem4tv.com) @@ -502,6 +514,7 @@ and provided thanks to the work of the following contributors: * [Leo Wzukw](mailto:leowzukw@users.noreply.github.com) * [Leonie](mailto:62470640+bubblineyuri@users.noreply.github.com) * [Lex Alexander](mailto:l.alexander10@gmail.com) +* [LinAGKar](mailto:linus.kardell@gmail.com) * [Lorenz Diener](mailto:lorenzd@gmail.com) * [Luc Didry](mailto:ldidry@users.noreply.github.com) * [Lukas Burk](mailto:jemus42@users.noreply.github.com) @@ -534,6 +547,7 @@ and provided thanks to the work of the following contributors: * [Milton Mazzarri](mailto:milmazz@gmail.com) * [Minku Lee](mailto:premist@me.com) * [Minori Hiraoka](mailto:mnkai@users.noreply.github.com) +* [MitarashiDango](mailto:mitarashi_dango@mail.matcha-soft.com) * [Mitchell Hentges](mailto:mitch9654@gmail.com) * [Mostafa Ahangarha](mailto:ahangarha@users.noreply.github.com) * [Mouse Reeve](mailto:mousereeve@riseup.net) @@ -553,7 +567,6 @@ and provided thanks to the work of the following contributors: * [Norayr Chilingarian](mailto:norayr@arnet.am) * [Noëlle Anthony](mailto:noelle.d.anthony@gmail.com) * [N氏](mailto:uenok.htc@gmail.com) -* [OSAMU SATO](mailto:satosamu@gmail.com) * [Olivier Nicole](mailto:olivierthnicole@gmail.com) * [Oskari Noppa](mailto:noppa@users.noreply.github.com) * [Otakan](mailto:otakan951@gmail.com) @@ -566,6 +579,7 @@ and provided thanks to the work of the following contributors: * [Ratmir Karabut](mailto:rkarabut@sfmodern.ru) * [Reto Kromer](mailto:retokromer@users.noreply.github.com) * [Rob Watson](mailto:rfwatson@users.noreply.github.com) +* [Rohan Sharma](mailto:i.am.lone.survivor@protonmail.com) * [Ryan Freebern](mailto:ryan@freebern.org) * [Ryan Wade](mailto:ryan.wade@protonmail.com) * [Ryo Kajiwara](mailto:kfe-fecn6.prussian@s01.info) @@ -595,6 +609,8 @@ and provided thanks to the work of the following contributors: * [StefOfficiel](mailto:pichard.stephane@free.fr) * [Steven Tappert](mailto:admin@dark-it.net) * [Stéphane Guillou](mailto:stephane.guillou@member.fsf.org) +* [Su Yang](mailto:soulteary@users.noreply.github.com) +* [Sumak](mailto:44816995+kawsay@users.noreply.github.com) * [Svetlozar Todorov](mailto:svetlik@users.noreply.github.com) * [Sébastien Santoro](mailto:dereckson@espace-win.org) * [Tad Thorley](mailto:phaedryx@users.noreply.github.com) @@ -611,6 +627,7 @@ and provided thanks to the work of the following contributors: * [Tomonori Murakami](mailto:crosslife777@gmail.com) * [TomoyaShibata](mailto:wind.of.hometown@gmail.com) * [Treyssat-Vincent Nino](mailto:treyssatvincent@users.noreply.github.com) +* [Truong Nguyen](mailto:truongnmt.dev@gmail.com) * [Udo Kramer](mailto:optik@fluffel.io) * [Una](mailto:una@unascribed.com) * [Ushitora Anqou](mailto:ushitora@anqou.net) @@ -621,6 +638,7 @@ and provided thanks to the work of the following contributors: * [Wenceslao Páez Chávez](mailto:wcpaez@gmail.com) * [Wesley Ellis](mailto:tahnok@gmail.com) * [Wiktor](mailto:wiktor@metacode.biz) +* [Wonderfall](mailto:wonderfall@protonmail.com) * [Wonderfall](mailto:wonderfall@schrodinger.io) * [Y.Yamashiro](mailto:shukukei@mojizuri.jp) * [YDrogen](mailto:ydrogen45@gmail.com) @@ -634,11 +652,13 @@ and provided thanks to the work of the following contributors: * [Yeechan Lu](mailto:wz.bluesnow@gmail.com) * [Your Name](mailto:lorenzd@gmail.com) * [Yusuke Abe](mailto:moonset20@gmail.com) +* [Zach Neill](mailto:neillz@berea.edu) * [Zachary Spector](mailto:logicaldash@gmail.com) * [ZiiX](mailto:ziix@users.noreply.github.com) * [asria-jp](mailto:is@alicematic.com) * [ava](mailto:vladooku@users.noreply.github.com) * [benklop](mailto:benklop@gmail.com) +* [bobbyd0g](mailto:93697464+bobbyd0g@users.noreply.github.com) * [bsky](mailto:git@imbsky.net) * [caesarologia](mailto:lopesgemelli.1@gmail.com) * [cbayerlein](mailto:c.bayerlein@gmail.com) @@ -646,6 +666,7 @@ and provided thanks to the work of the following contributors: * [chrolis](mailto:chrolis@users.noreply.github.com) * [cormo](mailto:cormorant2+github@gmail.com) * [d0p1](mailto:dopi-sama@hush.com) +* [dogelover911](mailto:84288771+dogelover911@users.noreply.github.com) * [dxwc](mailto:dxwc@users.noreply.github.com) * [evilny0](mailto:evilny0@moomoocamp.net) * [febrezo](mailto:felixbrezo@gmail.com) @@ -656,6 +677,8 @@ and provided thanks to the work of the following contributors: * [guigeekz](mailto:pattusg@gmail.com) * [hakoai](mailto:hk--76@qa2.so-net.ne.jp) * [haosbvnker](mailto:github@chaosbunker.com) +* [heguro](mailto:65112898+heguro@users.noreply.github.com) +* [helloworldstack](mailto:66512512+helloworldstack@users.noreply.github.com) * [ichi_i](mailto:51489410+ichi-i@users.noreply.github.com) * [isati](mailto:phil@juchnowi.cz) * [jacob](mailto:jacobherringtondeveloper@gmail.com) @@ -671,7 +694,7 @@ and provided thanks to the work of the following contributors: * [kedama](mailto:32974885+kedamadq@users.noreply.github.com) * [kuro5hin](mailto:rusty@kuro5hin.org) * [leo60228](mailto:leo@60228.dev) -* [luzpaz](mailto:luzpaz@users.noreply.github.com) +* [matildepark](mailto:matilde.park@pm.me) * [maxypy](mailto:maxime@mpigou.fr) * [mhe](mailto:mail@marcus-herrmann.com) * [mike castleman](mailto:m@mlcastle.net) @@ -681,14 +704,15 @@ and provided thanks to the work of the following contributors: * [muan](mailto:muan@github.com) * [namelessGonbai](mailto:43787036+namelessgonbai@users.noreply.github.com) * [neetshin](mailto:neetshin@neetsh.in) -* [noiob](mailto:8197071+noiob@users.noreply.github.com) * [notozeki](mailto:notozeki@users.noreply.github.com) * [ntl-purism](mailto:57806346+ntl-purism@users.noreply.github.com) * [nzws](mailto:git-yuzu@svk.jp) +* [potpro](mailto:pptppctt@gmail.com) * [proxy](mailto:51172302+3n-k1@users.noreply.github.com) * [rch850](mailto:rich850@gmail.com) * [roikale](mailto:roikale@users.noreply.github.com) * [rysiekpl](mailto:rysiek@hackerspace.pl) +* [sasanquaneuf](mailto:sasanquaneuf@gmail.com) * [saturday06](mailto:dyob@lunaport.net) * [scd31](mailto:57571338+scd31@users.noreply.github.com) * [scriptjunkie](mailto:scriptjunkie@scriptjunkie.us) @@ -698,6 +722,7 @@ and provided thanks to the work of the following contributors: * [syui](mailto:syui@users.noreply.github.com) * [tackeyy](mailto:mailto.takita.yusuke@gmail.com) * [taicv](mailto:chuvantai@gmail.com) +* [tkr](mailto:account@kgtkr.net) * [tmyt](mailto:shigure@refy.net) * [trevDev()](mailto:trev@trevdev.ca) * [tsia](mailto:github@tsia.de) @@ -707,6 +732,7 @@ and provided thanks to the work of the following contributors: * [y-temp4](mailto:y.temp4@gmail.com) * [ymmtmdk](mailto:ymmtmdk@gmail.com) * [yoshipc](mailto:yoooo@yoshipc.net) +* [zunda](mailto:zundan@gmail.com) * [Özcan Zafer AYAN](mailto:ozcanzaferayan@gmail.com) * [ばん](mailto:detteiu0321@gmail.com) * [ふるふる](mailto:frfs@users.noreply.github.com) @@ -726,107 +752,126 @@ This document is provided for informational purposes only. Since it is only upda Following people have contributed to translation of Mastodon: - GunChleoc (*Scottish Gaelic*) -- ᛤᚤᛠᛥⴲ 👽 (KNTRO) (*Spanish, Argentina*) -- adrmzz (*Sardinian*) -- Hồ Nhất Duy (kantcer) (*Vietnamese*) -- Zoltán Gera (gerazo) (*Hungarian*) +- ケインツロ 👾 (KNTRO) (*Spanish, Argentina*) - Sveinn í Felli (sveinki) (*Icelandic*) -- qezwan (*Persian, Sorani (Kurdish)*) -- NCAA (*Danish*) -- Ramdziana F Y (rafeyu) (*Indonesian*) -- taicv (*Vietnamese*) -- ButterflyOfFire (BoFFire) (*French, Arabic, Kabyle*) +- Hồ Nhất Duy (honhatduy) (*Vietnamese*) +- Zoltán Gera (gerazo) (*Hungarian*) +- Kristaps_M (*Latvian*) +- NCAA (*French, Danish*) +- adrmzz (*Sardinian*) - Xosé M. (XoseM) (*Spanish, Galician*) -- Evert Prants (IcyDiamond) (*Estonian*) -- Besnik_b (*Albanian*) +- Ramdziana F Y (rafeyu) (*Indonesian*) +- Jeong Arm (Kjwon15) (*Spanish, Japanese, Korean, Esperanto*) - Emanuel Pina (emanuelpina) (*Portuguese*) -- Jeong Arm (Kjwon15) (*Japanese, Korean, Esperanto*) -- Alix Rossi (palindromordnilap) (*French, Esperanto, Corsican*) +- qezwan (*Persian, Sorani (Kurdish)*) +- Besnik_b (*Albanian*) +- ButterflyOfFire (BoFFire) (*French, Arabic, Kabyle*) - Thai Localization (thl10n) (*Thai*) +- Cyax (Cyaxares) (*Kurmanji (Kurdish)*) +- taicv (*Vietnamese*) - Daniele Lira Mereb (danilmereb) (*Portuguese, Brazilian*) +- spla (*Spanish, Catalan*) +- Evert Prants (IcyDiamond) (*Estonian*) +- koyu (*German*) +- Alix Rossi (palindromordnilap) (*French, Esperanto, Corsican*) - Joene (joenepraat) (*Dutch*) -- Kristijan Tkalec (lapor) (*Slovenian*) - stan ionut (stanionut12) (*Romanian*) -- spla (*Spanish, Catalan*) +- Mastodon 中文译者 (mastodon-linguist) (*Chinese Simplified*) +- Kristijan Tkalec (lapor) (*Slovenian*) +- Danial Behzadi (danialbehzadi) (*Persian*) +- Asier Iturralde Sarasola (aldatsa) (*Basque*) +- ManeraKai (*Arabic*) - мачко (ma4ko) (*Bulgarian*) -- 奈卜拉 (nebula_moe) (*Chinese Simplified*) +- Roboron (*Spanish*) +- Alessandro Levati (Oct326) (*Italian*) +- xatier (*Chinese Traditional, Chinese Traditional, Hong Kong*) +- Ondřej Pokorný (unextro) (*Czech*) +- Alexander Sorokin (Brawaru) (*French, Catalan, Danish, German, Greek, Hungarian, Armenian, Korean, Portuguese, Russian, Albanian, Swedish, Ukrainian, Vietnamese, Galician*) - kamee (*Armenian*) -- AJ-عجائب البرمجة (Esmail_Hazem) (*Arabic*) - Michal Stanke (mstanke) (*Czech*) -- Danial Behzadi (danialbehzadi) (*Persian*) - borys_sh (*Ukrainian*) -- Asier Iturralde Sarasola (aldatsa) (*Basque*) - Imre Kristoffer Eilertsen (DandelionSprout) (*Norwegian*) -- koyu (*German*) - yeft (*Chinese Traditional, Chinese Traditional, Hong Kong*) - Miguel Mayol (mitcoes) (*Spanish, Catalan*) -- Sasha Sorokin (Brawaru) (*French, Catalan, Danish, German, Greek, Hungarian, Armenian, Korean, Russian, Albanian, Swedish, Ukrainian, Vietnamese, Galician*) -- Roboron (*Spanish*) +- Marek Ľach (mareklach) (*Polish, Slovak*) +- Manuel Viens (manuelviens) (*French*) +- Kimmo Kujansuu (mrkujansuu) (*Finnish*) - Koala Yeung (yookoala) (*Chinese Traditional, Hong Kong*) -- Ondřej Pokorný (unextro) (*Czech*) +- enolp (*Asturian*) - Osoitz (*Basque*) - Peterandre (*Norwegian, Norwegian Nynorsk*) - tzium (*Sardinian*) -- Mélanie Chauvel (ariasuni) (*French, Arabic, Czech, German, Greek, Hungarian, Slovenian, Ukrainian, Chinese Simplified, Portuguese, Brazilian, Persian, Norwegian Nynorsk, Esperanto, Breton, Corsican, Sardinian, Kabyle*) -- Iváns (Ivans_translator) (*Galician*) - Maya Minatsuki (mayaeh) (*Japanese*) -- Manuel Viens (manuelviens) (*French*) -- Alessandro Levati (Oct326) (*Italian*) +- Mélanie Chauvel (ariasuni) (*French, Arabic, Czech, German, Greek, Hungarian, Slovenian, Ukrainian, Chinese Simplified, Portuguese, Brazilian, Persian, Norwegian Nynorsk, Esperanto, Breton, Corsican, Sardinian, Kabyle*) +- T. E. Kalaycı (tekrei) (*Turkish*) +- Takeçi (polygoat) (*French, Italian*) +- Galician Translator (Galician_translator) (*Galician*) - lamnatos (*Greek*) - Sean Young (assanges) (*Chinese Traditional*) - tolstoevsky (*Russian*) -- enolp (*Asturian*) +- Ihor Hordiichuk (ihor_ck) (*Ukrainian*) +- Ali Demirtaş (alidemirtas) (*Turkish*) - Jasmine Cam Andrever (gourmas) (*Cornish*) +- coxde (*Chinese Simplified*) - gagik_ (*Armenian*) - Masoud Abkenar (mabkenar) (*Persian*) - arshat (*Kazakh*) - Marcin Mikołajczak (mkljczkk) (*Czech, Polish, Russian*) -- Marek Ľach (mareklach) (*Polish, Slovak*) -- Ali Demirtaş (alidemirtas) (*Turkish*) +- Jeff Huang (s8321414) (*Chinese Traditional*) - Blak Ouille (BlakOuille16) (*French*) +- e (diveedd) (*Kurmanji (Kurdish)*) - Em St Cenydd (cancennau) (*Welsh*) - Diluns (*Occitan*) -- Muha Aliss (muhaaliss) (*Turkish*) +- Nurul Azeera Hidayah @ Muhammad Nur Hidayat Yasuyoshi (MNH48.moe) (mnh48) (*Malay*) +- Tagomago (tagomago) (*French, Spanish*) - Jurica (ahjk) (*Croatian*) - Aditoo17 (*Czech*) +- Tigran (tigransimonyan) (*Armenian*) - vishnuvaratharajan (*Tamil*) - pulmonarycosignerkindness (*Swedish*) +- calypsoopenmail (*French*) - cybergene (cyber-gene) (*Japanese*) -- Takeçi (polygoat) (*French, Italian*) -- xatier (*Chinese Traditional*) -- Ihor Hordiichuk (ihor_ck) (*Ukrainian*) +- Bran_Ruz (*Breton*) +- Gearguy (*Finnish*) +- GiorgioHerbie (*Italian*) +- Balázs Meskó (mesko.balazs) (*Czech, Hungarian*) +- Martin (miles) (*Slovenian*) - regulartranslator (*Portuguese, Brazilian*) +- Saederup92 (*Danish*) - ozzii (*French, Serbian (Cyrillic)*) - Irfan (Irfan_Radz) (*Malay*) -- Saederup92 (*Danish*) -- Akarshan Biswas (biswasab) (*Bengali, Sanskrit*) - Yi-Jyun Pan (pan93412) (*Chinese Traditional*) +- ClearlyClaire (*French, Icelandic*) +- Akarshan Biswas (biswasab) (*Bengali, Sanskrit*) +- Kristoffer Grundström (Umeaboy) (*Swedish*) - Rafael H L Moretti (Moretti) (*Portuguese, Brazilian*) - d5Ziif3K (*Ukrainian*) -- GiorgioHerbie (*Italian*) +- හෙළබස (HelaBasa) (*Sinhala*) +- xpil (*Polish*) +- Rojdayek (*Kurmanji (Kurdish)*) - christalleras (*Norwegian Nynorsk*) +- Allen Zhong (AstroProfundis) (*Chinese Simplified*) - Taloran (*Norwegian Nynorsk*) -- ThibG (*French, Icelandic*) +- Sokratis Alichanidis (alichani) (*Greek*) +- Catalina (catalina.st) (*Romanian*) - otrapersona (*Spanish, Spanish, Mexico*) -- Store (HelaBasa) (*Sinhala*) +- Ryo (DrRyo) (*Korean*) - Mauzi (*German, Swedish*) - atarashiako (*Chinese Simplified*) -- 101010 (101010pl) (*Polish*) - erictapen (*German*) -- Tagomago (tagomago) (*French, Spanish*) +- 101010 (101010pl) (*Polish*) - Jaz-Michael King (jazmichaelking) (*Welsh*) -- coxde (*Chinese Simplified*) -- T. E. Kalaycı (tekrei) (*Turkish*) +- axi (*Finnish*) - silkevicious (*Italian*) - Floxu (fredrikdim1) (*Norwegian Nynorsk*) -- Ryo (DrRyo) (*Korean*) +- NadieAishi (*Spanish, Spanish, Mexico*) - Bertil Hedkvist (Berrahed) (*Swedish*) - William(ѕ)ⁿ (wmlgr) (*Spanish*) +- Eshagh (eshagh79) (*Persian*) +- LNDDYL (*Chinese Traditional*) - norayr (*Armenian*) - Satnam S Virdi (pika10singh) (*Punjabi*) - Tiago Epifânio (tfve) (*Portuguese*) -- Balázs Meskó (mesko.balazs) (*Hungarian*) -- Sokratis Alichanidis (alichani) (*Greek*) - Mentor Gashi (mentorgashi.com) (*Albanian*) - carolinagiorno (*Portuguese, Brazilian*) - Hayk Khachatryan (brutusromanus123) (*Armenian*) @@ -834,394 +879,472 @@ Following people have contributed to translation of Mastodon: - Bharat Kumar (Marwari) (*Hindi*) - Austra Muizniece (aus_m) (*Latvian*) - ThonyVezbe (*Breton*) +- Just Spanish (7_7) (*Spanish, Mexico*) - v4vachan (*Malayalam*) +- bilfri (*Danish*) - dkdarshan760 (*Sanskrit*) -- tykayn (*French*) -- axi (*Finnish*) -- Selyan Slimane AMIRI (SelyanKab) (*Kabyle*) - Timur Seber (seber) (*Tatar*) +- Slimane Selyan AMIRI (SelyanKab) (*Kabyle*) +- VaiTon (*Italian*) +- Vik (ViktorOn) (*Danish, Russian*) +- tykayn (*French*) +- GCardo (*Portuguese, Brazilian*) - taoxvx (*Danish*) - Hrach Mkrtchyan (mhrach87) (*Armenian*) - sabri (thetomatoisavegetable) (*Spanish, Spanish, Argentina*) - Dewi (Unkorneg) (*French, Breton*) - CoelacanthusHex (*Chinese Simplified*) -- syncopams (*Chinese Simplified, Chinese Traditional, Chinese Traditional, Hong Kong*) - Rhys Harrison (rhedders) (*Esperanto*) -- Hakim Oubouali (zenata1) (*Standard Moroccan Tamazight*) +- syncopams (*Chinese Simplified, Chinese Traditional, Chinese Traditional, Hong Kong*) - SteinarK (*Norwegian Nynorsk*) +- Maxine B. Vågnes (vagnes) (*Norwegian, Norwegian Nynorsk*) +- Hakim Oubouali (zenata1) (*Standard Moroccan Tamazight*) +- ahangarha (*Persian*) - Lalo Tafolla (lalotafo) (*Spanish, Spanish, Mexico*) -- Mathias B. Vagnes (vagnes) (*Norwegian*) - dashersyed (*Urdu (Pakistan)*) -- Acolyte (666noob404) (*Ukrainian*) - Conight Wang (xfddwhh) (*Chinese Simplified*) - liffon (*Swedish*) - Damjan Dimitrioski (gnud) (*Macedonian*) +- Rikard Linde (rikardlinde) (*Swedish*) +- rondnunes (*Portuguese, Brazilian*) +- strubbl (*German*) - PPNplus (*Thai*) +- Frontier Translation Ltd. (frontier-translation) (*Chinese Simplified*) - shioko (*Chinese Simplified*) +- Kahina Mess (K_hina) (*Kabyle*) - ZiriSut (*Kabyle*) -- Evgeny Petrov (kondra007) (*Russian*) +- Groosha (groosha) (*Russian*) +- Hexandcube (hexandcube) (*Polish*) - Gwenn (Belvar) (*Breton*) +- 游荡 (MamaShip) (*Chinese Simplified*) - StanleyFrew (*French*) +- mynameismonkey (*Welsh*) +- Edward Navarro (EdwardNavarro) (*Spanish*) - Nikita Epifanov (Nikets) (*Russian*) - jaranta (*Finnish*) - Slobodan Simić (Слободан Симић) (slsimic) (*Serbian (Cyrillic)*) +- retiolus (*Catalan*) +- iVampireSP (*Chinese Simplified, Chinese Traditional*) - Felicia Jongleur (midsommar) (*Swedish*) - Denys (dector) (*Ukrainian*) -- iVampireSP (*Chinese Simplified, Chinese Traditional*) -- Pukima (pukimaaa) (*German*) -- 游荡 (MamaShip) (*Chinese Simplified*) +- Mo_der Steven (SakuraPuare) (*Chinese Simplified*) - Vanege (*Esperanto*) -- Rikard Linde (rikardlinde) (*Swedish*) - Jess Rafn (therealyez) (*Danish*) -- strubbl (*German*) - Stasiek Michalski (hellcp) (*Polish*) - dxwc (*Bengali*) -- jmontane (*Catalan*) +- Filbert Salim (gamesbert6) (*Indonesian*) - Liboide (*Spanish*) -- Hexandcube (hexandcube) (*Polish*) +- jmontane (*Catalan*) - Chris Kay (chriskarasoulis) (*Greek*) - Johan Schiff (schyffel) (*Swedish*) +- Rex_sa (rex07) (*Arabic*) - Arunmozhi (tecoholic) (*Tamil*) - zer0-x (ZER0-X) (*Arabic*) - kat (katktv) (*Russian, Ukrainian*) - Lauren Liberda (selfisekai) (*Polish*) -- mynameismonkey (*Welsh*) - oti4500 (*Hungarian, Ukrainian*) +- Delta (Delta-Time) (*Japanese*) +- Michael Zeevi (maze88) (*Hebrew*) +- SarfarazAhmed (*Urdu (Pakistan)*) - Mats Gunnar Ahlqvist (goqbi) (*Swedish*) - diazepan (*Spanish, Spanish, Argentina*) - marzuquccen (*Kabyle*) +- atriix (*Swedish*) - VictorCorreia (victorcorreia1984) (*Afrikaans*) -- Tigran (tigransimonyan) (*Armenian*) +- Remito (remitocat) (*Japanese*) +- AlexKoala (alexkoala) (*Korean*) - Juan José Salvador Piedra (JuanjoSalvador) (*Spanish*) - BurekzFinezt (*Serbian (Cyrillic)*) +- 森の子リスのミーコの大冒険 (Phroneris) (*Japanese*) +- asnomgtu (*Hungarian*) - SHeija (*Finnish*) -- Gearguy (*Finnish*) -- atriix (*Swedish*) +- Врабац (Slovorad) (*Serbian (Cyrillic)*) +- Dženan (Dzenan) (*Swedish*) - Jack R (isaac.97_WT) (*Spanish*) - antonyho (*Chinese Traditional, Hong Kong*) -- asnomgtu (*Hungarian*) -- ahangarha (*Persian*) +- FreddyG (*Esperanto*) - andruhov (*Russian, Ukrainian*) - phena109 (*Chinese Traditional, Hong Kong*) - Aryamik Sharma (Aryamik) (*Swedish, Hindi*) - Unmual (*Spanish*) -- 森の子リスのミーコの大冒険 (Phroneris) (*Japanese*) +- Adrián Graña (alaris83) (*Spanish*) +- cruz2020 (*Portuguese*) +- vpei (*Chinese Simplified*) - るいーね (ruine) (*Japanese*) - Sam Tux (imahbub) (*Bengali*) -- Kristoffer Grundström (Umeaboy) (*Swedish*) - igordrozniak (*Polish*) +- Michał Sidor (michcioperz) (*Polish*) - Isaac Huang (caasih) (*Chinese Traditional*) - AW Unad (awcodify) (*Indonesian*) -- Allen Zhong (AstroProfundis) (*Chinese Simplified*) +- 1Alino (*Slovak*) - Cutls (cutls) (*Japanese*) +- Goudarz Jafari (Goudarz) (*Persian*) +- Parodper (*Galician*) +- 1 (Ipsumry) (*Spanish*) - Falling Snowdin (tghgg) (*Vietnamese*) -- Ray (Ipsumry) (*Spanish*) -- Gianfranco Fronteddu (gianfro.gianfro) (*Sardinian*) - Rasmus Lindroth (RasmusLindroth) (*Swedish*) +- Gianfranco Fronteddu (gianfro.gianfro) (*Sardinian*) - Andrea Lo Iacono (niels0n) (*Italian*) -- Parodper (*Galician*) - fucsia (*Italian*) -- NadieAishi (*Spanish, Spanish, Mexico*) +- Vedran Serbu (SerenoXGen) (*Croatian*) - Kinshuk Sunil (kinshuksunil) (*Hindi*) - Ullas Joseph (ullasjoseph) (*Malayalam*) -- Goudarz Jafari (Goudarz) (*Persian*) +- al_._ (*German, Russian*) +- Matthías Páll Gissurarson (icetritlo) (*Icelandic*) +- Percy (kecrily) (*Chinese Simplified*) - Yu-Pai Liu (tedliou) (*Chinese Traditional*) +- KcKcZi (*Chinese Simplified*) - Amarin Cemthong (acitmaster) (*Thai*) - Johannes Nilsson (nlssn) (*Swedish*) - juanda097 (juanda-097) (*Spanish*) +- xsml (*Chinese Simplified*) - Anunnakey (*Macedonian*) - erikkemp (*Dutch*) - erikstl (*Esperanto*) -- bobchao (*Chinese Traditional*) - twpenguin (*Chinese Traditional*) -- MadeInSteak (*Finnish*) +- Po-chiang Chao (bobchao) (*Chinese Traditional*) +- Marcus Myge (mygg-priv) (*Norwegian*) - Esther (esthermations) (*Portuguese*) +- MadeInSteak (*Finnish*) - t_aus_m (*German*) +- serapolis (*Japanese, Chinese Simplified, Chinese Traditional, Chinese Traditional, Hong Kong*) - Heimen Stoffels (Vistaus) (*Dutch*) - Rajarshi Guha (rajarshiguha) (*Bengali*) -- Mo_der Steven (SakuraPuare) (*Chinese Simplified*) - Gopal Sharma (gopalvirat) (*Hindi*) -- arethsu (*Swedish*) -- Carlos Solís (csolisr) (*Esperanto*) +- Linnéa (lesbian_subnet) (*Swedish*) +- 北䑓如法 (Nyoho) (*Japanese*) +- abidin toumi (Zet24) (*Arabic*) - Tofiq Abdula (Xwla) (*Sorani (Kurdish)*) +- Carlos Solís (csolisr) (*Esperanto*) +- Yamagishi Kazutoshi (ykzts) (*Japanese, Vietnamese, Icelandic, Sorani (Kurdish)*) - Parthan S Ramanujam (parthan) (*Tamil*) - Kasper Nymand (KasperNymand) (*Danish*) -- Jeff Huang (s8321414) (*Chinese Traditional*) -- TS (morte) (*Finnish*) - subram (*Turkish*) +- TS (morte) (*Finnish*) - SensDeViata (*Ukrainian*) - Ptrcmd (ptrcmd) (*Chinese Traditional*) +- megaleo (*Portuguese, Brazilian*) - SergioFMiranda (*Portuguese, Brazilian*) -- Percy (scvoet) (*Chinese Simplified*) -- Vivek K J (Vivekkj) (*Malayalam*) - hiroTS (*Chinese Traditional*) +- Vivek K J (Vivekkj) (*Malayalam*) +- arielcostas3 (*Galician*) - johne32rus23 (*Russian*) - AzureNya (*Chinese Simplified*) - OctolinGamer (octolingamer) (*Portuguese, Brazilian*) +- filippodb (*Italian*) - Ram varma (ram4varma) (*Tamil*) -- 北䑓如法 (Nyoho) (*Japanese*) +- sanser (*Russian*) +- Y.Yamashiro (uist1idrju3i) (*Japanese*) - Pukima (Pukimaa) (*German*) - diorama (*Italian*) -- Daniel Dimitrov (daniel.dimitrov) (*Bulgarian*) - frumble (*German*) +- Daniel Dimitrov (daniel.dimitrov) (*Bulgarian*) - kekkepikkuni (*Tamil*) +- MODcraft (*Chinese Simplified*) - oorsutri (*Tamil*) - Neo_Chen (NeoChen1024) (*Chinese Traditional*) - Nithin V (Nithin896) (*Tamil*) -- Marcus Myge (mygg-priv) (*Norwegian*) - Miro Rauhala (mirorauhala) (*Finnish*) -- AlexKoala (alexkoala) (*Korean*) +- Oymate (*Bengali*) - ಚಿರಾಗ್ ನಟರಾಜ್ (chiraag-nataraj) (*Kannada*) -- Aswin C (officialcjunior) (*Malayalam*) - Guillaume Turchini (orion78fr) (*French*) +- Aswin C (officialcjunior) (*Malayalam*) - Ganesh D (auntgd) (*Marathi*) +- Yuval Nehemia (yuvalne) (*Hebrew*) - mawoka-myblock (mawoka) (*German*) - dragnucs2 (*Arabic*) - Ryan Ho (koungho) (*Chinese Traditional*) -- Pedro Henrique (exploronauta) (*Portuguese, Brazilian*) - Tejas Harad (h_tejas) (*Marathi*) +- Pedro Henrique (exploronauta) (*Portuguese, Brazilian*) +- Amir Reza (ElAmir) (*Persian*) +- Tatsuto "Laminne" Yamamoto (laminne) (*Japanese*) - Vasanthan (vasanthan) (*Tamil*) - 硫酸鶏 (acid_chicken) (*Japanese*) -- clarmin b8 (clarminb8) (*Sorani (Kurdish)*) - programizer (*German*) +- clarmin b8 (clarminb8) (*Sorani (Kurdish)*) - manukp (*Malayalam*) -- earth dweller (sanethoughtyt) (*Marathi*) - psymyn (*Hebrew*) +- earth dweller (sanethoughtyt) (*Marathi*) +- Marek Ľach (marek-lach) (*Slovak*) - meijerivoi (toilet) (*Finnish*) - essaar (*Tamil*) +- Nemuj (Dentrado) (*Japanese, Esperanto*) - serubeena (*Swedish*) - Rintan (*Japanese*) - Karol Kosek (krkkPL) (*Polish*) -- Khó͘ Tiat-lêng (khotiatleng) (*Chinese Traditional, Taigi*) -- Hernik (hernik27) (*Czech*) +- Khó͘ Tiatlêng (khotiatleng) (*Chinese Traditional, Taigi*) - valarivan (*Tamil*) -- kuchengrab (*German*) +- Hernik (hernik27) (*Czech*) +- revarioba (*Spanish*) - friedbeans (*Croatian*) +- kuchengrab (*German*) - Abi Turi (abi123) (*Georgian*) - Hinaloe (hinaloe) (*Japanese*) - Sebastián Andil (Selrond) (*Slovak*) -- KEINOS (*Japanese*) -- filippodb (*Italian*) +- Ifnuth (*German*) - Asbjørn Olling (a2) (*Danish*) +- KEINOS (*Japanese*) - Balázs Meskó (meskobalazs) (*Hungarian*) +- Artem Mikhalitsin (artemmikhalitsin) (*Russian*) +- Algustionesa Yoshi (algustionesa) (*Indonesian*) - Bottle (suryasalem2010) (*Tamil*) - Wrya ali (John12) (*Sorani (Kurdish)*) - JzshAC (*Chinese Simplified*) +- siamano (*Thai, Esperanto*) +- gnu-ewm (*Polish*) - Antillion (antillion99) (*Spanish*) - Steven Tappert (sammy8806) (*German*) - Reg3xp (*Persian*) - Wassim EL BOUHAMIDI (elbouhamidiw) (*Arabic*) +- Maciej Błędkowski (mble) (*Polish*) - gowthamanb (*Tamil*) - hiphipvargas (*Portuguese*) -- Ch. (sftblw) (*Korean*) +- tunisiano187 (*French*) - Arttu Ylhävuori (arttu.ylhavuori) (*Finnish*) -- tctovsli (*Norwegian Nynorsk*) -- Timo Tijhof (Krinkle) (*Dutch*) +- Ch. (sftblw) (*Korean*) +- eorn (*Breton*) +- Jona (88wcJoWl) (*Spanish*) - Mikkel B. Goldschmidt (mikkelbjoern) (*Danish*) +- Timo Tijhof (Krinkle) (*Dutch*) +- Ka2n (kaanmetu) (*Turkish*) +- tctovsli (*Norwegian Nynorsk*) - mecqor labi (mecqorlabi) (*Persian*) - Odyssey346 (alexader612) (*Norwegian*) -- Yamagishi Kazutoshi (ykzts) (*Japanese, Icelandic, Sorani (Kurdish)*) -- Eban (ebanDev) (*French, Esperanto*) - vjasiegd (*Polish*) +- Eban (ebanDev) (*French, Esperanto*) - SamitiMed (samiti3d) (*Thai*) - Nícolas Lavinicki (nclavinicki) (*Portuguese, Brazilian*) -- snatcher (*Portuguese, Brazilian*) - Rekan Adl (rekan-adl1) (*Sorani (Kurdish)*) +- Antara2Cinta (Se7enTime) (*Indonesian*) +- Yassine Aït-El-Mouden (yaitelmouden) (*Standard Moroccan Tamazight*) - VSx86 (*Russian*) - umelard (*Hebrew*) -- Antara2Cinta (Se7enTime) (*Indonesian*) - parnikkapore (*Thai*) +- Lagash (lagash) (*Esperanto*) - Sherwan Othman (sherwanothman11) (*Sorani (Kurdish)*) -- Yassine Aït-El-Mouden (yaitelmouden) (*Standard Moroccan Tamazight*) - SKELET (*Danish*) +- Exbu (*Dutch*) +- Chine Sebastien (chine.sebastien) (*French*) - Fei Yang (Fei1Yang) (*Chinese Traditional*) +- A A (sebastien.chine) (*French*) - Ğani (freegnu) (*Tatar*) -- Renato "Lond" Cerqueira (renatolond) (*Portuguese, Brazilian*) - enipra (*Armenian*) -- ALEM FARID (faridatcemlulaqbayli) (*Kabyle*) +- Renato "Lond" Cerqueira (renatolond) (*Portuguese, Brazilian*) - musix (*Persian*) - ギャラ (gyara) (*Japanese, Chinese Simplified*) +- ALEM FARID (faridatcemlulaqbayli) (*Kabyle*) - Hougo (hougo) (*French*) -- ybardapurkar (*Marathi*) -- 亜緯丹穂 (ayiniho) (*Japanese*) -- Adrián Lattes (haztecaso) (*Spanish*) - Mordi Sacks (MordiSacks) (*Hebrew*) - Trinsec (*Dutch*) +- Adrián Lattes (haztecaso) (*Spanish*) - Tigran's Tips (tigrank08) (*Armenian*) -- TracyJacks (*Chinese Simplified*) +- 亜緯丹穂 (ayiniho) (*Japanese*) +- ybardapurkar (*Marathi*) - Szabolcs Gál (galszabolcs810624) (*Hungarian*) - Vladislav Săcrieriu (vladislavs14) (*Romanian*) -- danreznik (*Hebrew*) +- TracyJacks (*Chinese Simplified*) - rasheedgm (*Kannada*) -- omquylzu (*Latvian*) -- c6ristian (*German*) -- Belkacem Mohammed (belkacem77) (*Kabyle*) +- danreznik (*Hebrew*) +- Cirelli (cirelli94) (*Italian*) +- Siddharastro Doraku (sidharastro) (*Spanish, Mexico*) - lexxai (*Ukrainian*) +- omquylzu (*Latvian*) - Navjot Singh (nspeaks) (*Hindi*) +- mkljczk (*Polish*) +- Belkacem Mohammed (belkacem77) (*Kabyle*) +- c6ristian (*German*) +- damascene (*Arabic*) - Ozai (*German*) - Sahak Petrosyan (petrosyan) (*Armenian*) -- Oymate (*Bengali*) - Viorel-Cătălin Răpițeanu (rapiteanu) (*Romanian*) -- siamano (*Thai, Esperanto*) - Siddhartha Sarathi Basu (quinoa_biryani) (*Bengali*) - Pachara Chantawong (pachara2202) (*Thai*) -- Zijian Zhao (jobs2512821228) (*Chinese Simplified*) - Skew (noan.perrot) (*French*) -- mkljczk (*Polish*) +- Zijian Zhao (jobs2512821228) (*Chinese Simplified*) +- Overflow Cat (OverflowCat) (*Chinese Simplified, Chinese Traditional*) +- dbeaver (*German*) +- zordsdavini (*Lithuanian*) - Guru Prasath Anandapadmanaban (guruprasath) (*Tamil*) - turtle836 (*German*) - Marcepanek_ (thekingmarcepan) (*Polish*) -- Lamin (laminne) (*Japanese*) -- Yann Aguettaz (yann-a) (*French*) - Feruz Oripov (FeruzOripov) (*Russian*) -- serapolis (*Chinese Simplified, Chinese Traditional*) +- Yann Aguettaz (yann-a) (*French*) - Mick Onio (xgc.redes) (*Asturian*) - Malik Mann (dermalikmann) (*German*) +- padulafacundo (*Spanish*) - dadosch (*German*) -- r3dsp1 (*Chinese Traditional, Hong Kong*) - hg6 (*Hindi*) - Tianqi Zhang (tina.zhang040609) (*Chinese Simplified*) -- padulafacundo (*Spanish*) +- r3dsp1 (*Chinese Traditional, Hong Kong*) - johannes hove-henriksen (J0hsHH) (*Norwegian*) - Orlando Murcio (Atos20) (*Spanish, Mexico*) -- Padraic Calpin (padraic-padraic) (*Slovenian*) - cenegd (*Chinese Simplified*) -- piupiupiudiu (*Chinese Simplified*) +- Youngeon Lee (YoungeonLee) (*Korean*) - shdy (*German*) +- Umi (mtrumi) (*Chinese Simplified, Chinese Traditional, Hong Kong*) +- Padraic Calpin (padraic-padraic) (*Slovenian*) - Ильзира Рахматуллина (rahmatullinailzira53) (*Tatar*) -- Hugh Liu (youloveonlymeh) (*Chinese Simplified*) +- piupiupiudiu (*Chinese Simplified*) - Pixelcode (realpixelcode) (*German*) +- Dennis Reimund (reimunddennis7) (*German*) - Yogesh K S (yogi) (*Kannada*) - Adithya K (adithyak04) (*Malayalam*) -- Dennis Reimund (reimunddennis7) (*German*) +- DAI JIE (daijie) (*Chinese Simplified*) +- Hugh Liu (youloveonlymeh) (*Chinese Simplified*) - Rakino (rakino) (*Chinese Simplified*) -- Michał Sidor (michcioperz) (*Polish*) -- AmazighNM (*Kabyle*) -- Miquel Sabaté Solà (mssola) (*Catalan*) +- ZQYD (*Chinese Simplified*) +- X.M (kimonoki) (*Chinese Simplified*) +- boni777 (*Chinese Simplified*) - Jothipazhani Nagarajan (jothipazhani.n) (*Tamil*) -- hallomaurits (*Dutch*) +- Miquel Sabaté Solà (mssola) (*Catalan*) +- Stanisław Jelnicki (JelNiSlaw) (*Polish*) +- AmazighNM (*Kabyle*) - alnd hezh (alndhezh) (*Sorani (Kurdish)*) +- CloudSet (*Chinese Simplified*) - Clash Clans (KURD12345) (*Sorani (Kurdish)*) -- Solid Rhino (SolidRhino) (*Dutch*) - Metehan Özyürek (MetehanOzyurek) (*Turkish*) +- Paula SIMON (EncoreEutIlFalluQueJeLeSusse) (*French*) +- Solid Rhino (SolidRhino) (*Dutch*) +- nua_kr (*Korean*) +- hallomaurits (*Dutch*) - 林水溶 (shuiRong) (*Chinese Simplified*) -- Sébastien Feugère (smonff) (*French*) -- Y.Yamashiro (uist1idrju3i) (*Japanese*) +- rikrise (*Swedish*) - Takeshi Umeda (noellabo) (*Japanese*) - k_taka (peaceroad) (*Japanese*) -- hussama (*Portuguese, Brazilian*) +- Sébastien Feugère (smonff) (*French*) - Hallo Abdullah (hallo_hamza12) (*Sorani (Kurdish)*) -- Ashok314 (ashok314) (*Hindi*) -- PifyZ (*French*) -- OminousCry (*Russian*) +- hussama (*Portuguese, Brazilian*) +- EzigboOmenana (*Cornish*) - Robert Yano (throwcalmbobaway) (*Spanish, Mexico*) -- Tom_ (*Czech*) -- Tagada (Tagadda) (*French*) -- shafouz (*Portuguese, Brazilian*) - Yasin İsa YILDIRIM (redsfyre) (*Turkish*) +- PifyZ (*French*) +- Tagada (Tagadda) (*French*) - eichkat3r (*German*) +- Ashok314 (ashok314) (*Hindi*) +- Zlr- (cZeler) (*French*) - SnDer (*Dutch*) -- Kahina Mess (K_hina) (*Kabyle*) -- Swati Sani (swatisani) (*Urdu (Pakistan)*) +- OminousCry (*Russian*) +- Adam Sapiński (Adamos9898) (*Polish*) +- Tom_ (*Czech*) +- shafouz (*Portuguese, Brazilian*) +- Shrinivasan T (tshrinivasan) (*Tamil*) - Kk (kishorkumara3) (*Kannada*) +- Swati Sani (swatisani) (*Urdu (Pakistan)*) +- papayaisnotafood (*Chinese Traditional*) +- さっかりんにーさん (saccharin23) (*Japanese*) - Daniel M. (daniconil) (*Catalan*) -- Shrinivasan T (tshrinivasan) (*Tamil*) -- 夜楓Yoka (Yoka2627) (*Chinese Simplified*) +- César Daniel Cavanzo Quintero (LeinadCQ) (*Esperanto*) - Nathaël Noguès (NatNgs) (*French*) -- さっかりんにーさん (saccharin23) (*Japanese*) -- Rex_sa (rex07) (*Arabic*) +- 夜楓Yoka (Yoka2627) (*Chinese Simplified*) +- Mt Front (mtfront) (*Chinese Simplified*) +- Artem (Artem4ik) (*Russian*) - Robin van der Vliet (RobinvanderVliet) (*Esperanto*) -- Vikatakavi (*Kannada*) - Tradjincal (tradjincal) (*French*) -- pullopen (*Chinese Simplified*) - SusVersiva (*Catalan*) -- Marvin (magicmarvman) (*German*) - Zinkokooo (*Basque*) -- Livingston Samuel (livingston) (*Tamil*) -- CyberAmoeba (pseudoobscura) (*Chinese Simplified*) -- tsundoker (*Malayalam*) -- eorn (*Breton*) +- Marvin (magicmarvman) (*German*) +- Vikatakavi (*Kannada*) +- pullopen (*Chinese Simplified*) +- sergioaraujo1 (*Portuguese, Brazilian*) - prabhjot (*Hindi*) +- CyberAmoeba (pseudoobscura) (*Chinese Simplified*) - mmokhi (*Persian*) -- sergioaraujo1 (*Portuguese, Brazilian*) - Entelekheia-ousia (*Chinese Simplified*) -- Pierre Morvan (Iriep) (*Breton*) -- oscfd (*Spanish*) +- Livingston Samuel (livingston) (*Tamil*) +- tsundoker (*Malayalam*) - skaaarrr (*German*) -- mkljczk (mykylyjczyk) (*Polish*) -- fedot (*Russian*) +- Pierre Morvan (Iriep) (*Breton*) - Paz Galindo (paz.almendra.g) (*Spanish*) +- fedot (*Russian*) +- mkljczk (mykylyjczyk) (*Polish*) - Ricardo Colin (rysard) (*Spanish*) - Philipp Fischbeck (PFischbeck) (*German*) +- oscfd (*Spanish*) - Zoé Bőle (zoe1337) (*German*) -- EzigboOmenana (*Cornish*) - GaggiX (*Italian*) -- Lukas Fülling (lfuelling) (*German*) - JackXu (Merman-Jack) (*Chinese Simplified*) +- Lukas Fülling (lfuelling) (*German*) - ralozkolya (*Georgian*) -- Apple (blackteaovo) (*Chinese Simplified*) +- Jason Gibson (barberpike606) (*Slovenian, Chinese Simplified*) +- Dremski (*Bulgarian*) +- Kaede (kaedech) (*Japanese*) +- Aymeric (AymBroussier) (*French*) +- mashirozx (*Chinese Simplified*) +- María José Vera (mjverap) (*Spanish*) - asala4544 (*Basque*) -- Xurxo Guerra (xguerrap) (*Galician*) +- ronee (*Kurmanji (Kurdish)*) - qwerty287 (*German*) -- Anoop (anoopp) (*Malayalam*) - pezcurrel (*Italian*) -- Samir Tighzert (samir_t7) (*Kabyle*) -- Dremski (*Bulgarian*) -- Dennis Reimund (reimund_dennis) (*German*) +- Anoop (anoopp) (*Malayalam*) +- Apple (blackteaovo) (*Chinese Simplified*) +- Lilian Nabati (Lilounab49) (*French*) - ru_mactunnag (*Scottish Gaelic*) - Nocta (*French*) -- Aymeric (AymBroussier) (*French*) -- mashirozx (*Chinese Simplified*) +- Tangcuyu (*Chinese Simplified*) +- Dennis Reimund (reimund_dennis) (*German*) - Albatroz Jeremias (albjeremias) (*Portuguese*) +- Xurxo Guerra (xguerrap) (*Galician*) +- Samir Tighzert (samir_t7) (*Kabyle*) +- lokalisoija (*Finnish*) +- codl (*French*) +- thisdudeisvegan (braydofficial) (*German*) +- tamaina (*Japanese*) - Matias Lavik (matiaslavik) (*Norwegian Nynorsk*) +- Aman Alam (aalam) (*Punjabi*) +- Holger Huo (holgerhuo) (*Chinese Simplified*) - Amith Raj Shetty (amithraj1989) (*Kannada*) -- abidin toumi (Zet24) (*Arabic*) -- mikel (mikelalas) (*Spanish*) -- OpenAlgeria (*Arabic*) -- random_person (*Spanish*) -- Sais Lakshmanan (Saislakshmanan) (*Tamil*) -- Trond Boksasp (boksasp) (*Norwegian*) -- xpac1985 (xpac) (*German*) -- Zlr- (cZeler) (*French*) -- Mohammad Adnan Mahmood (adnanmig) (*Arabic*) - mimikun (*Japanese*) -- smedvedev (*Russian*) -- asretro (*Chinese Traditional, Hong Kong*) -- tamaina (*Japanese*) -- Aman Alam (aalam) (*Punjabi*) +- Ragnars Eggerts (rmegg1933) (*Latvian*) - ÀŘǾŚ PÀŚĦÀÍ (arospashai) (*Sorani (Kurdish)*) -- Kaede (kaedech) (*Japanese*) +- smedvedev (*Russian*) +- Sais Lakshmanan (Saislakshmanan) (*Tamil*) +- Mohammad Adnan Mahmood (adnanmig) (*Arabic*) +- OpenAlgeria (*Arabic*) +- Trond Boksasp (boksasp) (*Norwegian*) - Doug (douglasalvespe) (*Portuguese, Brazilian*) +- Mohd Bilal (mdb571) (*Malayalam*) - Fleva (*Sardinian*) -- Abijeet Patro (Abijeet) (*Basque*) -- SamOak (*Portuguese, Brazilian*) -- Aries (orlea) (*Japanese*) +- xpac1985 (xpac) (*German*) +- mikel (mikelalas) (*Spanish*) +- random_person (*Spanish*) +- asretro (*Chinese Traditional, Hong Kong*) +- Arĝentakato (argxentakato) (*Japanese*) +- Nithya Mary (nithyamary25) (*Tamil*) +- Azad ahmad (dashty) (*Sorani (Kurdish)*) - Bartek Fijałkowski (brateq) (*Polish*) -- NeverMine17 (*Russian*) -- Brodi (brodi1) (*Dutch*) -- Ács Zoltán (zoli111) (*Hungarian*) -- capiscuas (*Spanish*) -- Benjamin Cobb (benjamincobb) (*German*) -- djoerd (*Dutch*) -- waweic (*German*) -- Amir Kurdo (kuraking202) (*Sorani (Kurdish)*) +- ebrezhoneg (*Breton*) +- maksutheam (*Finnish*) +- majorblazr (*Danish*) +- Jill H. (kokakiwi) (*French*) +- Patrice Boivin (patriceboivin58) (*French*) +- centumix (*Japanese*) +- 江尚寒 (jiangshanghan) (*Chinese Simplified*) +- hud5634j (*Spanish*) +- おさ (osapon) (*Japanese*) +- Jiniux (*Italian*) +- Hannah (Aniqueper1) (*Chinese Simplified*) +- Ni Futchi (futchitwo) (*Japanese*) - dobrado (*Portuguese, Brazilian*) -- Baban Abdulrahman (baban.abdulrehman) (*Sorani (Kurdish)*) - dcapillae (*Spanish*) -- Azad ahmad (dashty) (*Sorani (Kurdish)*) -- Salh_haji6 (*Sorani (Kurdish)*) - Ranj A Abdulqadir (RanjAhmed) (*Sorani (Kurdish)*) -- tateisu (*Japanese*) +- Kurdish Translator (Kurdish.boy) (*Sorani (Kurdish)*) +- Amir Kurdo (kuraking202) (*Sorani (Kurdish)*) +- umonaca (*Chinese Simplified*) +- Jari Ronkainen (ronchaine) (*Finnish*) +- djoerd (*Dutch*) - Savarín Electrográfico Marmota Intergalactica (herrero.maty) (*Spanish*) -- ebrezhoneg (*Breton*) - 于晚霞 (xissshawww) (*Chinese Simplified*) -- silverscat_3 (SilversCat) (*Japanese*) -- centumix (*Japanese*) -- umonaca (*Chinese Simplified*) -- Ni Futchi (futchitwo) (*Japanese*) -- おさ (osapon) (*Japanese*) +- tateisu (*Japanese*) +- NeverMine17 (*Russian*) +- soheilkhanalipur (*Persian*) +- SamOak (*Portuguese, Brazilian*) - kavitha129 (*Tamil*) -- Hannah (Aniqueper1) (*Chinese Simplified*) -- Jiniux (*Italian*) -- Jari Ronkainen (ronchaine) (*Finnish*) -- Nithya Mary (nithyamary25) (*Tamil*) +- Salh_haji6 (*Sorani (Kurdish)*) +- Brodi (brodi1) (*Dutch*) +- capiscuas (*Spanish*) +- HSD Channel (kvdbve34) (*Russian*) +- Abijeet Patro (Abijeet) (*Basque*) +- Ács Zoltán (zoli111) (*Hungarian*) +- Benjamin Cobb (benjamincobb) (*German*) +- waweic (*German*) +- Aries (orlea) (*Japanese*) diff --git a/Aptfile b/Aptfile index b2cbad714d4dc9..9235141ad5e1d7 100644 --- a/Aptfile +++ b/Aptfile @@ -4,10 +4,8 @@ libicu-dev libidn11 libidn11-dev libpq-dev -libprotobuf-dev libxdamage1 libxfixes3 -protobuf-compiler zlib1g-dev libcairo2 libcroco3 diff --git a/CHANGELOG.md b/CHANGELOG.md index 3359b26324e230..8814d5a4c30158 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,243 @@ Changelog All notable changes to this project will be documented in this file. -## [3.4.7] - 2022-03-30 +## [3.5.1] - 2022-04-08 +### Added + +- Add pagination for trending statuses in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/17976)) + +### Changed + +- Change e-mail notifications to only be sent when recipient is offline ([Gargron](https://github.com/mastodon/mastodon/pull/17984)) + - Send e-mails for mentions and follows by default again + - But only when recipient does not have push notifications through an app +- Change `website` attribute to be nullable on `Application` entity in REST API ([rinsuki](https://github.com/mastodon/mastodon/pull/17962)) + +### Removed + +- Remove sign-in token authentication, instead send e-mail about new sign-in ([Gargron](https://github.com/mastodon/mastodon/pull/17970)) + - You no longer need to enter a security code sent through e-mail + - Instead you get an e-mail about a new sign-in from an unfamiliar IP address + +### Fixed + +- Fix error resposes for `from` search prefix ([single-right-quote](https://github.com/mastodon/mastodon/pull/17963)) +- Fix dangling language-specific trends ([Gargron](https://github.com/mastodon/mastodon/pull/17997)) +- Fix extremely rare race condition when deleting a status or account ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17994)) +- Fix trends returning less results per page when filtered in REST API ([Gargron](https://github.com/mastodon/mastodon/pull/17996)) +- Fix pagination header on empty trends responses in REST API ([Gargron](https://github.com/mastodon/mastodon/pull/17986)) +- Fix cookies secure flag being set when served over Tor ([Gargron](https://github.com/mastodon/mastodon/pull/17992)) +- Fix migration error handling ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17991)) +- Fix error when re-running some migrations if they get interrupted at the wrong moment ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17989)) +- Fix potentially missing statuses when reconnecting to streaming API in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17981), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17987), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17980)) +- Fix error when sending warning emails with custom text ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17983)) +- Fix unset `SMTP_RETURN_PATH` environment variable causing e-mail not to send ([Gargron](https://github.com/mastodon/mastodon/pull/17982)) +- Fix possible duplicate statuses in timelines in some edge cases in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17971)) +- Fix spurious edits and require incoming edits to be explicitly marked as such ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17918)) +- Fix error when encountering invalid pinned statuses ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17964)) +- Fix inconsistency in error handling when removing a status ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17974)) +- Fix admin API unconditionally requiring CSRF token ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17975)) +- Fix trending tags endpoint missing `offset` param in REST API ([Gargron](https://github.com/mastodon/mastodon/pull/17973)) +- Fix unusual number formatting in some locales ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17929)) +- Fix `S3_FORCE_SINGLE_REQUEST` environment variable not working ([HolgerHuo](https://github.com/mastodon/mastodon/pull/17922)) +- Fix failure to build assets with OpenSSL 3 ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17930)) +- Fix PWA manifest using outdated routes ([HolgerHuo](https://github.com/mastodon/mastodon/pull/17921)) +- Fix error when indexing statuses into Elasticsearch ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17912)) + +## [3.5.0] - 2022-03-30 +### Added + +- **Add support for incoming edited posts** ([Gargron](https://github.com/mastodon/mastodon/pull/16697), [Gargron](https://github.com/mastodon/mastodon/pull/17727), [Gargron](https://github.com/mastodon/mastodon/pull/17728), [Gargron](https://github.com/mastodon/mastodon/pull/17320), [Gargron](https://github.com/mastodon/mastodon/pull/17404), [Gargron](https://github.com/mastodon/mastodon/pull/17390), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17335), [Gargron](https://github.com/mastodon/mastodon/pull/17696), [Gargron](https://github.com/mastodon/mastodon/pull/17745), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17740), [Gargron](https://github.com/mastodon/mastodon/pull/17697), [Gargron](https://github.com/mastodon/mastodon/pull/17648), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17531), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17499), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17498), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17380), [Gargron](https://github.com/mastodon/mastodon/pull/17373), [Gargron](https://github.com/mastodon/mastodon/pull/17334), [Gargron](https://github.com/mastodon/mastodon/pull/17333), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17699), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17748)) + - Previous versions remain available for perusal and comparison + - People who reblogged a post are notified when it's edited + - New REST APIs: + - `PUT /api/v1/statuses/:id` + - `GET /api/v1/statuses/:id/history` + - `GET /api/v1/statuses/:id/source` + - New streaming API event: + - `status.update` +- **Add appeals for moderator decisions** ([Gargron](https://github.com/mastodon/mastodon/pull/17364), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17725), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17566), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17652), [Gargron](https://github.com/mastodon/mastodon/pull/17616), [Gargron](https://github.com/mastodon/mastodon/pull/17615), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17554), [Gargron](https://github.com/mastodon/mastodon/pull/17523)) + - All default moderator decisions now notify the affected user by e-mail + - They now link to an appeal page instead of suggesting replying to the e-mail + - They can now be found in account settings and not just e-mail + - Users can submit one appeal within 20 days of the decision + - Moderators can approve or reject the appeal +- **Add notifications for posts deleted by moderators** ([Gargron](https://github.com/mastodon/mastodon/pull/17204), [Gargron](https://github.com/mastodon/mastodon/pull/17668), [Gargron](https://github.com/mastodon/mastodon/pull/17746), [Gargron](https://github.com/mastodon/mastodon/pull/17679), [Gargron](https://github.com/mastodon/mastodon/pull/17487)) + - New, redesigned report view in admin UI + - Common report actions now only take one click to complete + - Deleting posts or marking as sensitive from report now notifies user + - Reports can be categorized by reason and specific rules violated + - The reasons are automatically cited in the notifications, except for spam + - Marking posts as sensitive now federates using post editing +- **Add explore page with trending posts and links** ([Gargron](https://github.com/mastodon/mastodon/pull/17123), [Gargron](https://github.com/mastodon/mastodon/pull/17431), [Gargron](https://github.com/mastodon/mastodon/pull/16917), [Gargron](https://github.com/mastodon/mastodon/pull/17677), [Gargron](https://github.com/mastodon/mastodon/pull/16938), [Gargron](https://github.com/mastodon/mastodon/pull/17044), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/16978), [Gargron](https://github.com/mastodon/mastodon/pull/16979), [tribela](https://github.com/mastodon/mastodon/pull/17066), [Gargron](https://github.com/mastodon/mastodon/pull/17072), [Gargron](https://github.com/mastodon/mastodon/pull/17403), [noiob](https://github.com/mastodon/mastodon/pull/17624), [mayaeh](https://github.com/mastodon/mastodon/pull/17755), [mayaeh](https://github.com/mastodon/mastodon/pull/17757), [Gargron](https://github.com/mastodon/mastodon/pull/17760), [mayaeh](https://github.com/mastodon/mastodon/pull/17762)) + - Hashtag trends algorithm is extended to work for posts and links + - Links are only considered if they have an adequate preview card + - Preview card generation has been improved to support structured data + - Links can only trend if the publisher (domain) has been approved + - Posts can only trend if the author has been approved + - Individual approval and rejection for posts and links is also available + - Moderators are notified about pending trends at most once every 2 hours + - Posts and link trends are language-specific + - Search page is redesigned into explore page in web UI + - Discovery tab is coming soon in official iOS and Android apps + - New REST APIs: + - `GET /api/v1/trends/links` + - `GET /api/v1/trends/statuses` + - `GET /api/v1/trends/tags` (alias of `GET /api/v1/trends`) + - `GET /api/v1/admin/trends/links` + - `GET /api/v1/admin/trends/statuses` + - `GET /api/v1/admin/trends/tags` +- **Add graphs and retention metrics to admin dashboard** ([Gargron](https://github.com/mastodon/mastodon/pull/16829), [Gargron](https://github.com/mastodon/mastodon/pull/17617), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17570), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/16910), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/16909), [mashirozx](https://github.com/mastodon/mastodon/pull/16884), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/16854)) + - Dashboard shows more numbers with development over time + - Other data such as most used interface languages and sign-up sources + - User retention graph shows how many new users stick around + - New REST APIs: + - `POST /api/v1/admin/measures` + - `POST /api/v1/admin/dimensions` + - `POST /api/v1/admin/retention` +- Add `GET /api/v1/accounts/familiar_followers` to REST API ([Gargron](https://github.com/mastodon/mastodon/pull/17700)) +- Add `POST /api/v1/accounts/:id/remove_from_followers` to REST API ([noellabo](https://github.com/mastodon/mastodon/pull/16864)) +- Add `category` and `rule_ids` params to `POST /api/v1/reports` IN REST API ([Gargron](https://github.com/mastodon/mastodon/pull/17492), [Gargron](https://github.com/mastodon/mastodon/pull/17682), [Gargron](https://github.com/mastodon/mastodon/pull/17713)) + - `category` can be one of: `spam`, `violation`, `other` (default) + - `rule_ids` must reference `rules` returned in `GET /api/v1/instance` +- Add global `lang` param to REST API ([Gargron](https://github.com/mastodon/mastodon/pull/17464), [Gargron](https://github.com/mastodon/mastodon/pull/17592)) +- Add `types` param to `GET /api/v1/notifications` in REST API ([Gargron](https://github.com/mastodon/mastodon/pull/17767)) +- **Add notifications for moderators about new sign-ups** ([Gargron](https://github.com/mastodon/mastodon/pull/16953), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17629)) + - When a new user confirms e-mail, moderators receive a notification + - New notification type: + - `admin.sign_up` +- Add authentication history ([Gargron](https://github.com/mastodon/mastodon/pull/16408), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/16428), [baby-gnu](https://github.com/mastodon/mastodon/pull/16654)) +- Add ability to automatically delete old posts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16529), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17691), [tribela](https://github.com/mastodon/mastodon/pull/16653)) +- Add ability to pin private posts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16954), [tribela](https://github.com/mastodon/mastodon/pull/17326), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17304), [MitarashiDango](https://github.com/mastodon/mastodon/pull/17647)) +- Add ability to filter search results by author using `from:` syntax ([tribela](https://github.com/mastodon/mastodon/pull/16526)) +- Add ability to delete canonical email blocks in admin UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16644)) +- Add ability to purge undeliverable domains in admin UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16686), [tribela](https://github.com/mastodon/mastodon/pull/17210), [tribela](https://github.com/mastodon/mastodon/pull/17741), [tribela](https://github.com/mastodon/mastodon/pull/17209)) +- Add ability to disable e-mail token authentication for specific users in admin UI ([Gargron](https://github.com/mastodon/mastodon/pull/16427)) +- **Add ability to suspend accounts in batches in admin UI** ([Gargron](https://github.com/mastodon/mastodon/pull/17009), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17301), [Gargron](https://github.com/mastodon/mastodon/pull/17444)) + - New, redesigned accounts list in admin UI + - Batch suspensions are meant to help clean up spam and bot accounts + - They do not generate notifications +- Add ability to filter reports by origin of target account in admin UI ([Gargron](https://github.com/mastodon/mastodon/pull/16487)) +- Add support for login through OpenID Connect ([chandrn7](https://github.com/mastodon/mastodon/pull/16221)) +- Add lazy loading for emoji picker in web UI ([mashirozx](https://github.com/mastodon/mastodon/pull/16907), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17011)) +- Add single option votes tooltip in polls in web UI ([Brawaru](https://github.com/mastodon/mastodon/pull/16849)) +- Add confirmation modal when closing media edit modal with unsaved changes in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16518)) +- Add hint about missing media attachment description in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/17845)) +- Add support for fetching Create and Announce activities by URI in ActivityPub ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16383)) +- Add `S3_FORCE_SINGLE_REQUEST` environment variable ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16866)) +- Add `OMNIAUTH_ONLY` environment variable ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17288), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17345)) +- Add `ES_USER` and `ES_PASS` environment variables for Elasticsearch authentication ([tribela](https://github.com/mastodon/mastodon/pull/16890)) +- Add `CAS_SECURITY_ASSUME_EMAIL_IS_VERIFIED` environment variable ([baby-gnu](https://github.com/mastodon/mastodon/pull/16655)) +- Add ability to pass specific domains to `tootctl accounts cull` ([tribela](https://github.com/mastodon/mastodon/pull/16511)) +- Add `--by-uri` option to `tootctl domains purge` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16434)) +- Add `--batch-size` option to `tootctl search deploy` ([aquarla](https://github.com/mastodon/mastodon/pull/17049)) +- Add `--remove-orphans` option to `tootctl statuses remove` ([noellabo](https://github.com/mastodon/mastodon/pull/17067)) + +### Changed + +- Change design of federation pages in admin UI ([Gargron](https://github.com/mastodon/mastodon/pull/17704), [noellabo](https://github.com/mastodon/mastodon/pull/17735), [Gargron](https://github.com/mastodon/mastodon/pull/17765)) +- Change design of account cards in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/17689)) +- Change `follow` scope to be covered by `read` and `write` scopes in REST API ([Gargron](https://github.com/mastodon/mastodon/pull/17678)) +- Change design of authorized applications page ([Gargron](https://github.com/mastodon/mastodon/pull/17656), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17686)) +- Change e-mail domain blocks to block IPs dynamically ([Gargron](https://github.com/mastodon/mastodon/pull/17635), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17650), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17649)) +- Change report modal to include category selection in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/17565), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17734), [Gargron](https://github.com/mastodon/mastodon/pull/17654), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17632)) +- Change reblogs to not count towards hashtag trends anymore ([Gargron](https://github.com/mastodon/mastodon/pull/17501)) +- Change languages to be listed under standard instead of native name in admin UI ([Gargron](https://github.com/mastodon/mastodon/pull/17485)) +- Change routing paths to use usernames in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/16171), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/16772), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/16773), [mashirozx](https://github.com/mastodon/mastodon/pull/16793), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17060)) +- Change list title input design in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17092)) +- Change "Opt-in to profile directory" preference to be general discoverability preference ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16637)) +- Change API rate limits to use /64 masking on IPv6 addresses ([tribela](https://github.com/mastodon/mastodon/pull/17588), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17600), [zunda](https://github.com/mastodon/mastodon/pull/17590)) +- Change allowed formats for locally uploaded custom emojis to include GIF ([rgroothuijsen](https://github.com/mastodon/mastodon/pull/17706), [Gargron](https://github.com/mastodon/mastodon/pull/17759)) +- Change error message when chosen password is too long ([rgroothuijsen](https://github.com/mastodon/mastodon/pull/17082)) +- Change minimum required Elasticsearch version from 6 to 7 ([noellabo](https://github.com/mastodon/mastodon/pull/16915)) + +### Removed + +- Remove profile directory link from main navigation panel in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/17688)) +- **Remove language detection through cld3** ([Gargron](https://github.com/mastodon/mastodon/pull/17478), [ykzts](https://github.com/mastodon/mastodon/pull/17539), [Gargron](https://github.com/mastodon/mastodon/pull/17496), [Gargron](https://github.com/mastodon/mastodon/pull/17722)) + - cld3 is very inaccurate on short-form content even with unique alphabets + - Post language can be overriden individually using `language` param + - Otherwise, it defaults to the user's interface language +- Remove support for `OAUTH_REDIRECT_AT_SIGN_IN` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17287)) + - Use `OMNIAUTH_ONLY` instead +- Remove Keybase integration ([Gargron](https://github.com/mastodon/mastodon/pull/17045)) +- Remove old columns and indexes ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17245), [Gargron](https://github.com/mastodon/mastodon/pull/16409), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17191)) +- Remove shortcodes from newly-created media attachments ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16730), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/16763)) + +### Deprecated + +- `GET /api/v1/trends` → `GET /api/v1/trends/tags` +- OAuth `follow` scope → `read` and/or `write` +- `text` attribute on `DELETE /api/v1/statuses/:id` → `GET /api/v1/statuses/:id/source` + +### Fixed + +- Fix IDN domains not being rendered correctly in a few left-over places ([Gargron](https://github.com/mastodon/mastodon/pull/17848)) +- Fix Sanskrit translation not being used in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17820)) +- Fix Kurdish languages having the wrong language codes ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17812)) +- Fix pghero making database schema suggestions ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17807)) +- Fix encoding glitch in the OpenGraph description of a profile page ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17821)) +- Fix web manifest not permitting PWA usage from alternate domains ([HolgerHuo](https://github.com/mastodon/mastodon/pull/16714)) +- Fix not being able to edit media attachments for scheduled posts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17690)) +- Fix subscribed relay activities being recorded as boosts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17571)) +- Fix streaming API server error messages when JSON parsing fails not specifying the source ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17559)) +- Fix browsers autofilling new password field with old password ([mashirozx](https://github.com/mastodon/mastodon/pull/17702)) +- Fix text being invisible before fonts load in web UI ([tribela](https://github.com/mastodon/mastodon/pull/16330)) +- Fix public profile pages of unconfirmed users being accessible ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17385), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17457)) +- Fix nil error when trying to fetch key for signature verification ([Gargron](https://github.com/mastodon/mastodon/pull/17747)) +- Fix null values being included in some indexes ([Gargron](https://github.com/mastodon/mastodon/pull/17711)) +- Fix `POST /api/v1/emails/confirmations` not being available after sign-up ([Gargron](https://github.com/mastodon/mastodon/pull/17743)) +- Fix rare race condition when reblogged post is deleted ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17693), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17730)) +- Fix being able to add more than 4 hashtags to hashtag column in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/17729)) +- Fix data integrity of featured tags ([Gargron](https://github.com/mastodon/mastodon/pull/17712)) +- Fix performance of account timelines ([Gargron](https://github.com/mastodon/mastodon/pull/17709)) +- Fix returning empty `

` tag for blank account `note` in REST API ([Gargron](https://github.com/mastodon/mastodon/pull/17687)) +- Fix leak of existence of otherwise inaccessible posts in REST API ([Gargron](https://github.com/mastodon/mastodon/pull/17684)) +- Fix not showing loading indicator when searching in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/17655)) +- Fix media modal footer's “external link” not being a link ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17561)) +- Fix reply button on media modal not giving focus to compose form ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17626)) +- Fix some media attachments being converted with too high framerates ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17619)) +- Fix sign in token and warning emails failing to send when contact e-mail address is malformed ([helloworldstack](https://github.com/mastodon/mastodon/pull/17589)) +- Fix opening the emoji picker scrolling the single-column view to the top ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17579)) +- Fix edge case where settings/admin page sidebar would be incorrectly hidden ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17580)) +- Fix performance of server-side filtering ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17575)) +- Fix privacy policy link not being visible on small screens ([Gargron](https://github.com/mastodon/mastodon/pull/17533)) +- Fix duplicate accounts when searching by IP range in admin UI ([Gargron](https://github.com/mastodon/mastodon/pull/17524), [tribela](https://github.com/mastodon/mastodon/pull/17150)) +- Fix error when performing a batch action on posts in admin UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17532)) +- Fix deletes not being signed in authorized fetch mode ([Gargron](https://github.com/mastodon/mastodon/pull/17484)) +- Fix Undo Announce sometimes inlining the originally Announced status ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17516)) +- Fix localization of cold-start follow recommendations ([Gargron](https://github.com/mastodon/mastodon/pull/17479), [Gargron](https://github.com/mastodon/mastodon/pull/17486)) +- Fix replies collection incorrectly looping ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17462)) +- Fix errors when multiple Delete are received for a given actor ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17460)) +- Fixed prototype pollution bug and only allow trusted origin ([r0hanSH](https://github.com/mastodon/mastodon/pull/17420)) +- Fix text being incorrectly pre-selected in composer textarea on /share ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17339)) +- Fix SMTP_ENABLE_STARTTLS_AUTO/SMTP_TLS/SMTP_SSL environment variables don't work ([kgtkr](https://github.com/mastodon/mastodon/pull/17216)) +- Fix media upload specific rate limits only being applied to v1 endpoint in REST API ([tribela](https://github.com/mastodon/mastodon/pull/17272)) +- Fix media descriptions not being used for client-side filtering ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17206)) +- Fix cold-start follow recommendation favouring older accounts due to wrong sorting ([noellabo](https://github.com/mastodon/mastodon/pull/17126)) +- Fix not redirect to the right page after authenticating with WebAuthn ([heguro](https://github.com/mastodon/mastodon/pull/17098)) +- Fix searching for additional hashtags in hashtag column ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17054)) +- Fix color of hashtag column settings inputs ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17058)) +- Fix performance of `tootctl statuses remove` ([noellabo](https://github.com/mastodon/mastodon/pull/17052)) +- Fix `tootctl accounts cull` not excluding domains on timeouts and certificate issues ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16433)) +- Fix 404 error when filtering admin action logs by non-existent target account ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16643)) +- Fix error when accessing streaming API without any OAuth scopes ([Brawaru](https://github.com/mastodon/mastodon/pull/16823)) +- Fix follow request count not updating when new follow requests arrive over streaming API in web UI ([matildepark](https://github.com/mastodon/mastodon/pull/16652)) +- Fix error when unsuspending a local account ([HolgerHuo](https://github.com/mastodon/mastodon/pull/16605)) +- Fix crash when a notification contains a not yet processed media attachment in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16573)) +- Fix wrong color of download button in audio player in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16572)) +- Fix notes for others accounts not being deleted when an account is deleted ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16579)) +- Fix error when logging occurrence of unsupported video file ([noellabo](https://github.com/mastodon/mastodon/pull/16581)) +- Fix wrong elements in trends widget being hidden on smaller screens in web UI ([tribela](https://github.com/mastodon/mastodon/pull/16570)) +- Fix link to about page being displayed in limited federation mode ([weex](https://github.com/mastodon/mastodon/pull/16432)) +- Fix styling of boost button in media modal not reflecting ability to boost ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16387)) +- Fix OCR failure when erroneous lang data is in cache ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16386)) +- Fix downloading media from blocked domains in `tootctl media refresh` ([tribela](https://github.com/mastodon/mastodon/pull/16914)) +- Fix login form being displayed on landing page when already logged in ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17348)) +- Fix polling for media processing status too frequently in web UI ([tribela](https://github.com/mastodon/mastodon/pull/17271)) +- Fix hashtag autocomplete overriding user-typed case ([weex](https://github.com/mastodon/mastodon/pull/16460)) +- Fix WebAuthn authentication setup to not prompt for PIN ([truongnmt](https://github.com/mastodon/mastodon/pull/16545)) + ### Security - Fix being able to post URLs longer than 4096 characters ([Gargron](https://github.com/mastodon/mastodon/pull/17908)) @@ -11,10 +247,12 @@ All notable changes to this project will be documented in this file. ## [3.4.6] - 2022-02-03 ### Fixed + - Fix `mastodon:webpush:generate_vapid_key` task requiring a functional environment ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17338)) - Fix spurious errors when receiving an Add activity for a private post ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17425)) ### Security + - Fix error-prone SQL queries ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15828)) - Fix not compacting incoming signed JSON-LD activities ([puckipedia](https://github.com/mastodon/mastodon/pull/17426), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17428)) (CVE-2022-24307) - Fix insufficient sanitization of report comments ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17430)) @@ -23,10 +261,12 @@ All notable changes to this project will be documented in this file. ## [3.4.5] - 2022-01-31 ### Added + - Add more advanced migration tests ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17393)) - Add github workflow to build Docker images ([unasuke](https://github.com/mastodon/mastodon/pull/16973), [Gargron](https://github.com/mastodon/mastodon/pull/16980), [Gargron](https://github.com/mastodon/mastodon/pull/17000)) ### Fixed + - Fix some old migrations failing when skipping releases ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17394)) - Fix migrations script failing in certain edge cases ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17398)) - Fix Docker build ([tribela](https://github.com/mastodon/mastodon/pull/17188)) @@ -89,7 +329,7 @@ All notable changes to this project will be documented in this file. - Fix suspended accounts statuses being merged back into timelines ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16628)) - Fix crash when encountering invalid account fields ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16598)) - Fix invalid blurhash handling for remote activities ([noellabo](https://github.com/mastodon/mastodon/pull/16583)) -- Fix newlines being added to accout notes when an account moves ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16415), [noellabo](https://github.com/mastodon/mastodon/pull/16576)) +- Fix newlines being added to account notes when an account moves ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16415), [noellabo](https://github.com/mastodon/mastodon/pull/16576)) - Fix crash when creating an announcement with links ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16941)) - Fix logging out from one browser logging out all other sessions ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16943)) @@ -422,7 +662,7 @@ All notable changes to this project will be documented in this file. - Fix inefficiency when fetching bookmarks ([akihikodaki](https://github.com/mastodon/mastodon/pull/14674)) - Fix inefficiency when fetching favourites ([akihikodaki](https://github.com/mastodon/mastodon/pull/14673)) - Fix inefficiency when fetching media-only account timeline ([akihikodaki](https://github.com/mastodon/mastodon/pull/14675)) -- Fix inefficieny when deleting accounts ([Gargron](https://github.com/mastodon/mastodon/pull/15387), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15409), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15407), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15408), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15402), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15416), [Gargron](https://github.com/mastodon/mastodon/pull/15421)) +- Fix inefficiency when deleting accounts ([Gargron](https://github.com/mastodon/mastodon/pull/15387), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15409), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15407), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15408), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15402), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15416), [Gargron](https://github.com/mastodon/mastodon/pull/15421)) - Fix redundant query when processing batch actions on custom emojis ([niwatori24](https://github.com/mastodon/mastodon/pull/14534)) - Fix slow distinct queries where grouped queries are faster ([Gargron](https://github.com/mastodon/mastodon/pull/15287)) - Fix performance on instances list in admin UI ([Gargron](https://github.com/mastodon/mastodon/pull/15282)) @@ -509,7 +749,7 @@ All notable changes to this project will be documented in this file. - Add blurhash to link previews ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13984), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/14143), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/13985), [Sasha-Sorokin](https://github.com/mastodon/mastodon/pull/14267), [Sasha-Sorokin](https://github.com/mastodon/mastodon/pull/14278), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/14126), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/14261), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/14260)) - In web UI, toots cannot be marked as sensitive unless there is media attached - However, it's possible to do via API or ActivityPub - - Thumnails of link previews of such posts now use blurhash in web UI + - Thumbnails of link previews of such posts now use blurhash in web UI - The Card entity in REST API has a new `blurhash` attribute - Add support for `summary` field for media description in ActivityPub ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13763)) - Add hints about incomplete remote content to web UI ([Gargron](https://github.com/mastodon/mastodon/pull/14031), [noellabo](https://github.com/mastodon/mastodon/pull/14195)) @@ -532,7 +772,7 @@ All notable changes to this project will be documented in this file. - The `meta` attribute on the Media Attachment entity in REST API can now have a `colors` attribute which in turn contains three hex colors: `background`, `foreground`, and `accent` - The background color is chosen from the most dominant color around the edges of the thumbnail - The foreground and accent colors are chosen from the colors that are the most different from the background color using the CIEDE2000 algorithm - - The most satured color of the two is designated as the accent color + - The most saturated color of the two is designated as the accent color - The one with the highest W3C contrast is designated as the foreground color - If there are not enough colors in the thumbnail, new ones are generated using a monochrome pattern - Add a visibility indicator to toots in web UI ([noellabo](https://github.com/mastodon/mastodon/pull/14123), [highemerly](https://github.com/mastodon/mastodon/pull/14292)) @@ -558,7 +798,7 @@ All notable changes to this project will be documented in this file. - Change boost button to no longer serve as visibility indicator in web UI ([noellabo](https://github.com/mastodon/mastodon/pull/14132), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/14373)) - Change contrast of flash messages ([cchoi12](https://github.com/mastodon/mastodon/pull/13892)) - Change wording from "Hide media" to "Hide image/images" in web UI ([ariasuni](https://github.com/mastodon/mastodon/pull/13834)) -- Change appearence of settings pages to be more consistent ([ariasuni](https://github.com/mastodon/mastodon/pull/13938)) +- Change appearance of settings pages to be more consistent ([ariasuni](https://github.com/mastodon/mastodon/pull/13938)) - Change "Add media" tooltip to not include long list of formats in web UI ([ariasuni](https://github.com/mastodon/mastodon/pull/13954)) - Change how badly contrasting emoji are rendered in web UI ([leo60228](https://github.com/mastodon/mastodon/pull/13773), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/13772), [mfmfuyu](https://github.com/mastodon/mastodon/pull/14020), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/14015)) - Change structure of unavailable content section on about page ([ariasuni](https://github.com/mastodon/mastodon/pull/13930)) @@ -574,14 +814,14 @@ All notable changes to this project will be documented in this file. - `EMAIL_DOMAIN_WHITELIST` → `EMAIL_DOMAIN_ALLOWLIST` - CLI option changed: - `tootctl domains purge --whitelist-mode` → `tootctl domains purge --limited-federation-mode` -- Remove some unnecessary database indices ([lfuelling](https://github.com/mastodon/mastodon/pull/13695), [noellabo](https://github.com/mastodon/mastodon/pull/14259)) +- Remove some unnecessary database indexes ([lfuelling](https://github.com/mastodon/mastodon/pull/13695), [noellabo](https://github.com/mastodon/mastodon/pull/14259)) - Remove unnecessary Node.js version upper bound ([ykzts](https://github.com/mastodon/mastodon/pull/14139)) ### Fixed - Fix `following` param not working when exact match is found in account search ([noellabo](https://github.com/mastodon/mastodon/pull/14394)) -- Fix sometimes occuring duplicate mention notifications ([noellabo](https://github.com/mastodon/mastodon/pull/14378)) -- Fix RSS feeds not being cachable ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14368)) +- Fix sometimes occurring duplicate mention notifications ([noellabo](https://github.com/mastodon/mastodon/pull/14378)) +- Fix RSS feeds not being cacheable ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14368)) - Fix lack of locking around processing of Announce activities in ActivityPub ([noellabo](https://github.com/mastodon/mastodon/pull/14365)) - Fix boosted toots from blocked account not being retroactively removed from TL ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14339)) - Fix large shortened numbers (like 1.2K) using incorrect pluralization ([Sasha-Sorokin](https://github.com/mastodon/mastodon/pull/14061)) @@ -593,7 +833,7 @@ All notable changes to this project will be documented in this file. - Fix new posts pushing down origin of opened dropdown in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14271), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/14348)) - Fix timeline markers not being saved sometimes ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13887), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/13889), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/14155)) - Fix CSV uploads being rejected ([noellabo](https://github.com/mastodon/mastodon/pull/13835)) -- Fix incompatibility with ElasticSearch 7.x ([noellabo](https://github.com/mastodon/mastodon/pull/13828)) +- Fix incompatibility with Elasticsearch 7.x ([noellabo](https://github.com/mastodon/mastodon/pull/13828)) - Fix being able to search posts where you're in the target audience but not actively mentioned ([noellabo](https://github.com/mastodon/mastodon/pull/13829)) - Fix non-local posts appearing on local-only hashtag timelines in web UI ([noellabo](https://github.com/mastodon/mastodon/pull/13827)) - Fix `tootctl media remove-orphans` choking on unknown files in storage ([Gargron](https://github.com/mastodon/mastodon/pull/13765)) @@ -708,7 +948,7 @@ All notable changes to this project will be documented in this file. - Fix poll refresh button not being debounced in web UI ([rasjonell](https://github.com/mastodon/mastodon/pull/13485), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/13490)) - Fix confusing error when failing to add an alias to an unknown account ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13480)) - Fix "Email changed" notification sometimes having wrong e-mail ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13475)) -- Fix varioues issues on the account aliases page ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13452)) +- Fix various issues on the account aliases page ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13452)) - Fix API footer link in web UI ([bubblineyuri](https://github.com/mastodon/mastodon/pull/13441)) - Fix pagination of following, followers, follow requests, blocks and mutes lists in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13445)) - Fix styling of polls in JS-less fallback on public pages ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13436)) @@ -1197,7 +1437,7 @@ All notable changes to this project will be documented in this file. - Fix URLs appearing twice in errors of ActivityPub::DeliveryWorker ([Gargron](https://github.com/mastodon/mastodon/pull/11231)) - Fix support for HTTP proxies ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11245)) - Fix HTTP requests to IPv6 hosts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11240)) -- Fix error in ElasticSearch index import ([mayaeh](https://github.com/mastodon/mastodon/pull/11192)) +- Fix error in Elasticsearch index import ([mayaeh](https://github.com/mastodon/mastodon/pull/11192)) - Fix duplicate account error when seeding development database ([ysksn](https://github.com/mastodon/mastodon/pull/11366)) - Fix performance of session clean-up scheduler ([abcang](https://github.com/mastodon/mastodon/pull/11871)) - Fix older migrations not running ([zunda](https://github.com/mastodon/mastodon/pull/11377)) @@ -1207,8 +1447,8 @@ All notable changes to this project will be documented in this file. - Fix muted text color not applying to all text ([trwnh](https://github.com/mastodon/mastodon/pull/11996)) - Fix follower/following lists resetting on back-navigation in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/11986)) - Fix n+1 query when approving multiple follow requests ([abcang](https://github.com/mastodon/mastodon/pull/12004)) -- Fix records not being indexed into ElasticSearch sometimes ([Gargron](https://github.com/mastodon/mastodon/pull/12024)) -- Fix needlessly indexing unsearchable statuses into ElasticSearch ([Gargron](https://github.com/mastodon/mastodon/pull/12041)) +- Fix records not being indexed into Elasticsearch sometimes ([Gargron](https://github.com/mastodon/mastodon/pull/12024)) +- Fix needlessly indexing unsearchable statuses into Elasticsearch ([Gargron](https://github.com/mastodon/mastodon/pull/12041)) - Fix new user bootstrapping crashing when to-be-followed accounts are invalid ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/12037)) - Fix featured hashtag URL being interpreted as media or replies tab ([Gargron](https://github.com/mastodon/mastodon/pull/12048)) - Fix account counters being overwritten by parallel writes ([Gargron](https://github.com/mastodon/mastodon/pull/12045)) @@ -1498,7 +1738,7 @@ All notable changes to this project will be documented in this file. - Change Docker image to use Ubuntu with jemalloc ([Sir-Boops](https://github.com/mastodon/mastodon/pull/10100), [BenLubar](https://github.com/mastodon/mastodon/pull/10212)) - Change public pages to be cacheable by proxies ([BenLubar](https://github.com/mastodon/mastodon/pull/9059)) - Change the 410 gone response for suspended accounts to be cacheable by proxies ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10339)) -- Change web UI to not not empty timeline of blocked users on block ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10359)) +- Change web UI to not empty timeline of blocked users on block ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10359)) - Change JSON serializer to remove unused `@context` values ([Gargron](https://github.com/mastodon/mastodon/pull/10378)) - Change GIFV file size limit to be the same as for other videos ([rinsuki](https://github.com/mastodon/mastodon/pull/9924)) - Change Webpack to not use @babel/preset-env to compile node_modules ([ykzts](https://github.com/mastodon/mastodon/pull/10289)) @@ -1675,7 +1915,7 @@ All notable changes to this project will be documented in this file. - Limit maximum visibility of local silenced users to unlisted ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9583)) - Change API error message for unconfirmed accounts ([noellabo](https://github.com/mastodon/mastodon/pull/9625)) - Change the icon to "reply-all" when it's a reply to other accounts ([mayaeh](https://github.com/mastodon/mastodon/pull/9378)) -- Do not ignore federated reports targetting already-reported accounts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9534)) +- Do not ignore federated reports targeting already-reported accounts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9534)) - Upgrade default Ruby version to 2.6.0 ([Gargron](https://github.com/mastodon/mastodon/pull/9688)) - Change e-mail digest frequency ([Gargron](https://github.com/mastodon/mastodon/pull/9689)) - Change Docker images for Tor support in docker-compose.yml ([Sir-Boops](https://github.com/mastodon/mastodon/pull/9438)) diff --git a/Dockerfile b/Dockerfile index cbcdb20345e4fd..2073cbebff1541 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,9 +2,10 @@ FROM ubuntu:20.04 as build-dep # Use bash for the shell SHELL ["/bin/bash", "-c"] +RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections -# Install Node v12 (LTS) -ENV NODE_VER="12.21.0" +# Install Node v16 (LTS) +ENV NODE_VER="16.14.2" RUN ARCH= && \ dpkgArch="$(dpkg --print-architecture)" && \ case "${dpkgArch##*-}" in \ @@ -18,15 +19,15 @@ RUN ARCH= && \ esac && \ echo "Etc/UTC" > /etc/localtime && \ apt-get update && \ - apt-get install -y --no-install-recommends ca-certificates wget python && \ + apt-get install -y --no-install-recommends ca-certificates wget python apt-utils && \ cd ~ && \ wget -q https://nodejs.org/download/release/v$NODE_VER/node-v$NODE_VER-linux-$ARCH.tar.gz && \ tar xf node-v$NODE_VER-linux-$ARCH.tar.gz && \ rm node-v$NODE_VER-linux-$ARCH.tar.gz && \ mv node-v$NODE_VER-linux-$ARCH /opt/node -# Install Ruby -ENV RUBY_VER="2.7.2" +# Install Ruby 3.0 +ENV RUBY_VER="3.0.3" RUN apt-get update && \ apt-get install -y --no-install-recommends build-essential \ bison libyaml-dev libgdbm-dev libreadline-dev libjemalloc-dev \ @@ -45,17 +46,19 @@ RUN apt-get update && \ ENV PATH="${PATH}:/opt/ruby/bin:/opt/node/bin" -RUN npm install -g yarn && \ +RUN npm install -g npm@latest && \ + npm install -g yarn && \ gem install bundler && \ apt-get update && \ apt-get install -y --no-install-recommends git libicu-dev libidn11-dev \ - libpq-dev libprotobuf-dev protobuf-compiler shared-mime-info + libpq-dev shared-mime-info COPY Gemfile* package.json yarn.lock /opt/mastodon/ RUN cd /opt/mastodon && \ bundle config set --local deployment 'true' && \ bundle config set --local without 'development test' && \ + bundle config set silence_root_warning true && \ bundle install -j"$(nproc)" && \ yarn install --pure-lockfile @@ -81,11 +84,12 @@ RUN apt-get update && \ rm -rf /var/lib/apt/lists/* # Install mastodon runtime deps +RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections RUN apt-get update && \ apt-get -y --no-install-recommends install \ libssl1.1 libpq5 imagemagick ffmpeg libjemalloc2 \ - libicu66 libprotobuf17 libidn11 libyaml-0-2 \ - file ca-certificates tzdata libreadline8 gcc tini && \ + libicu66 libidn11 libyaml-0-2 \ + file ca-certificates tzdata libreadline8 gcc tini apt-utils && \ ln -s /opt/mastodon /mastodon && \ gem install bundler && \ rm -rf /var/cache && \ diff --git a/FEDERATION.md b/FEDERATION.md new file mode 100644 index 00000000000000..cd1957cbd1ecf1 --- /dev/null +++ b/FEDERATION.md @@ -0,0 +1,30 @@ +## ActivityPub federation in Mastodon + +Mastodon largely follows the ActivityPub server-to-server specification but it makes uses of some non-standard extensions, some of which are required for interacting with Mastodon at all. + +Supported vocabulary: https://docs.joinmastodon.org/spec/activitypub/ + +### Required extensions + +#### Webfinger + +In Mastodon, users are identified by a `username` and `domain` pair (e.g., `Gargron@mastodon.social`). +This is used both for discovery and for unambiguously mentioning users across the fediverse. Furthermore, this is part of Mastodon's database design from its very beginnings. + +As a result, Mastodon requires that each ActivityPub actor uniquely maps back to an `acct:` URI that can be resolved via WebFinger. + +More information and examples are available at: https://docs.joinmastodon.org/spec/webfinger/ + +#### HTTP Signatures + +In order to authenticate activities, Mastodon relies on HTTP Signatures, signing every `POST` and `GET` request to other ActivityPub implementations on behalf of the user authoring an activity (for `POST` requests) or an actor representing the Mastodon server itself (for most `GET` requests). + +Mastodon requires all `POST` requests to be signed, and MAY require `GET` requests to be signed, depending on the configuration of the Mastodon server. + +More information on HTTP Signatures, as well as examples, can be found here: https://docs.joinmastodon.org/spec/security/#http + +### Optional extensions + +- Linked-Data Signatures: https://docs.joinmastodon.org/spec/security/#ld +- Bearcaps: https://docs.joinmastodon.org/spec/bearcaps/ +- Followers collection synchronization: https://git.activitypub.dev/ActivityPubDev/Fediverse-Enhancement-Proposals/src/branch/main/feps/fep-8fcf.md diff --git a/Gemfile b/Gemfile index a7187d69162888..0e924b78d8c4f5 100644 --- a/Gemfile +++ b/Gemfile @@ -4,33 +4,32 @@ source 'https://rubygems.org' ruby '>= 2.5.0', '< 3.1.0' gem 'pkg-config', '~> 1.4' +gem 'rexml', '~> 3.2' -gem 'puma', '~> 5.3' -gem 'rails', '~> 6.1.3' +gem 'puma', '~> 5.6' +gem 'rails', '~> 6.1.5' gem 'sprockets', '~> 3.7.2' -gem 'thor', '~> 1.1' +gem 'thor', '~> 1.2' gem 'rack', '~> 2.2.3' gem 'hamlit-rails', '~> 0.2' -gem 'pg', '~> 1.2' +gem 'pg', '~> 1.3' gem 'makara', '~> 0.5' gem 'pghero', '~> 2.8' gem 'dotenv-rails', '~> 2.7' -gem 'aws-sdk-s3', '~> 1.95', require: false +gem 'aws-sdk-s3', '~> 1.113', require: false gem 'fog-core', '<= 2.1.0' gem 'fog-openstack', '~> 0.3', require: false -gem 'paperclip', '~> 6.0' +gem 'kt-paperclip', '~> 7.1' gem 'blurhash', '~> 0.1' gem 'active_model_serializers', '~> 0.10' -gem 'addressable', '~> 2.7' -gem 'bootsnap', '~> 1.6.0', require: false +gem 'addressable', '~> 2.8' +gem 'bootsnap', '~> 1.10.3', require: false gem 'browser' gem 'charlock_holmes', '~> 0.7.7' -gem 'iso-639' -gem 'chewy', '~> 5.2' -gem 'cld3', '~> 3.4.2' +gem 'chewy', '~> 7.2' gem 'devise', '~> 4.8' gem 'devise-two-factor', '~> 4.0' @@ -41,71 +40,71 @@ end gem 'net-ldap', '~> 0.17' gem 'omniauth-cas', '~> 2.0' gem 'omniauth-saml', '~> 1.10' +gem 'gitlab-omniauth-openid-connect', '~>0.9.1', require: 'omniauth_openid_connect' gem 'omniauth', '~> 1.9' gem 'omniauth-rails_csrf_protection', '~> 0.1' gem 'color_diff', '~> 0.1' gem 'discard', '~> 1.2' gem 'doorkeeper', '~> 5.5' -gem 'ed25519', '~> 1.2' +gem 'ed25519', '~> 1.3' gem 'fast_blank', '~> 1.0' gem 'fastimage' gem 'hiredis', '~> 0.6' gem 'redis-namespace', '~> 1.8' gem 'htmlentities', '~> 4.3' -gem 'http', '~> 4.4' +gem 'http', '~> 5.0' gem 'http_accept_language', '~> 2.1' gem 'httplog', '~> 1.5.0' gem 'idn-ruby', require: 'idn' gem 'kaminari', '~> 1.2' gem 'link_header', '~> 0.0' -gem 'mime-types', '~> 3.3.1', require: 'mime/types/columnar' -gem 'nokogiri', '~> 1.11' +gem 'mime-types', '~> 3.4.1', require: 'mime/types/columnar' +gem 'nokogiri', '~> 1.13' gem 'nsa', '~> 0.2' -gem 'oj', '~> 3.11' +gem 'oj', '~> 3.13' gem 'ox', '~> 2.14' gem 'parslet' -gem 'parallel', '~> 1.20' gem 'posix-spawn' -gem 'pundit', '~> 2.1' +gem 'pundit', '~> 2.2' gem 'premailer-rails' -gem 'rack-attack', '~> 6.5' +gem 'rack-attack', '~> 6.6' gem 'rack-cors', '~> 1.1', require: 'rack/cors' gem 'rails-i18n', '~> 6.0' gem 'rails-settings-cached', '~> 0.6' -gem 'redis', '~> 4.2', require: ['redis', 'redis/connection/hiredis'] +gem 'redis', '~> 4.5', require: ['redis', 'redis/connection/hiredis'] gem 'mario-redis-lock', '~> 1.2', require: 'redis_lock' -gem 'rqrcode', '~> 2.0' +gem 'rqrcode', '~> 2.1' gem 'ruby-progressbar', '~> 1.11' -gem 'sanitize', '~> 5.2' -gem 'scenic', '~> 1.5' -gem 'sidekiq', '~> 6.2' -gem 'sidekiq-scheduler', '~> 3.0' -gem 'sidekiq-unique-jobs', '~> 7.0' +gem 'sanitize', '~> 6.0' +gem 'scenic', '~> 1.6' +gem 'sidekiq', '~> 6.4' +gem 'sidekiq-scheduler', '~> 3.1' +gem 'sidekiq-unique-jobs', '~> 7.1' gem 'sidekiq-bulk', '~>0.2.0' gem 'simple-navigation', '~> 4.3' gem 'simple_form', '~> 5.1' -gem 'sprockets-rails', '~> 3.2', require: 'sprockets/railtie' +gem 'sprockets-rails', '~> 3.4', require: 'sprockets/railtie' gem 'stoplight', '~> 2.2.1' gem 'strong_migrations', '~> 0.7' gem 'tty-prompt', '~> 0.23', require: false gem 'twitter-text', '~> 3.1.0' -gem 'tzinfo-data', '~> 1.2021' +gem 'tzinfo-data', '~> 1.2022' gem 'webpacker', '~> 5.4' gem 'webpush', '~> 0.3' gem 'webauthn', '~> 3.0.0.alpha1' gem 'json-ld' -gem 'json-ld-preloaded', '~> 3.1' -gem 'rdf-normalize', '~> 0.4' +gem 'json-ld-preloaded', '~> 3.2' +gem 'rdf-normalize', '~> 0.5' group :development, :test do - gem 'fabrication', '~> 2.22' + gem 'fabrication', '~> 2.28' gem 'fuubar', '~> 2.5' - gem 'i18n-tasks', '~> 0.9', require: false + gem 'i18n-tasks', '~> 1.0', require: false gem 'pry-byebug', '~> 3.9' gem 'pry-rails', '~> 0.3' - gem 'rspec-rails', '~> 5.0' + gem 'rspec-rails', '~> 5.1' end group :production, :test do @@ -113,33 +112,32 @@ group :production, :test do end group :test do - gem 'capybara', '~> 3.35' + gem 'capybara', '~> 3.36' gem 'climate_control', '~> 0.2' - gem 'faker', '~> 2.18' + gem 'faker', '~> 2.20' gem 'microformats', '~> 4.2' gem 'rails-controller-testing', '~> 1.0' gem 'rspec-sidekiq', '~> 3.1' gem 'simplecov', '~> 0.21', require: false - gem 'webmock', '~> 3.13' - gem 'parallel_tests', '~> 3.7' - gem 'rspec_junit_formatter', '~> 0.4' + gem 'webmock', '~> 3.14' + gem 'rspec_junit_formatter', '~> 0.5' end group :development do gem 'active_record_query_trace', '~> 1.8' - gem 'annotate', '~> 3.1' + gem 'annotate', '~> 3.2' gem 'better_errors', '~> 2.9' gem 'binding_of_caller', '~> 1.0' - gem 'bullet', '~> 6.1' - gem 'letter_opener', '~> 1.7' - gem 'letter_opener_web', '~> 1.4' + gem 'bullet', '~> 7.0' + gem 'letter_opener', '~> 1.8' + gem 'letter_opener_web', '~> 2.0' gem 'memory_profiler' - gem 'rubocop', '~> 1.15', require: false - gem 'rubocop-rails', '~> 2.10', require: false - gem 'brakeman', '~> 5.0', require: false - gem 'bundler-audit', '~> 0.8', require: false + gem 'rubocop', '~> 1.26', require: false + gem 'rubocop-rails', '~> 2.14', require: false + gem 'brakeman', '~> 5.2', require: false + gem 'bundler-audit', '~> 0.9', require: false - gem 'capistrano', '~> 3.16' + gem 'capistrano', '~> 3.17' gem 'capistrano-rails', '~> 1.6' gem 'capistrano-rbenv', '~> 2.2' gem 'capistrano-yarn', '~> 2.0' @@ -148,12 +146,10 @@ group :development do end group :production do - gem 'lograge', '~> 0.11' + gem 'lograge', '~> 0.12' end gem 'concurrent-ruby', require: false gem 'connection_pool', require: false gem 'xorcist', '~> 1.1' - -gem 'resolv', '~> 0.1.0' diff --git a/Gemfile.lock b/Gemfile.lock index 5b71445c8d6b5b..e19bfde09d1962 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,132 +1,142 @@ GEM remote: https://rubygems.org/ specs: - actioncable (6.1.3.2) - actionpack (= 6.1.3.2) - activesupport (= 6.1.3.2) + actioncable (6.1.5) + actionpack (= 6.1.5) + activesupport (= 6.1.5) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailbox (6.1.3.2) - actionpack (= 6.1.3.2) - activejob (= 6.1.3.2) - activerecord (= 6.1.3.2) - activestorage (= 6.1.3.2) - activesupport (= 6.1.3.2) + actionmailbox (6.1.5) + actionpack (= 6.1.5) + activejob (= 6.1.5) + activerecord (= 6.1.5) + activestorage (= 6.1.5) + activesupport (= 6.1.5) mail (>= 2.7.1) - actionmailer (6.1.3.2) - actionpack (= 6.1.3.2) - actionview (= 6.1.3.2) - activejob (= 6.1.3.2) - activesupport (= 6.1.3.2) + actionmailer (6.1.5) + actionpack (= 6.1.5) + actionview (= 6.1.5) + activejob (= 6.1.5) + activesupport (= 6.1.5) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 2.0) - actionpack (6.1.3.2) - actionview (= 6.1.3.2) - activesupport (= 6.1.3.2) + actionpack (6.1.5) + actionview (= 6.1.5) + activesupport (= 6.1.5) rack (~> 2.0, >= 2.0.9) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0) - actiontext (6.1.3.2) - actionpack (= 6.1.3.2) - activerecord (= 6.1.3.2) - activestorage (= 6.1.3.2) - activesupport (= 6.1.3.2) + actiontext (6.1.5) + actionpack (= 6.1.5) + activerecord (= 6.1.5) + activestorage (= 6.1.5) + activesupport (= 6.1.5) nokogiri (>= 1.8.5) - actionview (6.1.3.2) - activesupport (= 6.1.3.2) + actionview (6.1.5) + activesupport (= 6.1.5) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.1, >= 1.2.0) - active_model_serializers (0.10.12) - actionpack (>= 4.1, < 6.2) - activemodel (>= 4.1, < 6.2) + active_model_serializers (0.10.13) + actionpack (>= 4.1, < 7.1) + activemodel (>= 4.1, < 7.1) case_transform (>= 0.2) jsonapi-renderer (>= 0.1.1.beta1, < 0.3) active_record_query_trace (1.8) - activejob (6.1.3.2) - activesupport (= 6.1.3.2) + activejob (6.1.5) + activesupport (= 6.1.5) globalid (>= 0.3.6) - activemodel (6.1.3.2) - activesupport (= 6.1.3.2) - activerecord (6.1.3.2) - activemodel (= 6.1.3.2) - activesupport (= 6.1.3.2) - activestorage (6.1.3.2) - actionpack (= 6.1.3.2) - activejob (= 6.1.3.2) - activerecord (= 6.1.3.2) - activesupport (= 6.1.3.2) - marcel (~> 1.0.0) - mini_mime (~> 1.0.2) - activesupport (6.1.3.2) + activemodel (6.1.5) + activesupport (= 6.1.5) + activerecord (6.1.5) + activemodel (= 6.1.5) + activesupport (= 6.1.5) + activestorage (6.1.5) + actionpack (= 6.1.5) + activejob (= 6.1.5) + activerecord (= 6.1.5) + activesupport (= 6.1.5) + marcel (~> 1.0) + mini_mime (>= 1.1.0) + activesupport (6.1.5) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) tzinfo (~> 2.0) zeitwerk (~> 2.3) - addressable (2.7.0) + addressable (2.8.0) public_suffix (>= 2.0.2, < 5.0) + aes_key_wrap (1.1.0) airbrussh (1.4.0) sshkit (>= 1.6.1, != 1.7.0) android_key_attestation (0.3.0) - annotate (3.1.1) - activerecord (>= 3.2, < 7.0) + annotate (3.2.0) + activerecord (>= 3.2, < 8.0) rake (>= 10.4, < 14.0) ast (2.4.2) attr_encrypted (3.1.0) encryptor (~> 3.0.0) + attr_required (1.0.1) awrence (1.1.1) - aws-eventstream (1.1.1) - aws-partitions (1.465.0) - aws-sdk-core (3.114.0) + aws-eventstream (1.2.0) + aws-partitions (1.558.0) + aws-sdk-core (3.127.0) aws-eventstream (~> 1, >= 1.0.2) - aws-partitions (~> 1, >= 1.239.0) + aws-partitions (~> 1, >= 1.525.0) aws-sigv4 (~> 1.1) jmespath (~> 1.0) - aws-sdk-kms (1.43.0) - aws-sdk-core (~> 3, >= 3.112.0) + aws-sdk-kms (1.55.0) + aws-sdk-core (~> 3, >= 3.127.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.95.1) - aws-sdk-core (~> 3, >= 3.112.0) + aws-sdk-s3 (1.113.0) + aws-sdk-core (~> 3, >= 3.127.0) aws-sdk-kms (~> 1) - aws-sigv4 (~> 1.1) - aws-sigv4 (1.2.3) + aws-sigv4 (~> 1.4) + aws-sigv4 (1.4.0) aws-eventstream (~> 1, >= 1.0.2) - bcrypt (3.1.16) + bcrypt (3.1.17) better_errors (2.9.1) coderay (>= 1.0.0) erubi (>= 1.0.0) rack (>= 0.9.0) - bindata (2.4.8) + better_html (1.0.16) + actionview (>= 4.0) + activesupport (>= 4.0) + ast (~> 2.0) + erubi (~> 1.4) + html_tokenizer (~> 0.0.6) + parser (>= 2.4) + smart_properties + bindata (2.4.10) binding_of_caller (1.0.0) debug_inspector (>= 0.0.1) - blurhash (0.1.5) + blurhash (0.1.6) ffi (~> 1.14) - bootsnap (1.6.0) - msgpack (~> 1.0) - brakeman (5.0.1) + bootsnap (1.10.3) + msgpack (~> 1.2) + brakeman (5.2.1) browser (4.2.0) brpoplpush-redis_script (0.1.2) concurrent-ruby (~> 1.0, >= 1.0.5) redis (>= 1.0, <= 5.0) builder (3.2.4) - bullet (6.1.4) + bullet (7.0.1) activesupport (>= 3.0.0) uniform_notifier (~> 1.11) - bundler-audit (0.8.0) + bundler-audit (0.9.0.1) bundler (>= 1.2.0, < 3) thor (~> 1.0) byebug (11.1.3) - capistrano (3.16.0) + capistrano (3.17.0) airbrussh (>= 1.0.0) i18n rake (>= 10.0.0) sshkit (>= 1.9.0) capistrano-bundler (2.0.1) capistrano (~> 3.1) - capistrano-rails (1.6.1) + capistrano-rails (1.6.2) capistrano (~> 3.1) capistrano-bundler (>= 1.1, < 3) capistrano-rbenv (2.2.0) @@ -134,8 +144,9 @@ GEM sshkit (~> 1.3) capistrano-yarn (2.0.2) capistrano (~> 3.0) - capybara (3.35.3) + capybara (3.36.0) addressable + matrix mini_mime (>= 0.1.3) nokogiri (~> 1.8) rack (>= 1.6.0) @@ -146,17 +157,15 @@ GEM activesupport cbor (0.5.9.6) charlock_holmes (0.7.7) - chewy (5.2.0) + chewy (7.2.4) activesupport (>= 5.2) - elasticsearch (>= 2.0.0) + elasticsearch (>= 7.12.0, < 7.14.0) elasticsearch-dsl chunky_png (1.4.0) - cld3 (3.4.2) - ffi (>= 1.1.0, < 1.16.0) climate_control (0.2.0) coderay (1.1.3) color_diff (0.1) - concurrent-ruby (1.1.8) + concurrent-ruby (1.1.10) connection_pool (2.2.5) cose (1.0.0) cbor (~> 0.5.9) @@ -167,60 +176,78 @@ GEM css_parser (1.7.1) addressable debug_inspector (1.0.0) - devise (4.8.0) + devise (4.8.1) bcrypt (~> 3.0) orm_adapter (~> 0.1) railties (>= 4.1.0) responders warden (~> 1.2.3) - devise-two-factor (4.0.0) - activesupport (< 6.2) + devise-two-factor (4.0.2) + activesupport (< 7.1) attr_encrypted (>= 1.3, < 4, != 2) devise (~> 4.0) - railties (< 6.2) + railties (< 7.1) rotp (~> 6.0) devise_pam_authenticatable2 (9.2.0) devise (>= 4.0.0) rpam2 (~> 4.0) - diff-lcs (1.4.4) - discard (1.2.0) - activerecord (>= 4.2, < 7) + diff-lcs (1.5.0) + discard (1.2.1) + activerecord (>= 4.2, < 8) docile (1.3.4) domain_name (0.5.20190701) unf (>= 0.0.5, < 1.0.0) - doorkeeper (5.5.1) + doorkeeper (5.5.4) railties (>= 5) dotenv (2.7.6) dotenv-rails (2.7.6) dotenv (= 2.7.6) railties (>= 3.2) e2mmap (0.1.0) - ed25519 (1.2.4) - elasticsearch (7.10.1) - elasticsearch-api (= 7.10.1) - elasticsearch-transport (= 7.10.1) - elasticsearch-api (7.10.1) + ed25519 (1.3.0) + elasticsearch (7.13.3) + elasticsearch-api (= 7.13.3) + elasticsearch-transport (= 7.13.3) + elasticsearch-api (7.13.3) multi_json - elasticsearch-dsl (0.1.9) - elasticsearch-transport (7.10.1) + elasticsearch-dsl (0.1.10) + elasticsearch-transport (7.13.3) faraday (~> 1) multi_json encryptor (3.0.0) erubi (1.10.0) - et-orbi (1.2.4) + et-orbi (1.2.6) tzinfo excon (0.76.0) - fabrication (2.22.0) - faker (2.18.0) - i18n (>= 1.6, < 2) - faraday (1.3.0) + fabrication (2.28.0) + faker (2.20.0) + i18n (>= 1.8.11, < 2) + faraday (1.9.3) + faraday-em_http (~> 1.0) + faraday-em_synchrony (~> 1.0) + faraday-excon (~> 1.1) + faraday-httpclient (~> 1.0) + faraday-multipart (~> 1.0) faraday-net_http (~> 1.0) + faraday-net_http_persistent (~> 1.0) + faraday-patron (~> 1.0) + faraday-rack (~> 1.0) + faraday-retry (~> 1.0) + ruby2_keywords (>= 0.0.4) + faraday-em_http (1.0.0) + faraday-em_synchrony (1.0.0) + faraday-excon (1.1.0) + faraday-httpclient (1.0.1) + faraday-multipart (1.0.3) multipart-post (>= 1.2, < 3) - ruby2_keywords faraday-net_http (1.0.1) - fast_blank (1.0.0) - fastimage (2.2.3) - ffi (1.15.0) + faraday-net_http_persistent (1.2.0) + faraday-patron (1.0.0) + faraday-rack (1.0.0) + faraday-retry (1.0.3) + fast_blank (1.0.1) + fastimage (2.2.6) + ffi (1.15.5) ffi-compiler (1.0.1) ffi (>= 1.0.0) rake @@ -237,14 +264,18 @@ GEM fog-json (>= 1.0) ipaddress (>= 0.8) formatador (0.2.5) - fugit (1.3.9) + fugit (1.5.2) et-orbi (~> 1.1, >= 1.1.8) - raabro (~> 1.3) + raabro (~> 1.4) fuubar (2.5.1) rspec-core (~> 3.0) ruby-progressbar (~> 1.4) - globalid (0.4.2) - activesupport (>= 4.2.0) + gitlab-omniauth-openid-connect (0.9.1) + addressable (~> 2.7) + omniauth (~> 1.9) + openid_connect (~> 1.2) + globalid (1.0.0) + activesupport (>= 5.0) hamlit (2.13.0) temple (>= 0.8.2) thor @@ -254,33 +285,32 @@ GEM activesupport (>= 4.0.1) hamlit (>= 1.2.0) railties (>= 4.0.1) - hamster (3.0.0) - concurrent-ruby (~> 1.0) hashdiff (1.0.1) - hashie (4.1.0) + hashie (5.0.0) highline (2.0.3) hiredis (0.6.3) hkdf (0.3.0) + html_tokenizer (0.0.7) htmlentities (4.3.4) - http (4.4.1) - addressable (~> 2.3) + http (5.0.4) + addressable (~> 2.8) http-cookie (~> 1.0) http-form_data (~> 2.2) - http-parser (~> 1.2.0) - http-cookie (1.0.3) + llhttp-ffi (~> 0.4.0) + http-cookie (1.0.4) domain_name (~> 0.5) http-form_data (2.3.0) - http-parser (1.2.1) - ffi-compiler (>= 1.0, < 2.0) http_accept_language (2.1.1) + httpclient (2.8.3) httplog (1.5.0) rack (>= 1.0) rainbow (>= 2.0.0) - i18n (1.8.10) + i18n (1.10.0) concurrent-ruby (~> 1.0) - i18n-tasks (0.9.34) + i18n-tasks (1.0.8) activesupport (>= 4.0.2) ast (>= 2.1.0) + better_html (~> 1.0) erubi highline (>= 2.0.0) i18n @@ -288,98 +318,102 @@ GEM rails-i18n rainbow (>= 2.2.2, < 4.0) terminal-table (>= 1.5.1) - idn-ruby (0.1.0) + idn-ruby (0.1.4) ipaddress (0.8.3) - iso-639 (0.3.5) - jmespath (1.4.0) + jmespath (1.6.0) json (2.5.1) - json-canonicalization (0.2.1) - json-ld (3.1.9) + json-canonicalization (0.3.0) + json-jwt (1.13.0) + activesupport (>= 4.2) + aes_key_wrap + bindata + json-ld (3.2.0) htmlentities (~> 4.3) - json-canonicalization (~> 0.2) + json-canonicalization (~> 0.3) link_header (~> 0.0, >= 0.0.8) - multi_json (~> 1.14) - rack (~> 2.0) - rdf (~> 3.1) - json-ld-preloaded (3.1.5) - json-ld (~> 3.1) - rdf (~> 3.1) + multi_json (~> 1.15) + rack (~> 2.2) + rdf (~> 3.2) + json-ld-preloaded (3.2.0) + json-ld (~> 3.2) + rdf (~> 3.2) jsonapi-renderer (0.2.2) jwt (2.2.2) - kaminari (1.2.1) + kaminari (1.2.2) activesupport (>= 4.1.0) - kaminari-actionview (= 1.2.1) - kaminari-activerecord (= 1.2.1) - kaminari-core (= 1.2.1) - kaminari-actionview (1.2.1) + kaminari-actionview (= 1.2.2) + kaminari-activerecord (= 1.2.2) + kaminari-core (= 1.2.2) + kaminari-actionview (1.2.2) actionview - kaminari-core (= 1.2.1) - kaminari-activerecord (1.2.1) + kaminari-core (= 1.2.2) + kaminari-activerecord (1.2.2) activerecord - kaminari-core (= 1.2.1) - kaminari-core (1.2.1) + kaminari-core (= 1.2.2) + kaminari-core (1.2.2) + kt-paperclip (7.1.1) + activemodel (>= 4.2.0) + activesupport (>= 4.2.0) + marcel (~> 1.0.1) + mime-types + terrapin (~> 0.6.0) launchy (2.5.0) addressable (~> 2.7) - letter_opener (1.7.0) - launchy (~> 2.2) - letter_opener_web (1.4.0) - actionmailer (>= 3.2) - letter_opener (~> 1.0) - railties (>= 3.2) + letter_opener (1.8.1) + launchy (>= 2.2, < 3) + letter_opener_web (2.0.0) + actionmailer (>= 5.2) + letter_opener (~> 1.7) + railties (>= 5.2) + rexml link_header (0.0.8) - lograge (0.11.2) + llhttp-ffi (0.4.0) + ffi-compiler (~> 1.0) + rake (~> 13.0) + lograge (0.12.0) actionpack (>= 4) activesupport (>= 4) railties (>= 4) request_store (~> 1.0) - loofah (2.9.1) + loofah (2.16.0) crass (~> 1.0.2) nokogiri (>= 1.5.9) mail (2.7.1) mini_mime (>= 0.1.1) - makara (0.5.0) - activerecord (>= 3.0.0) - marcel (1.0.1) + makara (0.5.1) + activerecord (>= 5.2.0) + marcel (1.0.2) mario-redis-lock (1.2.1) redis (>= 3.0.5) + matrix (0.4.2) memory_profiler (1.0.0) method_source (1.0.0) microformats (4.3.1) json (~> 2.2) nokogiri (~> 1.10) - mime-types (3.3.1) + mime-types (3.4.1) mime-types-data (~> 3.2015) - mime-types-data (3.2020.0512) - mimemagic (0.3.10) - nokogiri (~> 1) - rake - mini_mime (1.0.3) - mini_portile2 (2.5.2) - net-ftp (~> 0.1) - minitest (5.14.4) - msgpack (1.4.2) + mime-types-data (3.2022.0105) + mini_mime (1.1.2) + mini_portile2 (2.8.0) + minitest (5.15.0) + msgpack (1.4.4) multi_json (1.15.0) multipart-post (2.1.1) - net-ftp (0.1.2) - net-protocol - time net-ldap (0.17.0) - net-protocol (0.1.0) net-scp (3.0.0) net-ssh (>= 2.6.5, < 7.0.0) net-ssh (6.1.0) - nio4r (2.5.7) - nokogiri (1.11.6) - mini_portile2 (~> 2.5.0) + nio4r (2.5.8) + nokogiri (1.13.3) + mini_portile2 (~> 2.8.0) racc (~> 1.4) - nokogumbo (2.0.4) - nokogiri (~> 1.8, >= 1.8.4) nsa (0.2.8) activesupport (>= 4.2, < 7) concurrent-ruby (~> 1.0, >= 1.0.2) sidekiq (>= 3.5) statsd-ruby (~> 1.4, >= 1.4.0) - oj (3.11.5) + oj (3.13.11) omniauth (1.9.1) hashie (>= 3.4.6) rack (>= 1.6.2, < 3) @@ -393,28 +427,30 @@ GEM omniauth-saml (1.10.3) omniauth (~> 1.3, >= 1.3.2) ruby-saml (~> 1.9) + openid_connect (1.3.0) + activemodel + attr_required (>= 1.0.0) + json-jwt (>= 1.5.0) + rack-oauth2 (>= 1.6.1) + swd (>= 1.0.0) + tzinfo + validate_email + validate_url + webfinger (>= 1.0.1) openssl (2.2.0) openssl-signature_algorithm (0.4.0) orm_adapter (0.5.0) - ox (2.14.4) - paperclip (6.0.0) - activemodel (>= 4.2.0) - activesupport (>= 4.2.0) - mime-types - mimemagic (~> 0.3.0) - terrapin (~> 0.6.0) - parallel (1.20.1) - parallel_tests (3.7.0) - parallel - parser (3.0.1.1) + ox (2.14.11) + parallel (1.22.1) + parser (3.1.1.0) ast (~> 2.4.1) parslet (2.0.0) pastel (0.8.0) tty-color (~> 0.5) - pg (1.2.3) - pghero (2.8.1) + pg (1.3.5) + pghero (2.8.2) activerecord (>= 5) - pkg-config (1.4.6) + pkg-config (1.4.7) posix-spawn (0.3.15) premailer (1.14.2) addressable @@ -433,35 +469,41 @@ GEM pry-rails (0.3.9) pry (>= 0.10.4) public_suffix (4.0.6) - puma (5.3.2) + puma (5.6.4) nio4r (~> 2.0) - pundit (2.1.0) + pundit (2.2.0) activesupport (>= 3.0.0) - raabro (1.3.3) - racc (1.5.2) + raabro (1.4.0) + racc (1.6.0) rack (2.2.3) - rack-attack (6.5.0) + rack-attack (6.6.0) rack (>= 1.0, < 3) rack-cors (1.1.1) rack (>= 2.0.0) - rack-proxy (0.6.5) + rack-oauth2 (1.19.0) + activesupport + attr_required + httpclient + json-jwt (>= 1.11.0) + rack (>= 2.1.0) + rack-proxy (0.7.0) rack rack-test (1.1.0) rack (>= 1.0, < 3) - rails (6.1.3.2) - actioncable (= 6.1.3.2) - actionmailbox (= 6.1.3.2) - actionmailer (= 6.1.3.2) - actionpack (= 6.1.3.2) - actiontext (= 6.1.3.2) - actionview (= 6.1.3.2) - activejob (= 6.1.3.2) - activemodel (= 6.1.3.2) - activerecord (= 6.1.3.2) - activestorage (= 6.1.3.2) - activesupport (= 6.1.3.2) + rails (6.1.5) + actioncable (= 6.1.5) + actionmailbox (= 6.1.5) + actionmailer (= 6.1.5) + actionpack (= 6.1.5) + actiontext (= 6.1.5) + actionview (= 6.1.5) + activejob (= 6.1.5) + activemodel (= 6.1.5) + activerecord (= 6.1.5) + activestorage (= 6.1.5) + activesupport (= 6.1.5) bundler (>= 1.15.0) - railties (= 6.1.3.2) + railties (= 6.1.5) sprockets-rails (>= 2.0.0) rails-controller-testing (1.0.5) actionpack (>= 5.0.1.rc1) @@ -470,52 +512,50 @@ GEM rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) - rails-html-sanitizer (1.3.0) + rails-html-sanitizer (1.4.2) loofah (~> 2.3) rails-i18n (6.0.0) i18n (>= 0.7, < 2) railties (>= 6.0.0, < 7) rails-settings-cached (0.6.6) rails (>= 4.2.0) - railties (6.1.3.2) - actionpack (= 6.1.3.2) - activesupport (= 6.1.3.2) + railties (6.1.5) + actionpack (= 6.1.5) + activesupport (= 6.1.5) method_source - rake (>= 0.8.7) + rake (>= 12.2) thor (~> 1.0) - rainbow (3.0.0) - rake (13.0.3) - rdf (3.1.13) - hamster (~> 3.0) + rainbow (3.1.1) + rake (13.0.6) + rdf (3.2.3) link_header (~> 0.0, >= 0.0.8) - rdf-normalize (0.4.0) - rdf (~> 3.1) - redis (4.2.5) - redis-namespace (1.8.1) + rdf-normalize (0.5.0) + rdf (~> 3.2) + redis (4.5.1) + redis-namespace (1.8.2) redis (>= 3.0.4) - regexp_parser (2.1.1) - request_store (1.5.0) + regexp_parser (2.2.1) + request_store (1.5.1) rack (>= 1.4) - resolv (0.1.0) responders (3.0.1) actionpack (>= 5.0) railties (>= 5.0) rexml (3.2.5) rotp (6.2.0) rpam2 (4.0.2) - rqrcode (2.0.0) + rqrcode (2.1.1) chunky_png (~> 1.0) rqrcode_core (~> 1.0) - rqrcode_core (1.0.0) - rspec-core (3.10.1) - rspec-support (~> 3.10.0) - rspec-expectations (3.10.1) + rqrcode_core (1.2.0) + rspec-core (3.11.0) + rspec-support (~> 3.11.0) + rspec-expectations (3.11.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.10.0) - rspec-mocks (3.10.2) + rspec-support (~> 3.11.0) + rspec-mocks (3.11.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.10.0) - rspec-rails (5.0.1) + rspec-support (~> 3.11.0) + rspec-rails (5.1.1) actionpack (>= 5.2) activesupport (>= 5.2) railties (>= 5.2) @@ -526,21 +566,21 @@ GEM rspec-sidekiq (3.1.0) rspec-core (~> 3.0, >= 3.0.0) sidekiq (>= 2.4.0) - rspec-support (3.10.2) - rspec_junit_formatter (0.4.1) + rspec-support (3.11.0) + rspec_junit_formatter (0.5.1) rspec-core (>= 2, < 4, != 2.12.0) - rubocop (1.15.0) + rubocop (1.26.1) parallel (~> 1.10) - parser (>= 3.0.0.0) + parser (>= 3.1.0.0) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.8, < 3.0) rexml - rubocop-ast (>= 1.5.0, < 2.0) + rubocop-ast (>= 1.16.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 3.0) - rubocop-ast (1.5.0) - parser (>= 3.0.1.1) - rubocop-rails (2.10.1) + rubocop-ast (1.16.0) + parser (>= 3.1.1.0) + rubocop-rails (2.14.2) activesupport (>= 4.2.0) rack (>= 1.1) rubocop (>= 1.7.0, < 2.0) @@ -548,38 +588,37 @@ GEM ruby-saml (1.13.0) nokogiri (>= 1.10.5) rexml - ruby2_keywords (0.0.4) - rufus-scheduler (3.6.0) + ruby2_keywords (0.0.5) + rufus-scheduler (3.8.1) fugit (~> 1.1, >= 1.1.6) safety_net_attestation (0.4.0) jwt (~> 2.0) - sanitize (5.2.3) + sanitize (6.0.0) crass (~> 1.0.2) - nokogiri (>= 1.8.0) - nokogumbo (~> 2.0) - scenic (1.5.4) + nokogiri (>= 1.12.0) + scenic (1.6.0) activerecord (>= 4.0.0) railties (>= 4.0.0) securecompare (1.0.0) semantic_range (3.0.0) - sidekiq (6.2.1) + sidekiq (6.4.1) connection_pool (>= 2.2.2) rack (~> 2.0) redis (>= 4.2.0) sidekiq-bulk (0.2.0) sidekiq - sidekiq-scheduler (3.0.1) + sidekiq-scheduler (3.1.1) e2mmap redis (>= 3, < 5) rufus-scheduler (~> 3.2) sidekiq (>= 3) thwait tilt (>= 1.4.0) - sidekiq-unique-jobs (7.0.11) + sidekiq-unique-jobs (7.1.16) brpoplpush-redis_script (> 0.1.1, <= 2.0.0) concurrent-ruby (~> 1.0, >= 1.0.5) - sidekiq (>= 5.0, < 7.0) - thor (>= 0.20, < 2.0) + sidekiq (>= 5.0, < 8.0) + thor (>= 0.20, < 3.0) simple-navigation (4.3.0) activesupport (>= 2.3.2) simple_form (5.1.0) @@ -591,31 +630,35 @@ GEM simplecov_json_formatter (~> 0.1) simplecov-html (0.12.3) simplecov_json_formatter (0.1.2) + smart_properties (1.17.0) sprockets (3.7.2) concurrent-ruby (~> 1.0) rack (> 1, < 3) - sprockets-rails (3.2.2) - actionpack (>= 4.0) - activesupport (>= 4.0) + sprockets-rails (3.4.2) + actionpack (>= 5.2) + activesupport (>= 5.2) sprockets (>= 3.0.0) sshkit (1.21.2) net-scp (>= 1.1.2) net-ssh (>= 2.8.0) - stackprof (0.2.17) + stackprof (0.2.19) statsd-ruby (1.5.0) stoplight (2.2.1) - strong_migrations (0.7.6) + strong_migrations (0.7.9) activerecord (>= 5) + swd (1.3.0) + activesupport (>= 3) + attr_required (>= 0.0.5) + httpclient (>= 2.4) temple (0.8.2) - terminal-table (3.0.0) - unicode-display_width (~> 1.1, >= 1.1.1) + terminal-table (3.0.2) + unicode-display_width (>= 1.1.1, < 3) terrapin (0.6.0) climate_control (>= 0.0.3, < 1.0) - thor (1.1.0) + thor (1.2.1) thwait (0.2.0) e2mmap tilt (2.0.10) - time (0.1.0) tpm-key_attestation (0.9.0) bindata (~> 2.4) openssl-signature_algorithm (~> 0.4.0) @@ -634,13 +677,19 @@ GEM unf (~> 0.1.0) tzinfo (2.0.4) concurrent-ruby (~> 1.0) - tzinfo-data (1.2021.1) + tzinfo-data (1.2022.1) tzinfo (>= 1.0.0) unf (0.1.4) unf_ext - unf_ext (0.0.7.7) - unicode-display_width (1.7.0) - uniform_notifier (1.14.1) + unf_ext (0.0.8) + unicode-display_width (2.1.0) + uniform_notifier (1.14.2) + validate_email (0.1.6) + activemodel (>= 3.0) + mail (>= 2.2.5) + validate_url (1.0.13) + activemodel (>= 3.0.0) + public_suffix warden (1.2.9) rack (>= 2.0.9) webauthn (3.0.0.alpha1) @@ -653,11 +702,14 @@ GEM safety_net_attestation (~> 0.4.0) securecompare (~> 1.0) tpm-key_attestation (~> 0.9.0) - webmock (3.13.0) - addressable (>= 2.3.6) + webfinger (1.2.0) + activesupport + httpclient (>= 2.4) + webmock (3.14.0) + addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) - webpacker (5.4.0) + webpacker (5.4.3) activesupport (>= 5.2) rack-proxy (>= 0.6.1) railties (>= 5.2) @@ -665,14 +717,14 @@ GEM webpush (0.3.8) hkdf (~> 0.2) jwt (~> 2.0) - websocket-driver (0.7.3) + websocket-driver (0.7.5) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) wisper (2.0.1) xorcist (1.1.2) xpath (3.2.0) nokogiri (~> 1.8) - zeitwerk (2.4.2) + zeitwerk (2.5.4) PLATFORMS ruby @@ -680,25 +732,24 @@ PLATFORMS DEPENDENCIES active_model_serializers (~> 0.10) active_record_query_trace (~> 1.8) - addressable (~> 2.7) - annotate (~> 3.1) - aws-sdk-s3 (~> 1.95) + addressable (~> 2.8) + annotate (~> 3.2) + aws-sdk-s3 (~> 1.113) better_errors (~> 2.9) binding_of_caller (~> 1.0) blurhash (~> 0.1) - bootsnap (~> 1.6.0) - brakeman (~> 5.0) + bootsnap (~> 1.10.3) + brakeman (~> 5.2) browser - bullet (~> 6.1) - bundler-audit (~> 0.8) - capistrano (~> 3.16) + bullet (~> 7.0) + bundler-audit (~> 0.9) + capistrano (~> 3.17) capistrano-rails (~> 1.6) capistrano-rbenv (~> 2.2) capistrano-yarn (~> 2.0) - capybara (~> 3.35) + capybara (~> 3.36) charlock_holmes (~> 0.7.7) - chewy (~> 5.2) - cld3 (~> 3.4.2) + chewy (~> 7.2) climate_control (~> 0.2) color_diff (~> 0.1) concurrent-ruby @@ -709,49 +760,47 @@ DEPENDENCIES discard (~> 1.2) doorkeeper (~> 5.5) dotenv-rails (~> 2.7) - ed25519 (~> 1.2) - fabrication (~> 2.22) - faker (~> 2.18) + ed25519 (~> 1.3) + fabrication (~> 2.28) + faker (~> 2.20) fast_blank (~> 1.0) fastimage fog-core (<= 2.1.0) fog-openstack (~> 0.3) fuubar (~> 2.5) + gitlab-omniauth-openid-connect (~> 0.9.1) hamlit-rails (~> 0.2) hiredis (~> 0.6) htmlentities (~> 4.3) - http (~> 4.4) + http (~> 5.0) http_accept_language (~> 2.1) httplog (~> 1.5.0) - i18n-tasks (~> 0.9) + i18n-tasks (~> 1.0) idn-ruby - iso-639 json-ld - json-ld-preloaded (~> 3.1) + json-ld-preloaded (~> 3.2) kaminari (~> 1.2) - letter_opener (~> 1.7) - letter_opener_web (~> 1.4) + kt-paperclip (~> 7.1) + letter_opener (~> 1.8) + letter_opener_web (~> 2.0) link_header (~> 0.0) - lograge (~> 0.11) + lograge (~> 0.12) makara (~> 0.5) mario-redis-lock (~> 1.2) memory_profiler microformats (~> 4.2) - mime-types (~> 3.3.1) + mime-types (~> 3.4.1) net-ldap (~> 0.17) - nokogiri (~> 1.11) + nokogiri (~> 1.13) nsa (~> 0.2) - oj (~> 3.11) + oj (~> 3.13) omniauth (~> 1.9) omniauth-cas (~> 2.0) omniauth-rails_csrf_protection (~> 0.1) omniauth-saml (~> 1.10) ox (~> 2.14) - paperclip (~> 6.0) - parallel (~> 1.20) - parallel_tests (~> 3.7) parslet - pg (~> 1.2) + pg (~> 1.3) pghero (~> 2.8) pkg-config (~> 1.4) posix-spawn @@ -759,46 +808,46 @@ DEPENDENCIES private_address_check (~> 0.5) pry-byebug (~> 3.9) pry-rails (~> 0.3) - puma (~> 5.3) - pundit (~> 2.1) + puma (~> 5.6) + pundit (~> 2.2) rack (~> 2.2.3) - rack-attack (~> 6.5) + rack-attack (~> 6.6) rack-cors (~> 1.1) - rails (~> 6.1.3) + rails (~> 6.1.5) rails-controller-testing (~> 1.0) rails-i18n (~> 6.0) rails-settings-cached (~> 0.6) - rdf-normalize (~> 0.4) - redis (~> 4.2) + rdf-normalize (~> 0.5) + redis (~> 4.5) redis-namespace (~> 1.8) - resolv (~> 0.1.0) - rqrcode (~> 2.0) - rspec-rails (~> 5.0) + rexml (~> 3.2) + rqrcode (~> 2.1) + rspec-rails (~> 5.1) rspec-sidekiq (~> 3.1) - rspec_junit_formatter (~> 0.4) - rubocop (~> 1.15) - rubocop-rails (~> 2.10) + rspec_junit_formatter (~> 0.5) + rubocop (~> 1.26) + rubocop-rails (~> 2.14) ruby-progressbar (~> 1.11) - sanitize (~> 5.2) - scenic (~> 1.5) - sidekiq (~> 6.2) + sanitize (~> 6.0) + scenic (~> 1.6) + sidekiq (~> 6.4) sidekiq-bulk (~> 0.2.0) - sidekiq-scheduler (~> 3.0) - sidekiq-unique-jobs (~> 7.0) + sidekiq-scheduler (~> 3.1) + sidekiq-unique-jobs (~> 7.1) simple-navigation (~> 4.3) simple_form (~> 5.1) simplecov (~> 0.21) sprockets (~> 3.7.2) - sprockets-rails (~> 3.2) + sprockets-rails (~> 3.4) stackprof stoplight (~> 2.2.1) strong_migrations (~> 0.7) - thor (~> 1.1) + thor (~> 1.2) tty-prompt (~> 0.23) twitter-text (~> 3.1.0) - tzinfo-data (~> 1.2021) + tzinfo-data (~> 1.2022) webauthn (~> 3.0.0.alpha1) - webmock (~> 3.13) + webmock (~> 3.14) webpacker (~> 5.4) webpush (~> 0.3) xorcist (~> 1.1) diff --git a/README.md b/README.md index 9c83fa3d4fae3f..4b48e071d8f641 100644 --- a/README.md +++ b/README.md @@ -3,13 +3,13 @@ [![GitHub release](https://img.shields.io/github/release/mastodon/mastodon.svg)][releases] [![Build Status](https://img.shields.io/circleci/project/github/mastodon/mastodon.svg)][circleci] -[![Code Climate](https://img.shields.io/codeclimate/maintainability/tootsuite/mastodon.svg)][code_climate] +[![Code Climate](https://img.shields.io/codeclimate/maintainability/mastodon/mastodon.svg)][code_climate] [![Crowdin](https://d322cqt584bo4o.cloudfront.net/mastodon/localized.svg)][crowdin] [![Docker Pulls](https://img.shields.io/docker/pulls/tootsuite/mastodon.svg)][docker] [releases]: https://github.com/mastodon/mastodon/releases [circleci]: https://circleci.com/gh/mastodon/mastodon -[code_climate]: https://codeclimate.com/github/tootsuite/mastodon +[code_climate]: https://codeclimate.com/github/mastodon/mastodon [crowdin]: https://crowdin.com/project/mastodon [docker]: https://hub.docker.com/r/tootsuite/mastodon/ @@ -28,7 +28,7 @@ Click below to **learn more** in a video: - [View sponsors](https://joinmastodon.org/sponsors) - [Blog](https://blog.joinmastodon.org) - [Documentation](https://docs.joinmastodon.org) -- [Browse Mastodon servers](https://joinmastodon.org/#getting-started) +- [Browse Mastodon servers](https://joinmastodon.org/communities) - [Browse Mastodon apps](https://joinmastodon.org/apps) [patreon]: https://www.patreon.com/mastodon @@ -37,56 +37,62 @@ Click below to **learn more** in a video: -**No vendor lock-in: Fully interoperable with any conforming platform** +### No vendor lock-in: Fully interoperable with any conforming platform -It doesn't have to be Mastodon, whatever implements ActivityPub is part of the social network! [Learn more](https://blog.joinmastodon.org/2018/06/why-activitypub-is-the-future/) +It doesn't have to be Mastodon; whatever implements ActivityPub is part of the social network! [Learn more](https://blog.joinmastodon.org/2018/06/why-activitypub-is-the-future/) -**Real-time, chronological timeline updates** +### Real-time, chronological timeline updates -See the updates of people you're following appear in real-time in the UI via WebSockets. There's a firehose view as well! +Updates of people you're following appear in real-time in the UI via WebSockets. There's a firehose view as well! -**Media attachments like images and short videos** +### Media attachments like images and short videos -Upload and view images and WebM/MP4 videos attached to the updates. Videos with no audio track are treated like GIFs; normal videos are looped - like vines! +Upload and view images and WebM/MP4 videos attached to the updates. Videos with no audio track are treated like GIFs; normal videos loop continuously! -**Safety and moderation tools** +### Safety and moderation tools -Private posts, locked accounts, phrase filtering, muting, blocking and all sorts of other features, along with a reporting and moderation system. [Learn more](https://blog.joinmastodon.org/2018/07/cage-the-mastodon/) +Mastodon includes private posts, locked accounts, phrase filtering, muting, blocking and all sorts of other features, along with a reporting and moderation system. [Learn more](https://blog.joinmastodon.org/2018/07/cage-the-mastodon/) -**OAuth2 and a straightforward REST API** +### OAuth2 and a straightforward REST API -Mastodon acts as an OAuth2 provider so 3rd party apps can use the REST and Streaming APIs, resulting in a rich app ecosystem with a lot of choices! +Mastodon acts as an OAuth2 provider, so 3rd party apps can use the REST and Streaming APIs. This results in a rich app ecosystem with a lot of choices! ## Deployment -**Tech stack:** +### Tech stack: - **Ruby on Rails** powers the REST API and other web pages - **React.js** and Redux are used for the dynamic parts of the interface - **Node.js** powers the streaming API -**Requirements:** +### Requirements: - **PostgreSQL** 9.5+ - **Redis** 4+ - **Ruby** 2.5+ - **Node.js** 12+ -The repository includes deployment configurations for **Docker and docker-compose**, but also a few specific platforms like **Heroku**, **Scalingo**, and **Nanobox**. The [**stand-alone** installation guide](https://docs.joinmastodon.org/admin/install/) is available in the documentation. +The repository includes deployment configurations for **Docker and docker-compose** as well as specific platforms like **Heroku**, **Scalingo**, and **Nanobox**. The [**standalone** installation guide](https://docs.joinmastodon.org/admin/install/) is available in the documentation. -A **Vagrant** configuration is included for development purposes. +A **Vagrant** configuration is included for development purposes. To use it, complete following steps: + +- Install Vagrant and Virtualbox +- Install the `vagrant-hostsupdater` plugin: `vagrant plugin install vagrant-hostsupdater` +- Run `vagrant up` +- Run `vagrant ssh -c "cd /vagrant && foreman start"` +- Open `http://mastodon.local` in your browser ## Contributing Mastodon is **free, open-source software** licensed under **AGPLv3**. -You can open issues for bugs you've found or features you think are missing. You can also submit pull requests to this repository, or submit translations using Crowdin. To get started, take a look at [CONTRIBUTING.md](CONTRIBUTING.md). If your contributions are accepted into Mastodon, you can request to be paid through [our OpenCollective](https://opencollective.com/mastodon). +You can open issues for bugs you've found or features you think are missing. You can also submit pull requests to this repository or submit translations using Crowdin. To get started, take a look at [CONTRIBUTING.md](CONTRIBUTING.md). If your contributions are accepted into Mastodon, you can request to be paid through [our OpenCollective](https://opencollective.com/mastodon). **IRC channel**: #mastodon on irc.libera.chat ## License -Copyright (C) 2016-2021 Eugen Rochko & other Mastodon contributors (see [AUTHORS.md](AUTHORS.md)) +Copyright (C) 2016-2022 Eugen Rochko & other Mastodon contributors (see [AUTHORS.md](AUTHORS.md)) This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. diff --git a/SECURITY.md b/SECURITY.md index 9d351fce61891d..12f50ed8806883 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,13 +1,20 @@ # Security Policy +If you believe you've identified a security vulnerability in Mastodon (a bug that allows something to happen that shouldn't be possible), you should submit the report through our [Bug Bounty Program][bug-bounty]. Alternatively, you can reach us at . + +You should *not* report such issues on GitHub or in other public spaces to give us time to publish a fix for the issue without exposing Mastodon's users to increased risk. + +## Scope + +A "vulnerability in Mastodon" is a vulnerability in the code distributed through our main source code repository on GitHub. Vulnerabilities that are specific to a given installation (e.g. misconfiguration) should be reported to the owner of that installation and not us. + ## Supported Versions | Version | Supported | | ------- | ------------------ | -| 3.4.x | :white_check_mark: | -| 3.3.x | :white_check_mark: | -| < 3.3 | :x: | - -## Reporting a Vulnerability +| 3.5.x | Yes | +| 3.4.x | Yes | +| 3.3.x | Yes | +| < 3.3 | No | -hello@joinmastodon.org +[bug-bounty]: https://app.intigriti.com/programs/mastodon/mastodonio/detail diff --git a/Vagrantfile b/Vagrantfile index 4d0cc0f762122b..3e73d9e470aa2b 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -12,7 +12,7 @@ curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - sudo apt-add-repository 'deb https://dl.yarnpkg.com/debian/ stable main' # Add repo for NodeJS -curl -sL https://deb.nodesource.com/setup_12.x | sudo bash - +curl -sL https://deb.nodesource.com/setup_14.x | sudo bash - # Add firewall rule to redirect 80 to PORT and save sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port #{ENV["PORT"]} @@ -33,11 +33,9 @@ sudo apt-get install \ redis-tools \ postgresql \ postgresql-contrib \ - protobuf-compiler \ yarn \ libicu-dev \ libidn11-dev \ - libprotobuf-dev \ libreadline-dev \ libpam0g-dev \ -y @@ -45,16 +43,8 @@ sudo apt-get install \ # Install rvm read RUBY_VERSION < .ruby-version -gpg_command="gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB" -$($gpg_command) -if [ $? -ne 0 ];then - echo "GPG command failed, This prevented RVM from installing." - echo "Retrying once..." && $($gpg_command) - if [ $? -ne 0 ];then - echo "GPG failed for the second time, please ensure network connectivity." - echo "Exiting..." && exit 1 - fi -fi +curl -sSL https://rvm.io/mpapis.asc | gpg --import +curl -sSL https://rvm.io/pkuczynski.asc | gpg --import curl -sSL https://raw.githubusercontent.com/rvm/rvm/stable/binscripts/rvm-installer | bash -s stable --ruby=$RUBY_VERSION source /home/vagrant/.rvm/scripts/rvm diff --git a/app.json b/app.json index 6b436538348ca1..c694908c539fb1 100644 --- a/app.json +++ b/app.json @@ -95,8 +95,5 @@ "scripts": { "postdeploy": "bundle exec rails db:migrate && bundle exec rails db:seed" }, - "addons": [ - "heroku-postgresql", - "heroku-redis" - ] + "addons": ["heroku-postgresql", "heroku-redis"] } diff --git a/app/chewy/accounts_index.rb b/app/chewy/accounts_index.rb index b814e009e5f779..6f9ea76e9a933b 100644 --- a/app/chewy/accounts_index.rb +++ b/app/chewy/accounts_index.rb @@ -23,21 +23,21 @@ class AccountsIndex < Chewy::Index }, } - define_type ::Account.searchable.includes(:account_stat), delete_if: ->(account) { account.destroyed? || !account.searchable? } do - root date_detection: false do - field :id, type: 'long' + index_scope ::Account.searchable.includes(:account_stat), delete_if: ->(account) { account.destroyed? || !account.searchable? } - field :display_name, type: 'text', analyzer: 'content' do - field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'content' - end + root date_detection: false do + field :id, type: 'long' - field :acct, type: 'text', analyzer: 'content', value: ->(account) { [account.username, account.domain].compact.join('@') } do - field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'content' - end + field :display_name, type: 'text', analyzer: 'content' do + field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'content' + end - field :following_count, type: 'long', value: ->(account) { account.following.local.count } - field :followers_count, type: 'long', value: ->(account) { account.followers.local.count } - field :last_status_at, type: 'date', value: ->(account) { account.last_status_at || account.created_at } + field :acct, type: 'text', analyzer: 'content', value: ->(account) { [account.username, account.domain].compact.join('@') } do + field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'content' end + + field :following_count, type: 'long', value: ->(account) { account.following.local.count } + field :followers_count, type: 'long', value: ->(account) { account.followers.local.count } + field :last_status_at, type: 'date', value: ->(account) { account.last_status_at || account.created_at } end end diff --git a/app/chewy/statuses_index.rb b/app/chewy/statuses_index.rb index 47cb856ea944a4..1381a96ed44908 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 FormattingHelper + settings index: { refresh_interval: '15m' }, analysis: { filter: { english_stop: { @@ -31,36 +33,36 @@ class StatusesIndex < Chewy::Index }, } - define_type ::Status.unscoped.kept.without_reblogs.includes(:media_attachments, :preloadable_poll) do - crutch :mentions do |collection| - data = ::Mention.where(status_id: collection.map(&:id)).where(account: Account.local, silent: false).pluck(:status_id, :account_id) - data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) } - end + index_scope ::Status.unscoped.kept.without_reblogs.includes(:media_attachments, :preloadable_poll) - crutch :favourites do |collection| - data = ::Favourite.where(status_id: collection.map(&:id)).where(account: Account.local).pluck(:status_id, :account_id) - data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) } - end + crutch :mentions do |collection| + data = ::Mention.where(status_id: collection.map(&:id)).where(account: Account.local, silent: false).pluck(:status_id, :account_id) + data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) } + end - crutch :reblogs do |collection| - data = ::Status.where(reblog_of_id: collection.map(&:id)).where(account: Account.local).pluck(:reblog_of_id, :account_id) - data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) } - end + crutch :favourites do |collection| + data = ::Favourite.where(status_id: collection.map(&:id)).where(account: Account.local).pluck(:status_id, :account_id) + data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) } + end - crutch :bookmarks do |collection| - data = ::Bookmark.where(status_id: collection.map(&:id)).where(account: Account.local).pluck(:status_id, :account_id) - data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) } - end + crutch :reblogs do |collection| + data = ::Status.where(reblog_of_id: collection.map(&:id)).where(account: Account.local).pluck(:reblog_of_id, :account_id) + data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) } + end - root date_detection: false do - field :id, type: 'long' - field :account_id, type: 'long' + crutch :bookmarks do |collection| + data = ::Bookmark.where(status_id: collection.map(&:id)).where(account: Account.local).pluck(:status_id, :account_id) + data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) } + end - field :text, type: 'text', value: ->(status) { [status.spoiler_text, Formatter.instance.plaintext(status)].concat(status.media_attachments.map(&:description)).concat(status.preloadable_poll ? status.preloadable_poll.options : []).join("\n\n") } do - field :stemmed, type: 'text', analyzer: 'content' - end + root date_detection: false do + field :id, type: 'long' + field :account_id, type: 'long' - field :searchable_by, type: 'long', value: ->(status, crutches) { status.searchable_by(crutches) } + field :text, type: 'text', value: ->(status) { status.searchable_text } do + field :stemmed, type: 'text', analyzer: 'content' end + + field :searchable_by, type: 'long', value: ->(status, crutches) { status.searchable_by(crutches) } end end diff --git a/app/chewy/tags_index.rb b/app/chewy/tags_index.rb index 300fc128f63f0f..f9db2b03af1724 100644 --- a/app/chewy/tags_index.rb +++ b/app/chewy/tags_index.rb @@ -23,15 +23,15 @@ class TagsIndex < Chewy::Index }, } - define_type ::Tag.listable, delete_if: ->(tag) { tag.destroyed? || !tag.listable? } do - root date_detection: false do - field :name, type: 'text', analyzer: 'content' do - field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'content' - end + index_scope ::Tag.listable, delete_if: ->(tag) { tag.destroyed? || !tag.listable? } - field :reviewed, type: 'boolean', value: ->(tag) { tag.reviewed? } - field :usage, type: 'long', value: ->(tag) { tag.history.reduce(0) { |total, day| total + day[:accounts].to_i } } - field :last_status_at, type: 'date', value: ->(tag) { tag.last_status_at || tag.created_at } + root date_detection: false do + field :name, type: 'text', analyzer: 'content' do + field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'content' end + + field :reviewed, type: 'boolean', value: ->(tag) { tag.reviewed? } + field :usage, type: 'long', value: ->(tag) { tag.history.reduce(0) { |total, day| total + day.accounts } } + field :last_status_at, type: 'date', value: ->(tag) { tag.last_status_at || tag.created_at } end end diff --git a/app/controllers/accounts_controller.rb b/app/controllers/accounts_controller.rb index 8210918d869c10..ddd38cbb01f702 100644 --- a/app/controllers/accounts_controller.rb +++ b/app/controllers/accounts_controller.rb @@ -28,7 +28,7 @@ def show return end - @pinned_statuses = cache_collection(@account.pinned_statuses, Status) if show_pinned_statuses? + @pinned_statuses = cached_filtered_status_pins if show_pinned_statuses? @statuses = cached_filtered_status_page @rss_url = rss_url @@ -64,6 +64,10 @@ def show_pinned_statuses? [replies_requested?, media_requested?, tag_requested?, params[:max_id].present?, params[:min_id].present?].none? end + def filtered_pinned_statuses + @account.pinned_statuses.where(visibility: [:public, :unlisted]) + end + def filtered_statuses default_statuses.tap do |statuses| statuses.merge!(hashtag_scope) if tag_requested? @@ -142,6 +146,13 @@ def tag_requested? request.path.split('.').first.end_with?(Addressable::URI.parse("/tagged/#{params[:tag]}").normalize) end + def cached_filtered_status_pins + cache_collection( + filtered_pinned_statuses, + Status + ) + end + def cached_filtered_status_page cache_collection_paginated_by_id( filtered_statuses, diff --git a/app/controllers/activitypub/base_controller.rb b/app/controllers/activitypub/base_controller.rb index 4cbc3ab8f209ad..196d85a32643e3 100644 --- a/app/controllers/activitypub/base_controller.rb +++ b/app/controllers/activitypub/base_controller.rb @@ -2,6 +2,7 @@ class ActivityPub::BaseController < Api::BaseController skip_before_action :require_authenticated_user! + skip_around_action :set_locale private diff --git a/app/controllers/activitypub/collections_controller.rb b/app/controllers/activitypub/collections_controller.rb index c8b6dcc88d4fb9..e4e994a9855c3b 100644 --- a/app/controllers/activitypub/collections_controller.rb +++ b/app/controllers/activitypub/collections_controller.rb @@ -21,6 +21,7 @@ def set_items case params[:id] when 'featured' @items = for_signed_account { cache_collection(@account.pinned_statuses, Status) } + @items = @items.map { |item| item.distributable? ? item : ActivityPub::TagManager.instance.uri_for(item) } when 'tags' @items = for_signed_account { @account.featured_tags } when 'devices' diff --git a/app/controllers/activitypub/outboxes_controller.rb b/app/controllers/activitypub/outboxes_controller.rb index b2aab56a56f253..cd3992502d7354 100644 --- a/app/controllers/activitypub/outboxes_controller.rb +++ b/app/controllers/activitypub/outboxes_controller.rb @@ -62,7 +62,7 @@ def set_statuses return unless page_requested? @statuses = cache_collection_paginated_by_id( - @account.statuses.permitted_for(@account, signed_request_account), + AccountStatusesFilter.new(@account, signed_request_account).results, Status, LIMIT, params_slice(:max_id, :min_id, :since_id) diff --git a/app/controllers/activitypub/replies_controller.rb b/app/controllers/activitypub/replies_controller.rb index fde6c861f28619..4ff7cfa080f238 100644 --- a/app/controllers/activitypub/replies_controller.rb +++ b/app/controllers/activitypub/replies_controller.rb @@ -63,15 +63,29 @@ def only_other_accounts? end def next_page - only_other_accounts = !(@replies&.last&.account_id == @account.id && @replies.size == DESCENDANTS_LIMIT) - - account_status_replies_url( - @account, - @status, - page: true, - min_id: only_other_accounts && !only_other_accounts? ? nil : @replies&.last&.id, - only_other_accounts: only_other_accounts - ) + if only_other_accounts? + # Only consider remote accounts + return nil if @replies.size < DESCENDANTS_LIMIT + + account_status_replies_url( + @account, + @status, + page: true, + min_id: @replies&.last&.id, + only_other_accounts: true + ) + else + # For now, we're serving only self-replies, but next page might be other accounts + next_only_other_accounts = @replies&.last&.account_id != @account.id || @replies.size < DESCENDANTS_LIMIT + + account_status_replies_url( + @account, + @status, + page: true, + min_id: next_only_other_accounts ? nil : @replies&.last&.id, + only_other_accounts: next_only_other_accounts + ) + end end def page_params diff --git a/app/controllers/admin/account_moderation_notes_controller.rb b/app/controllers/admin/account_moderation_notes_controller.rb index 44f6e34f80b0ad..4f36f33f47e0fe 100644 --- a/app/controllers/admin/account_moderation_notes_controller.rb +++ b/app/controllers/admin/account_moderation_notes_controller.rb @@ -14,7 +14,7 @@ def create else @account = @account_moderation_note.target_account @moderation_notes = @account.targeted_moderation_notes.latest - @warnings = @account.targeted_account_warnings.latest.custom + @warnings = @account.strikes.custom.latest render template: 'admin/accounts/show' end diff --git a/app/controllers/admin/accounts_controller.rb b/app/controllers/admin/accounts_controller.rb index 1dd7430e098295..e0ae71b9f2feda 100644 --- a/app/controllers/admin/accounts_controller.rb +++ b/app/controllers/admin/accounts_controller.rb @@ -2,13 +2,24 @@ module Admin class AccountsController < BaseController - before_action :set_account, except: [:index] + before_action :set_account, except: [:index, :batch] before_action :require_remote_account!, only: [:redownload] before_action :require_local_account!, only: [:enable, :memorialize, :approve, :reject] def index authorize :account, :index? + @accounts = filtered_accounts.page(params[:page]) + @form = Form::AccountBatch.new + end + + def batch + @form = Form::AccountBatch.new(form_account_batch_params.merge(current_account: current_account, action: action_from_button)) + @form.save + rescue ActionController::ParameterMissing + flash[:alert] = I18n.t('admin.accounts.no_account_selected') + ensure + redirect_to admin_accounts_path(filter_params) end def show @@ -17,7 +28,7 @@ def show @deletion_request = @account.deletion_request @account_moderation_note = current_account.account_moderation_notes.new(target_account: @account) @moderation_notes = @account.targeted_moderation_notes.latest - @warnings = @account.targeted_account_warnings.latest.custom + @warnings = @account.strikes.includes(:target_account, :account, :appeal).latest @domain_block = DomainBlock.rule_for(@account.domain) end @@ -38,13 +49,13 @@ def enable def approve authorize @account.user, :approve? @account.user.approve! - redirect_to admin_pending_accounts_path, notice: I18n.t('admin.accounts.approved_msg', username: @account.acct) + redirect_to admin_accounts_path(status: 'pending'), notice: I18n.t('admin.accounts.approved_msg', username: @account.acct) end def reject authorize @account.user, :reject? DeleteAccountService.new.call(@account, reserve_email: false, reserve_username: false) - redirect_to admin_pending_accounts_path, notice: I18n.t('admin.accounts.rejected_msg', username: @account.acct) + redirect_to admin_accounts_path(status: 'pending'), notice: I18n.t('admin.accounts.rejected_msg', username: @account.acct) end def destroy @@ -106,6 +117,16 @@ def remove_header redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.removed_header_msg', username: @account.acct) end + def unblock_email + authorize @account, :unblock_email? + + CanonicalEmailBlock.where(reference_account: @account).delete_all + + log_action :unblock_email, @account + + redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.unblocked_email_msg', username: @account.acct) + end + private def set_account @@ -121,11 +142,25 @@ def require_local_account! end def filtered_accounts - AccountFilter.new(filter_params).results + AccountFilter.new(filter_params.with_defaults(order: 'recent')).results end def filter_params - params.slice(*AccountFilter::KEYS).permit(*AccountFilter::KEYS) + params.slice(:page, *AccountFilter::KEYS).permit(:page, *AccountFilter::KEYS) + end + + def form_account_batch_params + params.require(:form_account_batch).permit(:action, account_ids: []) + end + + def action_from_button + if params[:suspend] + 'suspend' + elsif params[:approve] + 'approve' + elsif params[:reject] + 'reject' + end end end end diff --git a/app/controllers/admin/dashboard_controller.rb b/app/controllers/admin/dashboard_controller.rb index c829ed98f87346..e376baab22aff5 100644 --- a/app/controllers/admin/dashboard_controller.rb +++ b/app/controllers/admin/dashboard_controller.rb @@ -1,49 +1,18 @@ # frozen_string_literal: true -require 'sidekiq/api' module Admin class DashboardController < BaseController def index @system_checks = Admin::SystemCheck.perform - @users_count = User.count + @time_period = (29.days.ago.to_date...Time.now.utc.to_date) @pending_users_count = User.pending.count - @registrations_week = Redis.current.get("activity:accounts:local:#{current_week}") || 0 - @logins_week = Redis.current.pfcount("activity:logins:#{current_week}") - @interactions_week = Redis.current.get("activity:interactions:#{current_week}") || 0 - @relay_enabled = Relay.enabled.exists? - @single_user_mode = Rails.configuration.x.single_user_mode - @registrations_enabled = Setting.registrations_mode != 'none' - @deletions_enabled = Setting.open_deletion - @invites_enabled = Setting.min_invite_role == 'user' - @search_enabled = Chewy.enabled? - @version = Mastodon::Version.to_s - @database_version = ActiveRecord::Base.connection.execute('SELECT VERSION()').first['version'].match(/\A(?:PostgreSQL |)([^\s]+).*\z/)[1] - @redis_version = redis_info['redis_version'] - @reports_count = Report.unresolved.count - @queue_backlog = Sidekiq::Stats.new.enqueued - @recent_users = User.confirmed.recent.includes(:account).limit(8) - @database_size = ActiveRecord::Base.connection.execute('SELECT pg_database_size(current_database())').first['pg_database_size'] - @redis_size = redis_info['used_memory'] - @ldap_enabled = ENV['LDAP_ENABLED'] == 'true' - @cas_enabled = ENV['CAS_ENABLED'] == 'true' - @saml_enabled = ENV['SAML_ENABLED'] == 'true' - @pam_enabled = ENV['PAM_ENABLED'] == 'true' - @hidden_service = ENV['ALLOW_ACCESS_TO_HIDDEN_SERVICE'] == 'true' - @trending_hashtags = TrendingTags.get(10, filtered: false) + @pending_reports_count = Report.unresolved.count @pending_tags_count = Tag.pending_review.count - @authorized_fetch = authorized_fetch_mode? - @whitelist_enabled = whitelist_mode? - @profile_directory = Setting.profile_directory - @timeline_preview = Setting.timeline_preview - @trends_enabled = Setting.trends + @pending_appeals_count = Appeal.pending.count end private - def current_week - @current_week ||= Time.now.utc.to_date.cweek - end - def redis_info @redis_info ||= begin if Redis.current.is_a?(Redis::Namespace) diff --git a/app/controllers/admin/disputes/appeals_controller.rb b/app/controllers/admin/disputes/appeals_controller.rb new file mode 100644 index 00000000000000..32e5e2f6fd82ed --- /dev/null +++ b/app/controllers/admin/disputes/appeals_controller.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +class Admin::Disputes::AppealsController < Admin::BaseController + before_action :set_appeal, except: :index + + def index + authorize :appeal, :index? + + @appeals = filtered_appeals.page(params[:page]) + end + + def approve + authorize @appeal, :approve? + log_action :approve, @appeal + ApproveAppealService.new.call(@appeal, current_account) + redirect_to disputes_strike_path(@appeal.strike) + end + + def reject + authorize @appeal, :approve? + log_action :reject, @appeal + @appeal.reject!(current_account) + UserMailer.appeal_rejected(@appeal.account.user, @appeal) + redirect_to disputes_strike_path(@appeal.strike) + end + + private + + def filtered_appeals + Admin::AppealFilter.new(filter_params.with_defaults(status: 'pending')).results.includes(strike: :account) + end + + def filter_params + params.slice(:page, *Admin::AppealFilter::KEYS).permit(:page, *Admin::AppealFilter::KEYS) + end + + def set_appeal + @appeal = Appeal.find(params[:id]) + end +end diff --git a/app/controllers/admin/domain_blocks_controller.rb b/app/controllers/admin/domain_blocks_controller.rb index b140c454c17718..16defc1ea87b6c 100644 --- a/app/controllers/admin/domain_blocks_controller.rb +++ b/app/controllers/admin/domain_blocks_controller.rb @@ -56,10 +56,6 @@ def update end end - def show - authorize @domain_block, :show? - end - def destroy authorize @domain_block, :destroy? UnblockDomainService.new.call(@domain_block) diff --git a/app/controllers/admin/email_domain_blocks_controller.rb b/app/controllers/admin/email_domain_blocks_controller.rb index f7bdfb0c5f6f8b..a4bbbba5bad2f5 100644 --- a/app/controllers/admin/email_domain_blocks_controller.rb +++ b/app/controllers/admin/email_domain_blocks_controller.rb @@ -6,7 +6,20 @@ class EmailDomainBlocksController < BaseController def index authorize :email_domain_block, :index? + @email_domain_blocks = EmailDomainBlock.where(parent_id: nil).includes(:children).order(id: :desc).page(params[:page]) + @form = Form::EmailDomainBlockBatch.new + end + + def batch + @form = Form::EmailDomainBlockBatch.new(form_email_domain_block_batch_params.merge(current_account: current_account, action: action_from_button)) + @form.save + rescue ActionController::ParameterMissing + flash[:alert] = I18n.t('admin.email_domain_blocks.no_email_domain_block_selected') + rescue Mastodon::NotPermittedError + flash[:alert] = I18n.t('admin.custom_emojis.not_permitted') + ensure + redirect_to admin_email_domain_blocks_path end def new @@ -19,41 +32,27 @@ def create @email_domain_block = EmailDomainBlock.new(resource_params) - if @email_domain_block.save - log_action :create, @email_domain_block - - if @email_domain_block.with_dns_records? - hostnames = [] - ips = [] - - Resolv::DNS.open do |dns| - dns.timeouts = 5 + if action_from_button == 'save' + EmailDomainBlock.transaction do + @email_domain_block.save! + log_action :create, @email_domain_block - hostnames = dns.getresources(@email_domain_block.domain, Resolv::DNS::Resource::IN::MX).to_a.map { |e| e.exchange.to_s } + (@email_domain_block.other_domains || []).uniq.each do |domain| + next if EmailDomainBlock.where(domain: domain).exists? - ([@email_domain_block.domain] + hostnames).uniq.each do |hostname| - ips.concat(dns.getresources(hostname, Resolv::DNS::Resource::IN::A).to_a.map { |e| e.address.to_s }) - ips.concat(dns.getresources(hostname, Resolv::DNS::Resource::IN::AAAA).to_a.map { |e| e.address.to_s }) - end - end - - (hostnames + ips).each do |hostname| - another_email_domain_block = EmailDomainBlock.new(domain: hostname, parent: @email_domain_block) - log_action :create, another_email_domain_block if another_email_domain_block.save + other_email_domain_block = EmailDomainBlock.create!(domain: domain, parent: @email_domain_block) + log_action :create, other_email_domain_block end end redirect_to admin_email_domain_blocks_path, notice: I18n.t('admin.email_domain_blocks.created_msg') else + set_resolved_records render :new end - end - - def destroy - authorize @email_domain_block, :destroy? - @email_domain_block.destroy! - log_action :destroy, @email_domain_block - redirect_to admin_email_domain_blocks_path, notice: I18n.t('admin.email_domain_blocks.destroyed_msg') + rescue ActiveRecord::RecordInvalid + set_resolved_records + render :new end private @@ -62,8 +61,27 @@ def set_email_domain_block @email_domain_block = EmailDomainBlock.find(params[:id]) end + def set_resolved_records + Resolv::DNS.open do |dns| + dns.timeouts = 5 + @resolved_records = dns.getresources(@email_domain_block.domain, Resolv::DNS::Resource::IN::MX).to_a + end + end + def resource_params - params.require(:email_domain_block).permit(:domain, :with_dns_records) + params.require(:email_domain_block).permit(:domain, other_domains: []) + end + + def form_email_domain_block_batch_params + params.require(:form_email_domain_block_batch).permit(email_domain_block_ids: []) + end + + def action_from_button + if params[:delete] + 'delete' + elsif params[:save] + 'save' + end end end end diff --git a/app/controllers/admin/instances_controller.rb b/app/controllers/admin/instances_controller.rb index 748c5de5add44f..5c82331dea2f78 100644 --- a/app/controllers/admin/instances_controller.rb +++ b/app/controllers/admin/instances_controller.rb @@ -4,19 +4,26 @@ module Admin class InstancesController < BaseController before_action :set_instances, only: :index before_action :set_instance, except: :index - before_action :set_exhausted_deliveries_days, only: :show def index authorize :instance, :index? + preload_delivery_failures! end def show authorize :instance, :show? + @time_period = (6.days.ago.to_date...Time.now.utc.to_date) + end + + def destroy + authorize :instance, :destroy? + Admin::DomainPurgeWorker.perform_async(@instance.domain) + log_action :destroy, @instance + redirect_to admin_instances_path, notice: I18n.t('admin.instances.destroyed_msg', domain: @instance.domain) end def clear_delivery_errors authorize :delivery, :clear_delivery_errors? - @instance.delivery_failure_tracker.clear_failures! redirect_to admin_instance_path(@instance.domain) end @@ -24,11 +31,9 @@ def clear_delivery_errors def restart_delivery authorize :delivery, :restart_delivery? - last_unavailable_domain = unavailable_domain - - if last_unavailable_domain.present? + if @instance.unavailable? @instance.delivery_failure_tracker.track_success! - log_action :destroy, last_unavailable_domain + log_action :destroy, @instance.unavailable_domain end redirect_to admin_instance_path(@instance.domain) @@ -36,8 +41,7 @@ def restart_delivery def stop_delivery authorize :delivery, :stop_delivery? - - UnavailableDomain.create(domain: @instance.domain) + unavailable_domain = UnavailableDomain.create!(domain: @instance.domain) log_action :create, unavailable_domain redirect_to admin_instance_path(@instance.domain) end @@ -48,12 +52,11 @@ def set_instance @instance = Instance.find(params[:id]) end - def set_exhausted_deliveries_days - @exhausted_deliveries_days = @instance.delivery_failure_tracker.exhausted_deliveries_days - end - def set_instances @instances = filtered_instances.page(params[:page]) + end + + def preload_delivery_failures! warning_domains_map = DeliveryFailureTracker.warning_domains_map @instances.each do |instance| @@ -61,10 +64,6 @@ def set_instances end end - def unavailable_domain - UnavailableDomain.find_by(domain: @instance.domain) - end - def filtered_instances InstanceFilter.new(whitelist_mode? ? { allowed: true } : filter_params).results end diff --git a/app/controllers/admin/pending_accounts_controller.rb b/app/controllers/admin/pending_accounts_controller.rb deleted file mode 100644 index b62a9bc8466af9..00000000000000 --- a/app/controllers/admin/pending_accounts_controller.rb +++ /dev/null @@ -1,52 +0,0 @@ -# frozen_string_literal: true - -module Admin - class PendingAccountsController < BaseController - before_action :set_accounts, only: :index - - def index - @form = Form::AccountBatch.new - end - - def batch - @form = Form::AccountBatch.new(form_account_batch_params.merge(current_account: current_account, action: action_from_button)) - @form.save - rescue ActionController::ParameterMissing - flash[:alert] = I18n.t('admin.accounts.no_account_selected') - ensure - redirect_to admin_pending_accounts_path(current_params) - end - - def approve_all - Form::AccountBatch.new(current_account: current_account, account_ids: User.pending.pluck(:account_id), action: 'approve').save - redirect_to admin_pending_accounts_path(current_params) - end - - def reject_all - Form::AccountBatch.new(current_account: current_account, account_ids: User.pending.pluck(:account_id), action: 'reject').save - redirect_to admin_pending_accounts_path(current_params) - end - - private - - def set_accounts - @accounts = Account.joins(:user).merge(User.pending.recent).includes(user: :invite_request).page(params[:page]) - end - - def form_account_batch_params - params.require(:form_account_batch).permit(:action, account_ids: []) - end - - def action_from_button - if params[:approve] - 'approve' - elsif params[:reject] - 'reject' - end - end - - def current_params - params.slice(:page).permit(:page) - end - end -end diff --git a/app/controllers/admin/relationships_controller.rb b/app/controllers/admin/relationships_controller.rb index f8a95cfc8f0f00..085ded21c03e13 100644 --- a/app/controllers/admin/relationships_controller.rb +++ b/app/controllers/admin/relationships_controller.rb @@ -9,7 +9,8 @@ class RelationshipsController < BaseController def index authorize :account, :index? - @accounts = RelationshipFilter.new(@account, filter_params).results.page(params[:page]).per(PER_PAGE) + @accounts = RelationshipFilter.new(@account, filter_params).results.includes(:account_stat, user: [:ips, :invite_request]).page(params[:page]).per(PER_PAGE) + @form = Form::AccountBatch.new end private diff --git a/app/controllers/admin/report_notes_controller.rb b/app/controllers/admin/report_notes_controller.rb index b816c5b5d484ec..3fd815b60a0b58 100644 --- a/app/controllers/admin/report_notes_controller.rb +++ b/app/controllers/admin/report_notes_controller.rb @@ -14,20 +14,17 @@ def create if params[:create_and_resolve] @report.resolve!(current_account) log_action :resolve, @report - - redirect_to admin_reports_path, notice: I18n.t('admin.reports.resolved_msg') - return - end - - if params[:create_and_unresolve] + elsif params[:create_and_unresolve] @report.unresolve! log_action :reopen, @report end - redirect_to admin_report_path(@report), notice: I18n.t('admin.report_notes.created_msg') + redirect_to after_create_redirect_path, notice: I18n.t('admin.report_notes.created_msg') else - @report_notes = (@report.notes.latest + @report.history + @report.target_account.targeted_account_warnings.latest.custom).sort_by(&:created_at) - @form = Form::StatusBatch.new + @report_notes = @report.notes.includes(:account).order(id: :desc) + @action_logs = @report.history.includes(:target) + @form = Admin::StatusBatchAction.new + @statuses = @report.statuses.with_includes render template: 'admin/reports/show' end @@ -41,6 +38,14 @@ def destroy private + def after_create_redirect_path + if params[:create_and_resolve] + admin_reports_path + else + admin_report_path(@report) + end + end + def resource_params params.require(:report_note).permit( :content, diff --git a/app/controllers/admin/reported_statuses_controller.rb b/app/controllers/admin/reported_statuses_controller.rb deleted file mode 100644 index 3ba9f5df21e8d4..00000000000000 --- a/app/controllers/admin/reported_statuses_controller.rb +++ /dev/null @@ -1,44 +0,0 @@ -# frozen_string_literal: true - -module Admin - class ReportedStatusesController < BaseController - before_action :set_report - - def create - authorize :status, :update? - - @form = Form::StatusBatch.new(form_status_batch_params.merge(current_account: current_account, action: action_from_button)) - flash[:alert] = I18n.t('admin.statuses.failed_to_execute') unless @form.save - - redirect_to admin_report_path(@report) - rescue ActionController::ParameterMissing - flash[:alert] = I18n.t('admin.statuses.no_status_selected') - - redirect_to admin_report_path(@report) - end - - private - - def status_params - params.require(:status).permit(:sensitive) - end - - def form_status_batch_params - params.require(:form_status_batch).permit(status_ids: []) - end - - def action_from_button - if params[:nsfw_on] - 'nsfw_on' - elsif params[:nsfw_off] - 'nsfw_off' - elsif params[:delete] - 'delete' - end - end - - def set_report - @report = Report.find(params[:report_id]) - end - end -end diff --git a/app/controllers/admin/reports/actions_controller.rb b/app/controllers/admin/reports/actions_controller.rb new file mode 100644 index 00000000000000..5cb5c744f29106 --- /dev/null +++ b/app/controllers/admin/reports/actions_controller.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +class Admin::Reports::ActionsController < Admin::BaseController + before_action :set_report + + def create + authorize @report, :show? + + case action_from_button + when 'delete', 'mark_as_sensitive' + status_batch_action = Admin::StatusBatchAction.new( + type: action_from_button, + status_ids: @report.status_ids, + current_account: current_account, + report_id: @report.id, + send_email_notification: !@report.spam? + ) + + status_batch_action.save! + when 'silence', 'suspend' + account_action = Admin::AccountAction.new( + type: action_from_button, + report_id: @report.id, + target_account: @report.target_account, + current_account: current_account, + send_email_notification: !@report.spam? + ) + + account_action.save! + end + + redirect_to admin_reports_path + end + + private + + def set_report + @report = Report.find(params[:report_id]) + end + + def action_from_button + if params[:delete] + 'delete' + elsif params[:mark_as_sensitive] + 'mark_as_sensitive' + elsif params[:silence] + 'silence' + elsif params[:suspend] + 'suspend' + end + end +end diff --git a/app/controllers/admin/reports_controller.rb b/app/controllers/admin/reports_controller.rb index 7c831b3d4c3830..00d200d7c88ccc 100644 --- a/app/controllers/admin/reports_controller.rb +++ b/app/controllers/admin/reports_controller.rb @@ -13,8 +13,10 @@ def show authorize @report, :show? @report_note = @report.notes.new - @report_notes = (@report.notes.latest + @report.history + @report.target_account.targeted_account_warnings.latest.custom).sort_by(&:created_at) - @form = Form::StatusBatch.new + @report_notes = @report.notes.includes(:account).order(id: :desc) + @action_logs = @report.history.includes(:target) + @form = Admin::StatusBatchAction.new + @statuses = @report.statuses.with_includes end def assign_to_self diff --git a/app/controllers/admin/resets_controller.rb b/app/controllers/admin/resets_controller.rb index db8f61d64cb13f..7962b7a58c6d4c 100644 --- a/app/controllers/admin/resets_controller.rb +++ b/app/controllers/admin/resets_controller.rb @@ -6,9 +6,9 @@ class ResetsController < BaseController def create authorize @user, :reset_password? - @user.send_reset_password_instructions + @user.reset_password! log_action :reset_password, @user - redirect_to admin_accounts_path + redirect_to admin_account_path(@user.account_id) end end end diff --git a/app/controllers/admin/statuses_controller.rb b/app/controllers/admin/statuses_controller.rb index 58a0eb84ca7f42..817c0caa9000b9 100644 --- a/app/controllers/admin/statuses_controller.rb +++ b/app/controllers/admin/statuses_controller.rb @@ -2,71 +2,62 @@ module Admin class StatusesController < BaseController - helper_method :current_params - before_action :set_account + before_action :set_statuses PER_PAGE = 20 def index authorize :status, :index? - @statuses = @account.statuses.where(visibility: [:public, :unlisted]) - - if params[:media] - @statuses.merge!(Status.joins(:media_attachments).merge(@account.media_attachments.reorder(nil)).group(:id)).reorder('statuses.id desc') - end - - @statuses = @statuses.preload(:media_attachments, :mentions).page(params[:page]).per(PER_PAGE) - @form = Form::StatusBatch.new - end - - def show - authorize :status, :index? - - @statuses = @account.statuses.where(id: params[:id]) - authorize @statuses.first, :show? - - @form = Form::StatusBatch.new + @status_batch_action = Admin::StatusBatchAction.new end - def create - authorize :status, :update? - - @form = Form::StatusBatch.new(form_status_batch_params.merge(current_account: current_account, action: action_from_button)) - flash[:alert] = I18n.t('admin.statuses.failed_to_execute') unless @form.save - - redirect_to admin_account_statuses_path(@account.id, current_params) + def batch + @status_batch_action = Admin::StatusBatchAction.new(admin_status_batch_action_params.merge(current_account: current_account, report_id: params[:report_id], type: action_from_button)) + @status_batch_action.save! rescue ActionController::ParameterMissing flash[:alert] = I18n.t('admin.statuses.no_status_selected') - - redirect_to admin_account_statuses_path(@account.id, current_params) + ensure + redirect_to after_create_redirect_path end private - def form_status_batch_params - params.require(:form_status_batch).permit(:action, status_ids: []) + def admin_status_batch_action_params + params.require(:admin_status_batch_action).permit(status_ids: []) + end + + def after_create_redirect_path + report_id = @status_batch_action&.report_id || params[:report_id] + if report_id.present? + admin_report_path(report_id) + else + admin_account_statuses_path(params[:account_id], current_params) + end end def set_account @account = Account.find(params[:account_id]) end - def current_params - page = (params[:page] || 1).to_i + def set_statuses + @statuses = Admin::StatusFilter.new(@account, filter_params).results.preload(:application, :preloadable_poll, :media_attachments, active_mentions: :account, reblog: [:account, :application, :preloadable_poll, :media_attachments, active_mentions: :account]).page(params[:page]).per(PER_PAGE) + end - { - media: params[:media], - page: page > 1 && page, - }.select { |_, value| value.present? } + def filter_params + params.slice(*Admin::StatusFilter::KEYS).permit(*Admin::StatusFilter::KEYS) + end + + def current_params + params.slice(:media, :page).permit(:media, :page) end def action_from_button - if params[:nsfw_on] - 'nsfw_on' - elsif params[:nsfw_off] - 'nsfw_off' + if params[:report] + 'report' + elsif params[:remove_from_report] + 'remove_from_report' elsif params[:delete] 'delete' end diff --git a/app/controllers/admin/tags_controller.rb b/app/controllers/admin/tags_controller.rb index eed4feea2f5300..749e2f144d3a2b 100644 --- a/app/controllers/admin/tags_controller.rb +++ b/app/controllers/admin/tags_controller.rb @@ -2,38 +2,12 @@ module Admin class TagsController < BaseController - before_action :set_tag, except: [:index, :batch, :approve_all, :reject_all] - before_action :set_usage_by_domain, except: [:index, :batch, :approve_all, :reject_all] - before_action :set_counters, except: [:index, :batch, :approve_all, :reject_all] - - def index - authorize :tag, :index? - - @tags = filtered_tags.page(params[:page]) - @form = Form::TagBatch.new - end - - def batch - @form = Form::TagBatch.new(form_tag_batch_params.merge(current_account: current_account, action: action_from_button)) - @form.save - rescue ActionController::ParameterMissing - flash[:alert] = I18n.t('admin.accounts.no_account_selected') - ensure - redirect_to admin_tags_path(filter_params) - end - - def approve_all - Form::TagBatch.new(current_account: current_account, tag_ids: Tag.pending_review.pluck(:id), action: 'approve').save - redirect_to admin_tags_path(filter_params) - end - - def reject_all - Form::TagBatch.new(current_account: current_account, tag_ids: Tag.pending_review.pluck(:id), action: 'reject').save - redirect_to admin_tags_path(filter_params) - end + before_action :set_tag def show authorize @tag, :show? + + @time_period = (6.days.ago.to_date...Time.now.utc.to_date) end def update @@ -52,52 +26,8 @@ def set_tag @tag = Tag.find(params[:id]) end - def set_usage_by_domain - @usage_by_domain = @tag.statuses - .with_public_visibility - .excluding_silenced_accounts - .where(Status.arel_table[:id].gteq(Mastodon::Snowflake.id_at(Time.now.utc.beginning_of_day))) - .joins(:account) - .group('accounts.domain') - .reorder(statuses_count: :desc) - .pluck(Arel.sql('accounts.domain, count(*) AS statuses_count')) - end - - def set_counters - @accounts_today = @tag.history.first[:accounts] - @accounts_week = Redis.current.pfcount(*current_week_days.map { |day| "activity:tags:#{@tag.id}:#{day}:accounts" }) - end - - def filtered_tags - TagFilter.new(filter_params).results - end - - def filter_params - params.slice(:page, *TagFilter::KEYS).permit(:page, *TagFilter::KEYS) - end - def tag_params params.require(:tag).permit(:name, :trendable, :usable, :listable) end - - def current_week_days - now = Time.now.utc.beginning_of_day.to_date - - (Date.commercial(now.cwyear, now.cweek)..now).map do |date| - date.to_time(:utc).beginning_of_day.to_i - end - end - - def form_tag_batch_params - params.require(:form_tag_batch).permit(:action, tag_ids: []) - end - - def action_from_button - if params[:approve] - 'approve' - elsif params[:reject] - 'reject' - end - end end end diff --git a/app/controllers/admin/trends/links/preview_card_providers_controller.rb b/app/controllers/admin/trends/links/preview_card_providers_controller.rb new file mode 100644 index 00000000000000..40a466cd630433 --- /dev/null +++ b/app/controllers/admin/trends/links/preview_card_providers_controller.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +class Admin::Trends::Links::PreviewCardProvidersController < Admin::BaseController + def index + authorize :preview_card_provider, :index? + + @preview_card_providers = filtered_preview_card_providers.page(params[:page]) + @form = Trends::PreviewCardProviderBatch.new + end + + def batch + @form = Trends::PreviewCardProviderBatch.new(trends_preview_card_provider_batch_params.merge(current_account: current_account, action: action_from_button)) + @form.save + rescue ActionController::ParameterMissing + flash[:alert] = I18n.t('admin.accounts.no_account_selected') + ensure + redirect_to admin_trends_links_preview_card_providers_path(filter_params) + end + + private + + def filtered_preview_card_providers + Trends::PreviewCardProviderFilter.new(filter_params).results + end + + def filter_params + params.slice(:page, *Trends::PreviewCardProviderFilter::KEYS).permit(:page, *Trends::PreviewCardProviderFilter::KEYS) + end + + def trends_preview_card_provider_batch_params + params.require(:trends_preview_card_provider_batch).permit(:action, preview_card_provider_ids: []) + end + + def action_from_button + if params[:approve] + 'approve' + elsif params[:reject] + 'reject' + end + end +end diff --git a/app/controllers/admin/trends/links_controller.rb b/app/controllers/admin/trends/links_controller.rb new file mode 100644 index 00000000000000..434eec5feea3ad --- /dev/null +++ b/app/controllers/admin/trends/links_controller.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +class Admin::Trends::LinksController < Admin::BaseController + def index + authorize :preview_card, :index? + + @preview_cards = filtered_preview_cards.page(params[:page]) + @form = Trends::PreviewCardBatch.new + end + + def batch + @form = Trends::PreviewCardBatch.new(trends_preview_card_batch_params.merge(current_account: current_account, action: action_from_button)) + @form.save + rescue ActionController::ParameterMissing + flash[:alert] = I18n.t('admin.accounts.no_account_selected') + ensure + redirect_to admin_trends_links_path(filter_params) + end + + private + + def filtered_preview_cards + Trends::PreviewCardFilter.new(filter_params.with_defaults(trending: 'all')).results + end + + def filter_params + params.slice(:page, *Trends::PreviewCardFilter::KEYS).permit(:page, *Trends::PreviewCardFilter::KEYS) + end + + def trends_preview_card_batch_params + params.require(:trends_preview_card_batch).permit(:action, preview_card_ids: []) + end + + def action_from_button + if params[:approve] + 'approve' + elsif params[:approve_providers] + 'approve_providers' + elsif params[:reject] + 'reject' + elsif params[:reject_providers] + 'reject_providers' + end + end +end diff --git a/app/controllers/admin/trends/statuses_controller.rb b/app/controllers/admin/trends/statuses_controller.rb new file mode 100644 index 00000000000000..7662427387dc4d --- /dev/null +++ b/app/controllers/admin/trends/statuses_controller.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +class Admin::Trends::StatusesController < Admin::BaseController + def index + authorize :status, :index? + + @statuses = filtered_statuses.page(params[:page]) + @form = Trends::StatusBatch.new + end + + def batch + @form = Trends::StatusBatch.new(trends_status_batch_params.merge(current_account: current_account, action: action_from_button)) + @form.save + rescue ActionController::ParameterMissing + flash[:alert] = I18n.t('admin.accounts.no_account_selected') + ensure + redirect_to admin_trends_statuses_path(filter_params) + end + + private + + def filtered_statuses + Trends::StatusFilter.new(filter_params.with_defaults(trending: 'all')).results.includes(:account, :media_attachments, :active_mentions) + end + + def filter_params + params.slice(:page, *Trends::StatusFilter::KEYS).permit(:page, *Trends::StatusFilter::KEYS) + end + + def trends_status_batch_params + params.require(:trends_status_batch).permit(:action, status_ids: []) + end + + def action_from_button + if params[:approve] + 'approve' + elsif params[:approve_accounts] + 'approve_accounts' + elsif params[:reject] + 'reject' + elsif params[:reject_accounts] + 'reject_accounts' + end + end +end diff --git a/app/controllers/admin/trends/tags_controller.rb b/app/controllers/admin/trends/tags_controller.rb new file mode 100644 index 00000000000000..f4d1ec0d1f6725 --- /dev/null +++ b/app/controllers/admin/trends/tags_controller.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +class Admin::Trends::TagsController < Admin::BaseController + def index + authorize :tag, :index? + + @tags = filtered_tags.page(params[:page]) + @form = Trends::TagBatch.new + end + + def batch + @form = Trends::TagBatch.new(trends_tag_batch_params.merge(current_account: current_account, action: action_from_button)) + @form.save + rescue ActionController::ParameterMissing + flash[:alert] = I18n.t('admin.accounts.no_account_selected') + ensure + redirect_to admin_trends_tags_path(filter_params) + end + + private + + def filtered_tags + Trends::TagFilter.new(filter_params).results + end + + def filter_params + params.slice(:page, *Trends::TagFilter::KEYS).permit(:page, *Trends::TagFilter::KEYS) + end + + def trends_tag_batch_params + params.require(:trends_tag_batch).permit(:action, tag_ids: []) + end + + def action_from_button + if params[:approve] + 'approve' + elsif params[:reject] + 'reject' + end + end +end diff --git a/app/controllers/admin/two_factor_authentications_controller.rb b/app/controllers/admin/two_factor_authentications_controller.rb index 0652c3a7a4a123..f7fb7eb8fe3197 100644 --- a/app/controllers/admin/two_factor_authentications_controller.rb +++ b/app/controllers/admin/two_factor_authentications_controller.rb @@ -9,7 +9,7 @@ def destroy @user.disable_two_factor! log_action :disable_2fa, @user UserMailer.two_factor_disabled(@user).deliver_later! - redirect_to admin_accounts_path + redirect_to admin_account_path(@user.account_id) end private diff --git a/app/controllers/api/base_controller.rb b/app/controllers/api/base_controller.rb index 85f4cc7681a511..d96285b440479a 100644 --- a/app/controllers/api/base_controller.rb +++ b/app/controllers/api/base_controller.rb @@ -5,6 +5,7 @@ class Api::BaseController < ApplicationController DEFAULT_ACCOUNTS_LIMIT = 40 include RateLimitHeaders + include AccessTokenTrackingConcern skip_before_action :store_current_location skip_before_action :require_functional!, unless: :whitelist_mode? @@ -14,8 +15,6 @@ class Api::BaseController < ApplicationController protect_from_forgery with: :null_session - skip_around_action :set_locale - rescue_from ActiveRecord::RecordInvalid, Mastodon::ValidationError do |e| render json: { error: e.to_s }, status: 422 end @@ -40,7 +39,12 @@ class Api::BaseController < ApplicationController render json: { error: 'This action is not allowed' }, status: 403 end - rescue_from Mastodon::RaceConditionError, Seahorse::Client::NetworkingError, Stoplight::Error::RedLight do + rescue_from Seahorse::Client::NetworkingError do |e| + Rails.logger.warn "Storage server error: #{e}" + render json: { error: 'There was a temporary problem serving your request, please try again' }, status: 503 + end + + rescue_from Mastodon::RaceConditionError, Stoplight::Error::RedLight do render json: { error: 'There was a temporary problem serving your request, please try again' }, status: 503 end diff --git a/app/controllers/api/proofs_controller.rb b/app/controllers/api/proofs_controller.rb deleted file mode 100644 index dd32cd577bfcea..00000000000000 --- a/app/controllers/api/proofs_controller.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true - -class Api::ProofsController < Api::BaseController - include AccountOwnedConcern - - skip_before_action :require_authenticated_user! - - before_action :set_provider - - def index - render json: @account, serializer: @provider.serializer_class - end - - private - - def set_provider - @provider = ProofProvider.find(params[:provider]) || raise(ActiveRecord::RecordNotFound) - end - - def username_param - params[:username] - end -end diff --git a/app/controllers/api/v1/accounts/familiar_followers_controller.rb b/app/controllers/api/v1/accounts/familiar_followers_controller.rb new file mode 100644 index 00000000000000..b0bd8018a2177f --- /dev/null +++ b/app/controllers/api/v1/accounts/familiar_followers_controller.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +class Api::V1::Accounts::FamiliarFollowersController < Api::BaseController + before_action -> { doorkeeper_authorize! :read, :'read:follows' } + before_action :require_user! + before_action :set_accounts + + def index + render json: familiar_followers.accounts, each_serializer: REST::FamiliarFollowersSerializer + end + + private + + def set_accounts + @accounts = Account.without_suspended.where(id: account_ids).select('id, hide_collections').index_by(&:id).values_at(*account_ids).compact + end + + def familiar_followers + FamiliarFollowersPresenter.new(@accounts, current_user.account_id) + end + + def account_ids + Array(params[:id]).map(&:to_i) + end +end diff --git a/app/controllers/api/v1/accounts/identity_proofs_controller.rb b/app/controllers/api/v1/accounts/identity_proofs_controller.rb index 4b5f6902c7df95..48f293f47a325b 100644 --- a/app/controllers/api/v1/accounts/identity_proofs_controller.rb +++ b/app/controllers/api/v1/accounts/identity_proofs_controller.rb @@ -5,8 +5,7 @@ class Api::V1::Accounts::IdentityProofsController < Api::BaseController before_action :set_account def index - @proofs = @account.suspended? ? [] : @account.identity_proofs.active - render json: @proofs, each_serializer: REST::IdentityProofSerializer + render json: [] end private diff --git a/app/controllers/api/v1/accounts/statuses_controller.rb b/app/controllers/api/v1/accounts/statuses_controller.rb index 92ccb80615da26..38c9f5a20d0c7b 100644 --- a/app/controllers/api/v1/accounts/statuses_controller.rb +++ b/app/controllers/api/v1/accounts/statuses_controller.rb @@ -22,55 +22,16 @@ def load_statuses end def cached_account_statuses - statuses = truthy_param?(:pinned) ? pinned_scope : permitted_account_statuses - - statuses.merge!(only_media_scope) if truthy_param?(:only_media) - statuses.merge!(no_replies_scope) if truthy_param?(:exclude_replies) - statuses.merge!(no_reblogs_scope) if truthy_param?(:exclude_reblogs) - statuses.merge!(hashtag_scope) if params[:tagged].present? - cache_collection_paginated_by_id( - statuses, + AccountStatusesFilter.new(@account, current_account, params).results, Status, limit_param(DEFAULT_STATUSES_LIMIT), params_slice(:max_id, :since_id, :min_id) ) end - def permitted_account_statuses - @account.statuses.permitted_for(@account, current_account) - end - - def only_media_scope - Status.joins(:media_attachments).merge(@account.media_attachments.reorder(nil)).group(:id) - end - - def pinned_scope - return Status.none if @account.blocking?(current_account) - - @account.pinned_statuses - end - - def no_replies_scope - Status.without_replies - end - - def no_reblogs_scope - Status.without_reblogs - end - - def hashtag_scope - tag = Tag.find_normalized(params[:tagged]) - - if tag - Status.tagged_with(tag.id) - else - Status.none - end - end - def pagination_params(core_params) - params.slice(:limit, :only_media, :exclude_replies).permit(:limit, :only_media, :exclude_replies).merge(core_params) + params.slice(:limit, *AccountStatusesFilter::KEYS).permit(:limit, *AccountStatusesFilter::KEYS).merge(core_params) end def insert_pagination_headers diff --git a/app/controllers/api/v1/accounts_controller.rb b/app/controllers/api/v1/accounts_controller.rb index 95869f5541ff67..5134bfb94a6be0 100644 --- a/app/controllers/api/v1/accounts_controller.rb +++ b/app/controllers/api/v1/accounts_controller.rb @@ -1,10 +1,10 @@ # frozen_string_literal: true class Api::V1::AccountsController < Api::BaseController - before_action -> { authorize_if_got_token! :read, :'read:accounts' }, except: [:create, :follow, :unfollow, :block, :unblock, :mute, :unmute] - before_action -> { doorkeeper_authorize! :follow, :'write:follows' }, only: [:follow, :unfollow] - before_action -> { doorkeeper_authorize! :follow, :'write:mutes' }, only: [:mute, :unmute] - before_action -> { doorkeeper_authorize! :follow, :'write:blocks' }, only: [:block, :unblock] + before_action -> { authorize_if_got_token! :read, :'read:accounts' }, except: [:create, :follow, :unfollow, :remove_from_followers, :block, :unblock, :mute, :unmute] + before_action -> { doorkeeper_authorize! :follow, :write, :'write:follows' }, only: [:follow, :unfollow, :remove_from_followers] + before_action -> { doorkeeper_authorize! :follow, :write, :'write:mutes' }, only: [:mute, :unmute] + before_action -> { doorkeeper_authorize! :follow, :write, :'write:blocks' }, only: [:block, :unblock] before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, only: [:create] before_action :require_user!, except: [:show, :create] @@ -53,6 +53,11 @@ def unfollow render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships end + def remove_from_followers + RemoveFromFollowersService.new.call(current_user.account, @account) + render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships + end + def unblock UnblockService.new.call(current_user.account, @account) render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships @@ -78,10 +83,14 @@ def account_params end def check_enabled_registrations - forbidden if single_user_mode? || !allowed_registrations? + forbidden if single_user_mode? || omniauth_only? || !allowed_registrations? end def allowed_registrations? Setting.registrations_mode != 'none' end + + def omniauth_only? + ENV['OMNIAUTH_ONLY'] == 'true' + end end diff --git a/app/controllers/api/v1/admin/account_actions_controller.rb b/app/controllers/api/v1/admin/account_actions_controller.rb index 29c9b7107bf1a1..6c9e04402c9947 100644 --- a/app/controllers/api/v1/admin/account_actions_controller.rb +++ b/app/controllers/api/v1/admin/account_actions_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class Api::V1::Admin::AccountActionsController < Api::BaseController - before_action -> { doorkeeper_authorize! :'admin:write', :'admin:write:accounts' } + before_action -> { authorize_if_got_token! :'admin:write', :'admin:write:accounts' } before_action :require_staff! before_action :set_account diff --git a/app/controllers/api/v1/admin/accounts_controller.rb b/app/controllers/api/v1/admin/accounts_controller.rb index 63cc521ed01199..65ed69f7ba444e 100644 --- a/app/controllers/api/v1/admin/accounts_controller.rb +++ b/app/controllers/api/v1/admin/accounts_controller.rb @@ -6,8 +6,8 @@ class Api::V1::Admin::AccountsController < Api::BaseController LIMIT = 100 - before_action -> { doorkeeper_authorize! :'admin:read', :'admin:read:accounts' }, only: [:index, :show] - before_action -> { doorkeeper_authorize! :'admin:write', :'admin:write:accounts' }, except: [:index, :show] + before_action -> { authorize_if_got_token! :'admin:read', :'admin:read:accounts' }, only: [:index, :show] + before_action -> { authorize_if_got_token! :'admin:write', :'admin:write:accounts' }, except: [:index, :show] before_action :require_staff! before_action :set_accounts, only: :index before_action :set_account, except: :index @@ -65,8 +65,9 @@ def reject def destroy authorize @account, :destroy? + json = render_to_body json: @account, serializer: REST::Admin::AccountSerializer Admin::AccountDeletionWorker.perform_async(@account.id) - render json: @account, serializer: REST::Admin::AccountSerializer + render json: json end def unsensitive @@ -94,7 +95,7 @@ def unsuspend private def set_accounts - @accounts = filtered_accounts.order(id: :desc).includes(user: [:invite_request, :invite]).to_a_paginated_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id)) + @accounts = filtered_accounts.order(id: :desc).includes(user: [:invite_request, :invite, :ips]).to_a_paginated_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id)) end def set_account @@ -102,13 +103,27 @@ def set_account end def filtered_accounts - AccountFilter.new(filter_params).results + AccountFilter.new(translated_filter_params).results end def filter_params params.permit(*FILTER_PARAMS) end + def translated_filter_params + translated_params = { origin: 'local', status: 'active' }.merge(filter_params.slice(*AccountFilter::KEYS)) + + translated_params[:origin] = 'remote' if params[:remote].present? + + %i(active pending disabled silenced suspended).each do |status| + translated_params[:status] = status.to_s if params[status].present? + end + + translated_params[:permissions] = 'staff' if params[:staff].present? + + translated_params + end + def insert_pagination_headers set_pagination_headers(next_path, prev_path) end diff --git a/app/controllers/api/v1/admin/dimensions_controller.rb b/app/controllers/api/v1/admin/dimensions_controller.rb new file mode 100644 index 00000000000000..49a5be1c365d92 --- /dev/null +++ b/app/controllers/api/v1/admin/dimensions_controller.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +class Api::V1::Admin::DimensionsController < Api::BaseController + before_action -> { authorize_if_got_token! :'admin:read' } + before_action :require_staff! + before_action :set_dimensions + + def create + render json: @dimensions, each_serializer: REST::Admin::DimensionSerializer + end + + private + + def set_dimensions + @dimensions = Admin::Metrics::Dimension.retrieve( + params[:keys], + params[:start_at], + params[:end_at], + params[:limit], + params + ) + end +end diff --git a/app/controllers/api/v1/admin/measures_controller.rb b/app/controllers/api/v1/admin/measures_controller.rb new file mode 100644 index 00000000000000..da95d34220dc80 --- /dev/null +++ b/app/controllers/api/v1/admin/measures_controller.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +class Api::V1::Admin::MeasuresController < Api::BaseController + before_action -> { authorize_if_got_token! :'admin:read' } + before_action :require_staff! + before_action :set_measures + + def create + render json: @measures, each_serializer: REST::Admin::MeasureSerializer + end + + private + + def set_measures + @measures = Admin::Metrics::Measure.retrieve( + params[:keys], + params[:start_at], + params[:end_at], + params + ) + end +end diff --git a/app/controllers/api/v1/admin/reports_controller.rb b/app/controllers/api/v1/admin/reports_controller.rb index c8f4cd8d80c828..865ba3d23c8824 100644 --- a/app/controllers/api/v1/admin/reports_controller.rb +++ b/app/controllers/api/v1/admin/reports_controller.rb @@ -6,8 +6,8 @@ class Api::V1::Admin::ReportsController < Api::BaseController LIMIT = 100 - before_action -> { doorkeeper_authorize! :'admin:read', :'admin:read:reports' }, only: [:index, :show] - before_action -> { doorkeeper_authorize! :'admin:write', :'admin:write:reports' }, except: [:index, :show] + before_action -> { authorize_if_got_token! :'admin:read', :'admin:read:reports' }, only: [:index, :show] + before_action -> { authorize_if_got_token! :'admin:write', :'admin:write:reports' }, except: [:index, :show] before_action :require_staff! before_action :set_reports, only: :index before_action :set_report, except: :index @@ -32,6 +32,12 @@ def show render json: @report, serializer: REST::Admin::ReportSerializer end + def update + authorize @report, :update? + @report.update!(report_params) + render json: @report, serializer: REST::Admin::ReportSerializer + end + def assign_to_self authorize @report, :update? @report.update!(assigned_account_id: current_account.id) @@ -74,6 +80,10 @@ def filtered_reports ReportFilter.new(filter_params).results end + def report_params + params.permit(:category, rule_ids: []) + end + def filter_params params.permit(*FILTER_PARAMS) end diff --git a/app/controllers/api/v1/admin/retention_controller.rb b/app/controllers/api/v1/admin/retention_controller.rb new file mode 100644 index 00000000000000..98d1a3d813fb9c --- /dev/null +++ b/app/controllers/api/v1/admin/retention_controller.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class Api::V1::Admin::RetentionController < Api::BaseController + before_action -> { authorize_if_got_token! :'admin:read' } + before_action :require_staff! + before_action :set_cohorts + + def create + render json: @cohorts, each_serializer: REST::Admin::CohortSerializer + end + + private + + def set_cohorts + @cohorts = Admin::Metrics::Retention.new( + params[:start_at], + params[:end_at], + params[:frequency] + ).cohorts + end +end diff --git a/app/controllers/api/v1/admin/trends/links_controller.rb b/app/controllers/api/v1/admin/trends/links_controller.rb new file mode 100644 index 00000000000000..0a191fe4b26ffb --- /dev/null +++ b/app/controllers/api/v1/admin/trends/links_controller.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class Api::V1::Admin::Trends::LinksController < Api::BaseController + before_action -> { authorize_if_got_token! :'admin:read' } + before_action :require_staff! + before_action :set_links + + def index + render json: @links, each_serializer: REST::Trends::LinkSerializer + end + + private + + def set_links + @links = Trends.links.query.limit(limit_param(10)) + end +end diff --git a/app/controllers/api/v1/admin/trends/statuses_controller.rb b/app/controllers/api/v1/admin/trends/statuses_controller.rb new file mode 100644 index 00000000000000..cb145f165c33b0 --- /dev/null +++ b/app/controllers/api/v1/admin/trends/statuses_controller.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class Api::V1::Admin::Trends::StatusesController < Api::BaseController + before_action -> { authorize_if_got_token! :'admin:read' } + before_action :require_staff! + before_action :set_statuses + + def index + render json: @statuses, each_serializer: REST::StatusSerializer + end + + private + + def set_statuses + @statuses = cache_collection(Trends.statuses.query.limit(limit_param(DEFAULT_STATUSES_LIMIT)), Status) + end +end diff --git a/app/controllers/api/v1/admin/trends/tags_controller.rb b/app/controllers/api/v1/admin/trends/tags_controller.rb new file mode 100644 index 00000000000000..9c28b0412b495d --- /dev/null +++ b/app/controllers/api/v1/admin/trends/tags_controller.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class Api::V1::Admin::Trends::TagsController < Api::BaseController + before_action -> { authorize_if_got_token! :'admin:read' } + before_action :require_staff! + before_action :set_tags + + def index + render json: @tags, each_serializer: REST::Admin::TagSerializer + end + + private + + def set_tags + @tags = Trends.tags.query.limit(limit_param(10)) + end +end diff --git a/app/controllers/api/v1/blocks_controller.rb b/app/controllers/api/v1/blocks_controller.rb index 586cdfca9d4e6b..a65e762c9f82ad 100644 --- a/app/controllers/api/v1/blocks_controller.rb +++ b/app/controllers/api/v1/blocks_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class Api::V1::BlocksController < Api::BaseController - before_action -> { doorkeeper_authorize! :follow, :'read:blocks' } + before_action -> { doorkeeper_authorize! :follow, :read, :'read:blocks' } before_action :require_user! after_action :insert_pagination_headers diff --git a/app/controllers/api/v1/domain_blocks_controller.rb b/app/controllers/api/v1/domain_blocks_controller.rb index 5bb02d834cf6fe..1891261b9ca8d1 100644 --- a/app/controllers/api/v1/domain_blocks_controller.rb +++ b/app/controllers/api/v1/domain_blocks_controller.rb @@ -3,8 +3,8 @@ class Api::V1::DomainBlocksController < Api::BaseController BLOCK_LIMIT = 100 - before_action -> { doorkeeper_authorize! :follow, :'read:blocks' }, only: :show - before_action -> { doorkeeper_authorize! :follow, :'write:blocks' }, except: :show + before_action -> { doorkeeper_authorize! :follow, :read, :'read:blocks' }, only: :show + before_action -> { doorkeeper_authorize! :follow, :write, :'write:blocks' }, except: :show before_action :require_user! after_action :insert_pagination_headers, only: :show diff --git a/app/controllers/api/v1/emails/confirmations_controller.rb b/app/controllers/api/v1/emails/confirmations_controller.rb index f1d9954d095528..3faaea2fb78212 100644 --- a/app/controllers/api/v1/emails/confirmations_controller.rb +++ b/app/controllers/api/v1/emails/confirmations_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class Api::V1::Emails::ConfirmationsController < Api::BaseController - before_action :doorkeeper_authorize! + before_action -> { doorkeeper_authorize! :write, :'write:accounts' } before_action :require_user_owned_by_application! before_action :require_user_not_confirmed! @@ -19,6 +19,6 @@ def require_user_owned_by_application! end def require_user_not_confirmed! - render json: { error: 'This method is only available while the e-mail is awaiting confirmation' }, status: :forbidden if current_user.confirmed? || current_user.unconfirmed_email.blank? + render json: { error: 'This method is only available while the e-mail is awaiting confirmation' }, status: :forbidden unless !current_user.confirmed? || current_user.unconfirmed_email.present? end end diff --git a/app/controllers/api/v1/follow_requests_controller.rb b/app/controllers/api/v1/follow_requests_controller.rb index f4b2a74d0ae82a..54ff0e11d03e42 100644 --- a/app/controllers/api/v1/follow_requests_controller.rb +++ b/app/controllers/api/v1/follow_requests_controller.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true class Api::V1::FollowRequestsController < Api::BaseController - before_action -> { doorkeeper_authorize! :follow, :'read:follows' }, only: :index - before_action -> { doorkeeper_authorize! :follow, :'write:follows' }, except: :index + before_action -> { doorkeeper_authorize! :follow, :read, :'read:follows' }, only: :index + before_action -> { doorkeeper_authorize! :follow, :write, :'write:follows' }, except: :index before_action :require_user! after_action :insert_pagination_headers, only: :index @@ -13,7 +13,7 @@ def index def authorize AuthorizeFollowService.new.call(account, current_account) - NotifyService.new.call(current_account, :follow, Follow.find_by(account: account, target_account: current_account)) + LocalNotificationWorker.perform_async(current_account.id, Follow.find_by(account: account, target_account: current_account).id, 'Follow', 'follow') render json: account, serializer: REST::RelationshipSerializer, relationships: relationships end diff --git a/app/controllers/api/v1/instances/activity_controller.rb b/app/controllers/api/v1/instances/activity_controller.rb index 4f6b4bcbfa0878..bad61425a5dd5d 100644 --- a/app/controllers/api/v1/instances/activity_controller.rb +++ b/app/controllers/api/v1/instances/activity_controller.rb @@ -14,22 +14,21 @@ def show private def activity - weeks = [] - - 12.times do |i| - day = i.weeks.ago.to_date - week_id = day.cweek - week = Date.commercial(day.cwyear, week_id) - - weeks << { - week: week.to_time.to_i.to_s, - statuses: Redis.current.get("activity:statuses:local:#{week_id}") || '0', - logins: Redis.current.pfcount("activity:logins:#{week_id}").to_s, - registrations: Redis.current.get("activity:accounts:local:#{week_id}") || '0', + statuses_tracker = ActivityTracker.new('activity:statuses:local', :basic) + logins_tracker = ActivityTracker.new('activity:logins', :unique) + registrations_tracker = ActivityTracker.new('activity:accounts:local', :basic) + + (0...12).map do |i| + start_of_week = i.weeks.ago + end_of_week = start_of_week + 6.days + + { + week: start_of_week.to_i.to_s, + statuses: statuses_tracker.sum(start_of_week, end_of_week).to_s, + logins: logins_tracker.sum(start_of_week, end_of_week).to_s, + registrations: registrations_tracker.sum(start_of_week, end_of_week).to_s, } end - - weeks end def require_enabled_api! diff --git a/app/controllers/api/v1/media_controller.rb b/app/controllers/api/v1/media_controller.rb index a2a919a3e6f256..f9c935bf3e1ec4 100644 --- a/app/controllers/api/v1/media_controller.rb +++ b/app/controllers/api/v1/media_controller.rb @@ -20,7 +20,7 @@ def show end def update - @media_attachment.update!(media_attachment_params) + @media_attachment.update!(updateable_media_attachment_params) render json: @media_attachment, serializer: REST::MediaAttachmentSerializer, status: status_code_for_media_attachment end @@ -31,7 +31,7 @@ def status_code_for_media_attachment end def set_media_attachment - @media_attachment = current_account.media_attachments.unattached.find(params[:id]) + @media_attachment = current_account.media_attachments.where(status_id: nil).find(params[:id]) end def check_processing @@ -42,6 +42,10 @@ def media_attachment_params params.permit(:file, :thumbnail, :description, :focus) end + def updateable_media_attachment_params + params.permit(:thumbnail, :description, :focus) + end + def file_type_error { error: 'File type of uploaded media could not be verified' } end diff --git a/app/controllers/api/v1/mutes_controller.rb b/app/controllers/api/v1/mutes_controller.rb index fd52511d7eb049..6cde53a2a7dbfa 100644 --- a/app/controllers/api/v1/mutes_controller.rb +++ b/app/controllers/api/v1/mutes_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class Api::V1::MutesController < Api::BaseController - before_action -> { doorkeeper_authorize! :follow, :'read:mutes' } + before_action -> { doorkeeper_authorize! :follow, :read, :'read:mutes' } before_action :require_user! after_action :insert_pagination_headers diff --git a/app/controllers/api/v1/notifications_controller.rb b/app/controllers/api/v1/notifications_controller.rb index f9d780839849d3..6d464997ea98ea 100644 --- a/app/controllers/api/v1/notifications_controller.rb +++ b/app/controllers/api/v1/notifications_controller.rb @@ -35,13 +35,18 @@ def load_notifications limit_param(DEFAULT_NOTIFICATIONS_LIMIT), params_slice(:max_id, :since_id, :min_id) ) + Notification.preload_cache_collection_target_statuses(notifications) do |target_statuses| cache_collection(target_statuses, Status) end end def browserable_account_notifications - current_account.notifications.without_suspended.browserable(exclude_types, from_account) + current_account.notifications.without_suspended.browserable( + types: Array(browserable_params[:types]), + exclude_types: Array(browserable_params[:exclude_types]), + from_account_id: browserable_params[:account_id] + ) end def target_statuses_from_notifications @@ -72,17 +77,11 @@ def pagination_since_id @notifications.first.id end - def exclude_types - val = params.permit(exclude_types: [])[:exclude_types] || [] - val = [val] unless val.is_a?(Enumerable) - val - end - - def from_account - params[:account_id] + def browserable_params + params.permit(:account_id, types: [], exclude_types: []) end def pagination_params(core_params) - params.slice(:limit, :exclude_types).permit(:limit, exclude_types: []).merge(core_params) + params.slice(:limit, :account_id, :types, :exclude_types).permit(:limit, :account_id, types: [], exclude_types: []).merge(core_params) end end diff --git a/app/controllers/api/v1/reports_controller.rb b/app/controllers/api/v1/reports_controller.rb index e10083d450f1a5..8ff6c8fe5c9914 100644 --- a/app/controllers/api/v1/reports_controller.rb +++ b/app/controllers/api/v1/reports_controller.rb @@ -10,9 +10,7 @@ def create @report = ReportService.new.call( current_account, reported_account, - status_ids: reported_status_ids, - comment: report_params[:comment], - forward: report_params[:forward] + report_params ) render json: @report, serializer: REST::ReportSerializer @@ -20,19 +18,11 @@ def create private - def reported_status_ids - reported_account.statuses.with_discarded.find(status_ids).pluck(:id) - end - - def status_ids - Array(report_params[:status_ids]) - end - def reported_account Account.find(report_params[:account_id]) end def report_params - params.permit(:account_id, :comment, :forward, status_ids: []) + params.permit(:account_id, :comment, :category, :forward, status_ids: [], rule_ids: []) end end diff --git a/app/controllers/api/v1/statuses/histories_controller.rb b/app/controllers/api/v1/statuses/histories_controller.rb new file mode 100644 index 00000000000000..7fe73a6f5493cc --- /dev/null +++ b/app/controllers/api/v1/statuses/histories_controller.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class Api::V1::Statuses::HistoriesController < Api::BaseController + include Authorization + + before_action -> { authorize_if_got_token! :read, :'read:statuses' } + before_action :set_status + + def show + render json: @status.edits.includes(:account, status: [:account]), each_serializer: REST::StatusEditSerializer + end + + private + + def set_status + @status = Status.find(params[:status_id]) + authorize @status, :show? + rescue Mastodon::NotPermittedError + not_found + end +end diff --git a/app/controllers/api/v1/statuses/sources_controller.rb b/app/controllers/api/v1/statuses/sources_controller.rb new file mode 100644 index 00000000000000..43408645130edf --- /dev/null +++ b/app/controllers/api/v1/statuses/sources_controller.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class Api::V1::Statuses::SourcesController < Api::BaseController + include Authorization + + before_action -> { doorkeeper_authorize! :read, :'read:statuses' } + before_action :set_status + + def show + render json: @status, serializer: REST::StatusSourceSerializer + end + + private + + def set_status + @status = Status.find(params[:status_id]) + authorize @status, :show? + rescue Mastodon::NotPermittedError + not_found + end +end diff --git a/app/controllers/api/v1/statuses_controller.rb b/app/controllers/api/v1/statuses_controller.rb index 106fc8224e2876..9270117dad5842 100644 --- a/app/controllers/api/v1/statuses_controller.rb +++ b/app/controllers/api/v1/statuses_controller.rb @@ -3,13 +3,14 @@ class Api::V1::StatusesController < Api::BaseController include Authorization - before_action -> { authorize_if_got_token! :read, :'read:statuses' }, except: [:create, :destroy] - before_action -> { doorkeeper_authorize! :write, :'write:statuses' }, only: [:create, :destroy] + before_action -> { authorize_if_got_token! :read, :'read:statuses' }, except: [:create, :update, :destroy] + before_action -> { doorkeeper_authorize! :write, :'write:statuses' }, only: [:create, :update, :destroy] before_action :require_user!, except: [:show, :context] before_action :set_status, only: [:show, :context] before_action :set_thread, only: [:create] override_rate_limit_headers :create, family: :statuses + override_rate_limit_headers :update, family: :statuses # This API was originally unlimited, pagination cannot be introduced without # breaking backwards-compatibility. Arbitrarily high number to cover most @@ -35,31 +36,53 @@ def context end def create - @status = PostStatusService.new.call(current_user.account, - text: status_params[:status], - thread: @thread, - media_ids: status_params[:media_ids], - sensitive: status_params[:sensitive], - spoiler_text: status_params[:spoiler_text], - visibility: status_params[:visibility], - scheduled_at: status_params[:scheduled_at], - application: doorkeeper_token.application, - poll: status_params[:poll], - idempotency: request.headers['Idempotency-Key'], - with_rate_limit: true) + @status = PostStatusService.new.call( + current_user.account, + text: status_params[:status], + thread: @thread, + media_ids: status_params[:media_ids], + sensitive: status_params[:sensitive], + spoiler_text: status_params[:spoiler_text], + visibility: status_params[:visibility], + language: status_params[:language], + scheduled_at: status_params[:scheduled_at], + application: doorkeeper_token.application, + poll: status_params[:poll], + idempotency: request.headers['Idempotency-Key'], + with_rate_limit: true + ) render json: @status, serializer: @status.is_a?(ScheduledStatus) ? REST::ScheduledStatusSerializer : REST::StatusSerializer end + def update + @status = Status.where(account: current_account).find(params[:id]) + authorize @status, :update? + + UpdateStatusService.new.call( + @status, + current_account.id, + text: status_params[:status], + media_ids: status_params[:media_ids], + sensitive: status_params[:sensitive], + spoiler_text: status_params[:spoiler_text], + poll: status_params[:poll] + ) + + render json: @status, serializer: REST::StatusSerializer + end + def destroy - @status = Status.where(account_id: current_user.account).find(params[:id]) + @status = Status.where(account: current_account).find(params[:id]) authorize @status, :destroy? @status.discard - RemovalWorker.perform_async(@status.id, redraft: true) @status.account.statuses_count = @status.account.statuses_count - 1 + json = render_to_body json: @status, serializer: REST::StatusSerializer, source_requested: true + + RemovalWorker.perform_async(@status.id, { 'redraft' => true }) - render json: @status, serializer: REST::StatusSerializer, source_requested: true + render json: json end private @@ -72,8 +95,9 @@ def set_status end def set_thread - @thread = status_params[:in_reply_to_id].blank? ? nil : Status.find(status_params[:in_reply_to_id]) - rescue ActiveRecord::RecordNotFound + @thread = Status.find(status_params[:in_reply_to_id]) if status_params[:in_reply_to_id].present? + authorize(@thread, :show?) if @thread.present? + rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError render json: { error: I18n.t('statuses.errors.in_reply_not_found') }, status: 404 end @@ -84,6 +108,7 @@ def status_params :sensitive, :spoiler_text, :visibility, + :language, :scheduled_at, media_ids: [], poll: [ diff --git a/app/controllers/api/v1/trends/links_controller.rb b/app/controllers/api/v1/trends/links_controller.rb new file mode 100644 index 00000000000000..2385fe4380d2ae --- /dev/null +++ b/app/controllers/api/v1/trends/links_controller.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +class Api::V1::Trends::LinksController < Api::BaseController + before_action :set_links + + after_action :insert_pagination_headers + + DEFAULT_LINKS_LIMIT = 10 + + def index + render json: @links, each_serializer: REST::Trends::LinkSerializer + end + + private + + def set_links + @links = begin + if Setting.trends + links_from_trends + else + [] + end + end + end + + def links_from_trends + Trends.links.query.allowed.in_locale(content_locale).offset(offset_param).limit(limit_param(DEFAULT_LINKS_LIMIT)) + end + + def insert_pagination_headers + set_pagination_headers(next_path, prev_path) + end + + def pagination_params(core_params) + params.slice(:limit).permit(:limit).merge(core_params) + end + + def next_path + api_v1_trends_links_url pagination_params(offset: offset_param + limit_param(DEFAULT_LINKS_LIMIT)) if records_continue? + end + + def prev_path + api_v1_trends_links_url pagination_params(offset: offset_param - limit_param(DEFAULT_LINKS_LIMIT)) if offset_param > limit_param(DEFAULT_LINKS_LIMIT) + end + + def records_continue? + @links.size == limit_param(DEFAULT_LINKS_LIMIT) + end + + def offset_param + params[:offset].to_i + end +end diff --git a/app/controllers/api/v1/trends/statuses_controller.rb b/app/controllers/api/v1/trends/statuses_controller.rb new file mode 100644 index 00000000000000..1f2fff582de4eb --- /dev/null +++ b/app/controllers/api/v1/trends/statuses_controller.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +class Api::V1::Trends::StatusesController < Api::BaseController + before_action :set_statuses + + after_action :insert_pagination_headers + + def index + render json: @statuses, each_serializer: REST::StatusSerializer + end + + private + + def set_statuses + @statuses = begin + if Setting.trends + cache_collection(statuses_from_trends, Status) + else + [] + end + end + end + + def statuses_from_trends + scope = Trends.statuses.query.allowed.in_locale(content_locale) + scope = scope.filtered_for(current_account) if user_signed_in? + scope.offset(offset_param).limit(limit_param(DEFAULT_STATUSES_LIMIT)) + end + + def insert_pagination_headers + set_pagination_headers(next_path, prev_path) + end + + def pagination_params(core_params) + params.slice(:limit).permit(:limit).merge(core_params) + end + + def next_path + api_v1_trends_statuses_url pagination_params(offset: offset_param + limit_param(DEFAULT_STATUSES_LIMIT)) if records_continue? + end + + def prev_path + api_v1_trends_statuses_url pagination_params(offset: offset_param - limit_param(DEFAULT_STATUSES_LIMIT)) if offset_param > limit_param(DEFAULT_STATUSES_LIMIT) + end + + def offset_param + params[:offset].to_i + end + + def records_continue? + @statuses.size == limit_param(DEFAULT_STATUSES_LIMIT) + end +end diff --git a/app/controllers/api/v1/trends/tags_controller.rb b/app/controllers/api/v1/trends/tags_controller.rb new file mode 100644 index 00000000000000..38003f599afb99 --- /dev/null +++ b/app/controllers/api/v1/trends/tags_controller.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +class Api::V1::Trends::TagsController < Api::BaseController + before_action :set_tags + + after_action :insert_pagination_headers + + DEFAULT_TAGS_LIMIT = 10 + + def index + render json: @tags, each_serializer: REST::TagSerializer + end + + private + + def set_tags + @tags = begin + if Setting.trends + Trends.tags.query.allowed.offset(offset_param).limit(limit_param(DEFAULT_TAGS_LIMIT)) + else + [] + end + end + end + + def insert_pagination_headers + set_pagination_headers(next_path, prev_path) + end + + def pagination_params(core_params) + params.slice(:limit).permit(:limit).merge(core_params) + end + + def next_path + api_v1_trends_tags_url pagination_params(offset: offset_param + limit_param(DEFAULT_TAGS_LIMIT)) if records_continue? + end + + def prev_path + api_v1_trends_tags_url pagination_params(offset: offset_param - limit_param(DEFAULT_TAGS_LIMIT)) if offset_param > limit_param(DEFAULT_TAGS_LIMIT) + end + + def offset_param + params[:offset].to_i + end + + def records_continue? + @tags.size == limit_param(DEFAULT_TAGS_LIMIT) + end +end diff --git a/app/controllers/api/v1/trends_controller.rb b/app/controllers/api/v1/trends_controller.rb deleted file mode 100644 index c875e90414de37..00000000000000 --- a/app/controllers/api/v1/trends_controller.rb +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true - -class Api::V1::TrendsController < Api::BaseController - before_action :set_tags - - def index - render json: @tags, each_serializer: REST::TagSerializer - end - - private - - def set_tags - @tags = TrendingTags.get(limit_param(10)) - end -end diff --git a/app/controllers/api/v2/admin/accounts_controller.rb b/app/controllers/api/v2/admin/accounts_controller.rb new file mode 100644 index 00000000000000..a89e6835ee10ed --- /dev/null +++ b/app/controllers/api/v2/admin/accounts_controller.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +class Api::V2::Admin::AccountsController < Api::V1::Admin::AccountsController + FILTER_PARAMS = %i( + origin + status + permissions + username + by_domain + display_name + email + ip + invited_by + ).freeze + + PAGINATION_PARAMS = (%i(limit) + FILTER_PARAMS).freeze + + private + + def filtered_accounts + AccountFilter.new(filter_params).results + end + + def filter_params + params.permit(*FILTER_PARAMS) + end + + def pagination_params(core_params) + params.slice(*PAGINATION_PARAMS).permit(*PAGINATION_PARAMS).merge(core_params) + end +end diff --git a/app/controllers/api/v2/search_controller.rb b/app/controllers/api/v2/search_controller.rb index f17431dd10b8e9..a3056013338556 100644 --- a/app/controllers/api/v2/search_controller.rb +++ b/app/controllers/api/v2/search_controller.rb @@ -11,6 +11,10 @@ class Api::V2::SearchController < Api::BaseController def index @search = Search.new(search_results) render json: @search, serializer: REST::SearchSerializer + rescue Mastodon::SyntaxError + unprocessable_entity + rescue ActiveRecord::RecordNotFound + not_found end private diff --git a/app/controllers/api/web/embeds_controller.rb b/app/controllers/api/web/embeds_controller.rb index 741ba910fb8769..58f6345e68cb0d 100644 --- a/app/controllers/api/web/embeds_controller.rb +++ b/app/controllers/api/web/embeds_controller.rb @@ -15,7 +15,7 @@ def create return not_found if oembed.nil? begin - oembed[:html] = Formatter.instance.sanitize(oembed[:html], Sanitize::Config::MASTODON_OEMBED) + oembed[:html] = Sanitize.fragment(oembed[:html], Sanitize::Config::MASTODON_OEMBED) rescue ArgumentError return not_found end diff --git a/app/controllers/api/web/push_subscriptions_controller.rb b/app/controllers/api/web/push_subscriptions_controller.rb index bed57fc54cae6c..5167928e932a4b 100644 --- a/app/controllers/api/web/push_subscriptions_controller.rb +++ b/app/controllers/api/web/push_subscriptions_controller.rb @@ -17,16 +17,7 @@ def create data = { policy: 'all', - - alerts: { - follow: alerts_enabled, - follow_request: alerts_enabled, - favourite: alerts_enabled, - reblog: alerts_enabled, - mention: alerts_enabled, - poll: alerts_enabled, - status: alerts_enabled, - }, + alerts: Notification::TYPES.index_with { alerts_enabled }, } data.deep_merge!(data_params) if params[:data] @@ -61,6 +52,6 @@ def subscription_params end def data_params - @data_params ||= params.require(:data).permit(:policy, alerts: [:follow, :follow_request, :favourite, :reblog, :mention, :poll, :status]) + @data_params ||= params.require(:data).permit(:policy, alerts: Notification::TYPES) end end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index b4fb83661bc70b..3d2f8280b15f21 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -26,7 +26,12 @@ class ApplicationController < ActionController::Base rescue_from Mastodon::RateLimitExceededError, with: :too_many_requests rescue_from HTTP::Error, OpenSSL::SSL::SSLError, with: :internal_server_error - rescue_from Mastodon::RaceConditionError, Seahorse::Client::NetworkingError, Stoplight::Error::RedLight, ActiveRecord::SerializationFailure, with: :service_unavailable + rescue_from Mastodon::RaceConditionError, Stoplight::Error::RedLight, ActiveRecord::SerializationFailure, with: :service_unavailable + + rescue_from Seahorse::Client::NetworkingError do |e| + Rails.logger.warn "Storage server error: #{e}" + service_unavailable + end before_action :store_current_location, except: :raise_not_found, unless: :devise_controller? before_action :require_functional!, if: :user_signed_in? diff --git a/app/controllers/auth/omniauth_callbacks_controller.rb b/app/controllers/auth/omniauth_callbacks_controller.rb index 682c77016fd6cd..f9cf6d6551f681 100644 --- a/app/controllers/auth/omniauth_callbacks_controller.rb +++ b/app/controllers/auth/omniauth_callbacks_controller.rb @@ -4,14 +4,21 @@ class Auth::OmniauthCallbacksController < Devise::OmniauthCallbacksController skip_before_action :verify_authenticity_token def self.provides_callback_for(provider) - provider_id = provider.to_s.chomp '_oauth2' - define_method provider do @user = User.find_for_oauth(request.env['omniauth.auth'], current_user) if @user.persisted? + LoginActivity.create( + user: @user, + success: true, + authentication_method: :omniauth, + provider: provider, + ip: request.remote_ip, + user_agent: request.user_agent + ) + sign_in_and_redirect @user, event: :authentication - set_flash_message(:notice, :success, kind: provider_id.capitalize) if is_navigational_format? + set_flash_message(:notice, :success, kind: Devise.omniauth_configs[provider].strategy.display_name.capitalize) if is_navigational_format? else session["devise.#{provider}_data"] = request.env['omniauth.auth'] redirect_to new_user_registration_url @@ -24,7 +31,7 @@ def self.provides_callback_for(provider) end def after_sign_in_path_for(resource) - if resource.email_verified? + if resource.email_present? root_path else auth_setup_path(missing_email: '1') diff --git a/app/controllers/auth/registrations_controller.rb b/app/controllers/auth/registrations_controller.rb index 3c1730f25f4bf3..1c3adbd786bd91 100644 --- a/app/controllers/auth/registrations_controller.rb +++ b/app/controllers/auth/registrations_controller.rb @@ -9,6 +9,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController before_action :check_enabled_registrations, only: [:new, :create] before_action :configure_sign_up_params, only: [:create] before_action :set_sessions, only: [:edit, :update] + before_action :set_strikes, only: [:edit, :update] before_action :set_instance_presenter, only: [:new, :create, :update] before_action :set_body_classes, only: [:new, :create, :edit, :update] before_action :require_not_suspended!, only: [:update] @@ -81,13 +82,17 @@ def after_update_path_for(_resource) end def check_enabled_registrations - redirect_to root_path if single_user_mode? || !allowed_registrations? + redirect_to root_path if single_user_mode? || omniauth_only? || !allowed_registrations? end def allowed_registrations? Setting.registrations_mode != 'none' || @invite&.valid_for_use? end + def omniauth_only? + ENV['OMNIAUTH_ONLY'] == 'true' + end + def invite_code if params[:user] params[:user][:invite_code] @@ -107,8 +112,10 @@ def set_body_classes end def set_invite - invite = invite_code.present? ? Invite.find_by(code: invite_code) : nil - @invite = invite&.valid_for_use? ? invite : nil + @invite = begin + invite = Invite.find_by(code: invite_code) if invite_code.present? + invite if invite&.valid_for_use? + end end def determine_layout @@ -119,6 +126,10 @@ def set_sessions @sessions = current_user.session_activations end + def set_strikes + @strikes = current_account.strikes.recent.latest + end + def require_not_suspended! forbidden if current_account.suspended? end diff --git a/app/controllers/auth/sessions_controller.rb b/app/controllers/auth/sessions_controller.rb index 630b0dee5da85b..c4c8151e33314b 100644 --- a/app/controllers/auth/sessions_controller.rb +++ b/app/controllers/auth/sessions_controller.rb @@ -8,23 +8,17 @@ class Auth::SessionsController < Devise::SessionsController skip_before_action :update_user_sign_in include TwoFactorAuthenticationConcern - include SignInTokenAuthenticationConcern before_action :set_instance_presenter, only: [:new] before_action :set_body_classes - def new - Devise.omniauth_configs.each do |provider, config| - return redirect_to(omniauth_authorize_path(resource_name, provider)) if config.strategy.redirect_at_sign_in - end - - super - end - def create super do |resource| - resource.update_sign_in!(request, new_sign_in: true) - flash.delete(:notice) + # We only need to call this if this hasn't already been + # called from one of the two-factor or sign-in token + # authentication methods + + on_authentication_success(resource, :password) unless @on_authentication_success_called end end @@ -39,9 +33,10 @@ def destroy def webauthn_options user = User.find_by(id: session[:attempt_user_id]) - if user.webauthn_enabled? + if user&.webauthn_enabled? options_for_get = WebAuthn::Credential.options_for_get( - allow: user.webauthn_credentials.pluck(:external_id) + allow: user.webauthn_credentials.pluck(:external_id), + user_verification: 'discouraged' ) session[:webauthn_challenge] = options_for_get.challenge @@ -70,7 +65,7 @@ def find_user_from_params end def user_params - params.require(:user).permit(:email, :password, :otp_attempt, :sign_in_token_attempt, credential: {}) + params.require(:user).permit(:email, :password, :otp_attempt, credential: {}) end def after_sign_in_path_for(resource) @@ -83,14 +78,6 @@ def after_sign_in_path_for(resource) end end - def after_sign_out_path_for(_resource_or_scope) - Devise.omniauth_configs.each_value do |config| - return root_path if config.strategy.redirect_at_sign_in - end - - super - end - def require_no_authentication super @@ -137,4 +124,39 @@ def clear_attempt_from_session session.delete(:attempt_user_id) session.delete(:attempt_user_updated_at) end + + def on_authentication_success(user, security_measure) + @on_authentication_success_called = true + + clear_attempt_from_session + + user.update_sign_in!(new_sign_in: true) + sign_in(user) + flash.delete(:notice) + + LoginActivity.create( + user: user, + success: true, + authentication_method: security_measure, + ip: request.remote_ip, + user_agent: request.user_agent + ) + + UserMailer.suspicious_sign_in(user, request.remote_ip, request.user_agent, Time.now.utc).deliver_later! if suspicious_sign_in?(user) + end + + def suspicious_sign_in?(user) + SuspiciousSignInDetector.new(user).suspicious?(request) + end + + def on_authentication_failure(user, security_measure, failure_reason) + LoginActivity.create( + user: user, + success: false, + authentication_method: security_measure, + failure_reason: failure_reason, + ip: request.remote_ip, + user_agent: request.user_agent + ) + end end diff --git a/app/controllers/concerns/access_token_tracking_concern.rb b/app/controllers/concerns/access_token_tracking_concern.rb new file mode 100644 index 00000000000000..cf60cfb995ba49 --- /dev/null +++ b/app/controllers/concerns/access_token_tracking_concern.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module AccessTokenTrackingConcern + extend ActiveSupport::Concern + + ACCESS_TOKEN_UPDATE_FREQUENCY = 24.hours.freeze + + included do + before_action :update_access_token_last_used + end + + private + + def update_access_token_last_used + doorkeeper_token.update_last_used(request) if access_token_needs_update? + end + + def access_token_needs_update? + doorkeeper_token.present? && (doorkeeper_token.last_used_at.nil? || doorkeeper_token.last_used_at < ACCESS_TOKEN_UPDATE_FREQUENCY.ago) + end +end diff --git a/app/controllers/concerns/account_owned_concern.rb b/app/controllers/concerns/account_owned_concern.rb index 62e379846a6a62..25149d03fb6061 100644 --- a/app/controllers/concerns/account_owned_concern.rb +++ b/app/controllers/concerns/account_owned_concern.rb @@ -8,6 +8,7 @@ module AccountOwnedConcern before_action :set_account, if: :account_required? before_action :check_account_approval, if: :account_required? before_action :check_account_suspension, if: :account_required? + before_action :check_account_confirmation, if: :account_required? end private @@ -28,6 +29,10 @@ def check_account_approval not_found if @account.local? && @account.user_pending? end + def check_account_confirmation + not_found if @account.local? && !@account.user_confirmed? + end + def check_account_suspension if @account.suspended_permanently? permanent_suspension_response diff --git a/app/controllers/concerns/accountable_concern.rb b/app/controllers/concerns/accountable_concern.rb index 3cdcffc51c79bc..87d62478da2844 100644 --- a/app/controllers/concerns/accountable_concern.rb +++ b/app/controllers/concerns/accountable_concern.rb @@ -3,7 +3,7 @@ module AccountableConcern extend ActiveSupport::Concern - def log_action(action, target) - Admin::ActionLog.create(account: current_account, action: action, target: target) + def log_action(action, target, options = {}) + Admin::ActionLog.create(account: current_account, action: action, target: target, recorded_changes: options.stringify_keys) end end diff --git a/app/controllers/concerns/authorization.rb b/app/controllers/concerns/authorization.rb index 95a37e379e80ee..05260cc8ba83fe 100644 --- a/app/controllers/concerns/authorization.rb +++ b/app/controllers/concerns/authorization.rb @@ -3,7 +3,7 @@ module Authorization extend ActiveSupport::Concern - include Pundit + include Pundit::Authorization def pundit_user current_account diff --git a/app/controllers/concerns/localized.rb b/app/controllers/concerns/localized.rb index fe1142f34530d1..ede299d5a4d859 100644 --- a/app/controllers/concerns/localized.rb +++ b/app/controllers/concerns/localized.rb @@ -7,27 +7,28 @@ module Localized around_action :set_locale end - def set_locale - locale = current_user.locale if respond_to?(:user_signed_in?) && user_signed_in? - locale ||= session[:locale] ||= default_locale - locale = default_locale unless I18n.available_locales.include?(locale.to_sym) - - I18n.with_locale(locale) do - yield - end + def set_locale(&block) + I18n.with_locale(requested_locale || I18n.default_locale, &block) end private - def default_locale - if ENV['DEFAULT_LOCALE'].present? - I18n.default_locale - else - request_locale || I18n.default_locale - end + def requested_locale + requested_locale_name = available_locale_or_nil(params[:lang]) + requested_locale_name ||= available_locale_or_nil(current_user.locale) if respond_to?(:user_signed_in?) && user_signed_in? + requested_locale_name ||= http_accept_language if ENV['DEFAULT_LOCALE'].blank? + requested_locale_name end - def request_locale - http_accept_language.language_region_compatible_from(I18n.available_locales) + def http_accept_language + HttpAcceptLanguage::Parser.new(request.headers.fetch('Accept-Language')).language_region_compatible_from(I18n.available_locales) if request.headers.key?('Accept-Language') + end + + def available_locale_or_nil(locale_name) + locale_name.to_sym if locale_name.present? && I18n.available_locales.include?(locale_name.to_sym) + end + + def content_locale + @content_locale ||= I18n.locale.to_s.split(/[_-]/).first end end diff --git a/app/controllers/concerns/session_tracking_concern.rb b/app/controllers/concerns/session_tracking_concern.rb index 45361b019043bf..eaaa4ac59702dc 100644 --- a/app/controllers/concerns/session_tracking_concern.rb +++ b/app/controllers/concerns/session_tracking_concern.rb @@ -3,7 +3,7 @@ module SessionTrackingConcern extend ActiveSupport::Concern - UPDATE_SIGN_IN_HOURS = 24 + SESSION_UPDATE_FREQUENCY = 24.hours.freeze included do before_action :set_session_activity @@ -17,6 +17,6 @@ def set_session_activity end def session_needs_update? - !current_session.nil? && current_session.updated_at < UPDATE_SIGN_IN_HOURS.hours.ago + !current_session.nil? && current_session.updated_at < SESSION_UPDATE_FREQUENCY.ago end end diff --git a/app/controllers/concerns/sign_in_token_authentication_concern.rb b/app/controllers/concerns/sign_in_token_authentication_concern.rb deleted file mode 100644 index 2b5e79115e5358..00000000000000 --- a/app/controllers/concerns/sign_in_token_authentication_concern.rb +++ /dev/null @@ -1,56 +0,0 @@ -# frozen_string_literal: true - -module SignInTokenAuthenticationConcern - extend ActiveSupport::Concern - - included do - prepend_before_action :authenticate_with_sign_in_token, if: :sign_in_token_required?, only: [:create] - end - - def sign_in_token_required? - find_user&.suspicious_sign_in?(request.remote_ip) - end - - def valid_sign_in_token_attempt?(user) - Devise.secure_compare(user.sign_in_token, user_params[:sign_in_token_attempt]) - end - - def authenticate_with_sign_in_token - if user_params[:email].present? - user = self.resource = find_user_from_params - prompt_for_sign_in_token(user) if user&.external_or_valid_password?(user_params[:password]) - elsif session[:attempt_user_id] - user = self.resource = User.find_by(id: session[:attempt_user_id]) - return if user.nil? - - if session[:attempt_user_updated_at] != user.updated_at.to_s - restart_session - elsif user_params.key?(:sign_in_token_attempt) - authenticate_with_sign_in_token_attempt(user) - end - end - end - - def authenticate_with_sign_in_token_attempt(user) - if valid_sign_in_token_attempt?(user) - clear_attempt_from_session - sign_in(user) - else - flash.now[:alert] = I18n.t('users.invalid_sign_in_token') - prompt_for_sign_in_token(user) - end - end - - def prompt_for_sign_in_token(user) - if user.sign_in_token_expired? - user.generate_sign_in_token && user.save - UserMailer.sign_in_token(user, request.remote_ip, request.user_agent, Time.now.utc.to_s).deliver_later! - end - - set_attempt_session(user) - - @body_classes = 'lighter' - - set_locale { render :sign_in_token } - end -end diff --git a/app/controllers/concerns/two_factor_authentication_concern.rb b/app/controllers/concerns/two_factor_authentication_concern.rb index d65f072ffb278f..27f2367a8ec931 100644 --- a/app/controllers/concerns/two_factor_authentication_concern.rb +++ b/app/controllers/concerns/two_factor_authentication_concern.rb @@ -56,19 +56,19 @@ def authenticate_with_two_factor_via_webauthn(user) webauthn_credential = WebAuthn::Credential.from_get(user_params[:credential]) if valid_webauthn_credential?(user, webauthn_credential) - clear_attempt_from_session - sign_in(user) - render json: { redirect_path: root_path }, status: :ok + on_authentication_success(user, :webauthn) + render json: { redirect_path: after_sign_in_path_for(user) }, status: :ok else + on_authentication_failure(user, :webauthn, :invalid_credential) render json: { error: t('webauthn_credentials.invalid_credential') }, status: :unprocessable_entity end end def authenticate_with_two_factor_via_otp(user) if valid_otp_attempt?(user) - clear_attempt_from_session - sign_in(user) + on_authentication_success(user, :otp) else + on_authentication_failure(user, :otp, :invalid_otp_token) flash.now[:alert] = I18n.t('users.invalid_otp_token') prompt_for_two_factor(user) end diff --git a/app/controllers/concerns/user_tracking_concern.rb b/app/controllers/concerns/user_tracking_concern.rb index efda37fae75cb7..e960cce53fee2e 100644 --- a/app/controllers/concerns/user_tracking_concern.rb +++ b/app/controllers/concerns/user_tracking_concern.rb @@ -3,7 +3,7 @@ module UserTrackingConcern extend ActiveSupport::Concern - UPDATE_SIGN_IN_HOURS = 24 + SIGN_IN_UPDATE_FREQUENCY = 24.hours.freeze included do before_action :update_user_sign_in @@ -12,10 +12,10 @@ module UserTrackingConcern private def update_user_sign_in - current_user.update_sign_in!(request) if user_needs_sign_in_update? + current_user.update_sign_in! if user_needs_sign_in_update? end def user_needs_sign_in_update? - user_signed_in? && (current_user.current_sign_in_at.nil? || current_user.current_sign_in_at < UPDATE_SIGN_IN_HOURS.hours.ago) + user_signed_in? && (current_user.current_sign_in_at.nil? || current_user.current_sign_in_at < SIGN_IN_UPDATE_FREQUENCY.ago) end end diff --git a/app/controllers/disputes/appeals_controller.rb b/app/controllers/disputes/appeals_controller.rb new file mode 100644 index 00000000000000..eefd92b5a83c70 --- /dev/null +++ b/app/controllers/disputes/appeals_controller.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +class Disputes::AppealsController < Disputes::BaseController + before_action :set_strike + + def create + authorize @strike, :appeal? + + @appeal = AppealService.new.call(@strike, appeal_params[:text]) + + redirect_to disputes_strike_path(@strike), notice: I18n.t('disputes.strikes.appealed_msg') + rescue ActiveRecord::RecordInvalid => e + @appeal = e.record + render template: 'disputes/strikes/show' + end + + private + + def set_strike + @strike = current_account.strikes.find(params[:strike_id]) + end + + def appeal_params + params.require(:appeal).permit(:text) + end +end diff --git a/app/controllers/disputes/base_controller.rb b/app/controllers/disputes/base_controller.rb new file mode 100644 index 00000000000000..865146b5cc44e7 --- /dev/null +++ b/app/controllers/disputes/base_controller.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +class Disputes::BaseController < ApplicationController + include Authorization + + layout 'admin' + + skip_before_action :require_functional! + + before_action :set_body_classes + before_action :authenticate_user! + + private + + def set_body_classes + @body_classes = 'admin' + end +end diff --git a/app/controllers/disputes/strikes_controller.rb b/app/controllers/disputes/strikes_controller.rb new file mode 100644 index 00000000000000..d85dcb4d57f0da --- /dev/null +++ b/app/controllers/disputes/strikes_controller.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class Disputes::StrikesController < Disputes::BaseController + before_action :set_strike, only: [:show] + + def index + @strikes = current_account.strikes.latest + end + + def show + authorize @strike, :show? + + @appeal = @strike.appeal || @strike.build_appeal + end + + private + + def set_strike + @strike = AccountWarning.find(params[:id]) + end +end diff --git a/app/controllers/follower_accounts_controller.rb b/app/controllers/follower_accounts_controller.rb index b3589a39f86f1d..f3f8336c95fc7d 100644 --- a/app/controllers/follower_accounts_controller.rb +++ b/app/controllers/follower_accounts_controller.rb @@ -15,13 +15,13 @@ def index format.html do expires_in 0, public: true unless user_signed_in? - next if @account.user_hides_network? + next if @account.hide_collections? follows end format.json do - raise Mastodon::NotPermittedError if page_requested? && @account.user_hides_network? + raise Mastodon::NotPermittedError if page_requested? && @account.hide_collections? expires_in(page_requested? ? 0 : 3.minutes, public: public_fetch_mode?) @@ -82,7 +82,7 @@ def collection_presenter end def restrict_fields_to - if page_requested? || !@account.user_hides_network? + if page_requested? || !@account.hide_collections? # Return all fields else %i(id type total_items) diff --git a/app/controllers/following_accounts_controller.rb b/app/controllers/following_accounts_controller.rb index 8a72dc475b95e6..9d7f4c9bf32c73 100644 --- a/app/controllers/following_accounts_controller.rb +++ b/app/controllers/following_accounts_controller.rb @@ -15,13 +15,13 @@ def index format.html do expires_in 0, public: true unless user_signed_in? - next if @account.user_hides_network? + next if @account.hide_collections? follows end format.json do - raise Mastodon::NotPermittedError if page_requested? && @account.user_hides_network? + raise Mastodon::NotPermittedError if page_requested? && @account.hide_collections? expires_in(page_requested? ? 0 : 3.minutes, public: public_fetch_mode?) @@ -82,7 +82,7 @@ def collection_presenter end def restrict_fields_to - if page_requested? || !@account.user_hides_network? + if page_requested? || !@account.hide_collections? # Return all fields else %i(id type total_items) diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb index 702889cd030759..7e443eb9e74e21 100644 --- a/app/controllers/home_controller.rb +++ b/app/controllers/home_controller.rb @@ -14,30 +14,7 @@ def index def redirect_unauthenticated_to_permalinks! return if user_signed_in? - matches = request.path.match(/\A\/web\/(statuses|accounts)\/([\d]+)\z/) - - if matches - case matches[1] - when 'statuses' - status = Status.find_by(id: matches[2]) - - if status&.distributable? - redirect_to(ActivityPub::TagManager.instance.url_for(status)) - return - end - when 'accounts' - account = Account.find_by(id: matches[2]) - - if account - redirect_to(ActivityPub::TagManager.instance.url_for(account)) - return - end - end - end - - matches = request.path.match(%r{\A/web/timelines/tag/(?.+)\z}) - - redirect_to(matches ? tag_path(CGI.unescape(matches[:tag])) : default_redirect_path) + redirect_to(PermalinkRedirector.new(request.path).redirect_path || default_redirect_path) end def default_redirect_path diff --git a/app/controllers/instance_actors_controller.rb b/app/controllers/instance_actors_controller.rb index b3b5476e2f5a18..0853897f20123b 100644 --- a/app/controllers/instance_actors_controller.rb +++ b/app/controllers/instance_actors_controller.rb @@ -3,6 +3,7 @@ class InstanceActorsController < ApplicationController include AccountControllerConcern + skip_before_action :check_account_confirmation skip_around_action :set_locale def show diff --git a/app/controllers/media_controller.rb b/app/controllers/media_controller.rb index ce015dd1b216d3..ee82625a03e9f9 100644 --- a/app/controllers/media_controller.rb +++ b/app/controllers/media_controller.rb @@ -27,7 +27,12 @@ def player private def set_media_attachment - @media_attachment = MediaAttachment.attached.find_by!(shortcode: params[:id] || params[:medium_id]) + id = params[:id] || params[:medium_id] + return if id.nil? + + scope = MediaAttachment.local.attached + # If id is 19 characters long, it's a shortcode, otherwise it's an identifier + @media_attachment = id.size == 19 ? scope.find_by!(shortcode: id) : scope.find_by!(id: id) end def verify_permitted_status! diff --git a/app/controllers/settings/identity_proofs_controller.rb b/app/controllers/settings/identity_proofs_controller.rb deleted file mode 100644 index bf2899da66fc44..00000000000000 --- a/app/controllers/settings/identity_proofs_controller.rb +++ /dev/null @@ -1,60 +0,0 @@ -# frozen_string_literal: true - -class Settings::IdentityProofsController < Settings::BaseController - before_action :check_required_params, only: :new - - def index - @proofs = AccountIdentityProof.where(account: current_account).order(provider: :asc, provider_username: :asc) - @proofs.each(&:refresh!) - end - - def new - @proof = current_account.identity_proofs.new( - token: params[:token], - provider: params[:provider], - provider_username: params[:provider_username] - ) - - if current_account.username.casecmp(params[:username]).zero? - render layout: 'auth' - else - redirect_to settings_identity_proofs_path, alert: I18n.t('identity_proofs.errors.wrong_user', proving: params[:username], current: current_account.username) - end - end - - def create - @proof = current_account.identity_proofs.where(provider: resource_params[:provider], provider_username: resource_params[:provider_username]).first_or_initialize(resource_params) - @proof.token = resource_params[:token] - - if @proof.save - PostStatusService.new.call(current_user.account, text: post_params[:status_text]) if publish_proof? - redirect_to @proof.on_success_path(params[:user_agent]) - else - redirect_to settings_identity_proofs_path, alert: I18n.t('identity_proofs.errors.failed', provider: @proof.provider.capitalize) - end - end - - def destroy - @proof = current_account.identity_proofs.find(params[:id]) - @proof.destroy! - redirect_to settings_identity_proofs_path, success: I18n.t('identity_proofs.removed') - end - - private - - def check_required_params - redirect_to settings_identity_proofs_path unless [:provider, :provider_username, :username, :token].all? { |k| params[k].present? } - end - - def resource_params - params.require(:account_identity_proof).permit(:provider, :provider_username, :token) - end - - def publish_proof? - ActiveModel::Type::Boolean.new.cast(post_params[:post_status]) - end - - def post_params - params.require(:account_identity_proof).permit(:post_status, :status_text) - end -end diff --git a/app/controllers/settings/login_activities_controller.rb b/app/controllers/settings/login_activities_controller.rb new file mode 100644 index 00000000000000..57fa6aef0c8608 --- /dev/null +++ b/app/controllers/settings/login_activities_controller.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class Settings::LoginActivitiesController < Settings::BaseController + def index + @login_activities = LoginActivity.where(user: current_user).order(id: :desc).page(params[:page]) + end +end diff --git a/app/controllers/settings/preferences_controller.rb b/app/controllers/settings/preferences_controller.rb index 32b5d79487b741..bfe651bc667554 100644 --- a/app/controllers/settings/preferences_controller.rb +++ b/app/controllers/settings/preferences_controller.rb @@ -47,7 +47,6 @@ def user_settings_params :setting_system_font_ui, :setting_noindex, :setting_theme, - :setting_hide_network, :setting_aggregate_reblogs, :setting_show_application, :setting_advanced_layout, @@ -55,7 +54,8 @@ def user_settings_params :setting_use_pending_items, :setting_trends, :setting_crop_images, - notification_emails: %i(follow follow_request reblog favourite mention digest report pending_account trending_tag), + :setting_always_send_emails, + notification_emails: %i(follow follow_request reblog favourite mention digest report pending_account trending_tag appeal), interactions: %i(must_be_follower must_be_following must_be_following_dm) ) end diff --git a/app/controllers/settings/profiles_controller.rb b/app/controllers/settings/profiles_controller.rb index 0c15447a6c1343..be5b4f30293f6e 100644 --- a/app/controllers/settings/profiles_controller.rb +++ b/app/controllers/settings/profiles_controller.rb @@ -20,7 +20,7 @@ def update private def account_params - params.require(:account).permit(:display_name, :note, :avatar, :header, :locked, :bot, :discoverable, fields_attributes: [:name, :value]) + params.require(:account).permit(:display_name, :note, :avatar, :header, :locked, :bot, :discoverable, :hide_collections, fields_attributes: [:name, :value]) end def set_account diff --git a/app/controllers/settings/two_factor_authentication/webauthn_credentials_controller.rb b/app/controllers/settings/two_factor_authentication/webauthn_credentials_controller.rb index 1c557092ba53b7..a50d30f06f32c6 100644 --- a/app/controllers/settings/two_factor_authentication/webauthn_credentials_controller.rb +++ b/app/controllers/settings/two_factor_authentication/webauthn_credentials_controller.rb @@ -21,7 +21,8 @@ def options display_name: current_user.account.username, id: current_user.webauthn_id, }, - exclude: current_user.webauthn_credentials.pluck(:external_id) + exclude: current_user.webauthn_credentials.pluck(:external_id), + authenticator_selection: { user_verification: 'discouraged' } ) session[:webauthn_challenge] = options_for_create.challenge diff --git a/app/controllers/statuses_cleanup_controller.rb b/app/controllers/statuses_cleanup_controller.rb new file mode 100644 index 00000000000000..be234cdcb97ca0 --- /dev/null +++ b/app/controllers/statuses_cleanup_controller.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +class StatusesCleanupController < ApplicationController + layout 'admin' + + before_action :authenticate_user! + before_action :set_policy + before_action :set_body_classes + + def show; end + + def update + if @policy.update(resource_params) + redirect_to statuses_cleanup_path, notice: I18n.t('generic.changes_saved_msg') + else + render action: :show + end + rescue ActionController::ParameterMissing + # Do nothing + end + + private + + def set_policy + @policy = current_account.statuses_cleanup_policy || current_account.build_statuses_cleanup_policy(enabled: false) + end + + def resource_params + params.require(:account_statuses_cleanup_policy).permit(:enabled, :min_status_age, :keep_direct, :keep_pinned, :keep_polls, :keep_media, :keep_self_fav, :keep_self_bookmark, :min_favs, :min_reblogs) + end + + def set_body_classes + @body_classes = 'admin' + end +end diff --git a/app/controllers/well_known/keybase_proof_config_controller.rb b/app/controllers/well_known/keybase_proof_config_controller.rb deleted file mode 100644 index e1d43ecbe9a1ad..00000000000000 --- a/app/controllers/well_known/keybase_proof_config_controller.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -module WellKnown - class KeybaseProofConfigController < ActionController::Base - def show - render json: {}, serializer: ProofProvider::Keybase::ConfigSerializer, root: 'keybase_config' - end - end -end diff --git a/app/helpers/accounts_helper.rb b/app/helpers/accounts_helper.rb index db3da2b0507a3a..557f60f26f6df0 100644 --- a/app/helpers/accounts_helper.rb +++ b/app/helpers/accounts_helper.rb @@ -2,10 +2,12 @@ module AccountsHelper def display_name(account, **options) + str = account.display_name.presence || account.username + if options[:custom_emojify] - Formatter.instance.format_display_name(account, **options) + prerender_custom_emojis(h(str), account.emojis) else - account.display_name.presence || account.username + str end end @@ -95,7 +97,7 @@ def account_description(account) ].join(' '), ].join(', ') - [prepend_str, account.note].join(' · ') + [prepend_str, account.note].join(' · ') end def svg_logo diff --git a/app/helpers/admin/account_moderation_notes_helper.rb b/app/helpers/admin/account_moderation_notes_helper.rb index 40b2a5289224be..2f08538ca68e4d 100644 --- a/app/helpers/admin/account_moderation_notes_helper.rb +++ b/app/helpers/admin/account_moderation_notes_helper.rb @@ -1,10 +1,10 @@ # frozen_string_literal: true module Admin::AccountModerationNotesHelper - def admin_account_link_to(account) + def admin_account_link_to(account, path: nil) return if account.nil? - link_to admin_account_path(account.id), class: name_tag_classes(account), title: account.acct do + link_to path || admin_account_path(account.id), class: name_tag_classes(account), title: account.acct do safe_join([ image_tag(account.avatar.url, width: 15, height: 15, alt: display_name(account), class: 'avatar'), content_tag(:span, account.acct, class: 'username'), diff --git a/app/helpers/admin/action_logs_helper.rb b/app/helpers/admin/action_logs_helper.rb index e9a298a248e8aa..47eeeaac3a8eed 100644 --- a/app/helpers/admin/action_logs_helper.rb +++ b/app/helpers/admin/action_logs_helper.rb @@ -31,11 +31,17 @@ def linkable_log_target(record) link_to truncate(record.text), edit_admin_announcement_path(record.id) when 'IpBlock' "#{record.ip}/#{record.ip.prefix} (#{I18n.t("simple_form.labels.ip_block.severities.#{record.severity}")})" + when 'Instance' + record.domain + when 'Appeal' + link_to record.account.acct, disputes_strike_path(record.strike) end end def log_target_from_history(type, attributes) case type + when 'User' + attributes['username'] when 'CustomEmoji' attributes['shortcode'] when 'DomainBlock', 'DomainAllow', 'EmailDomainBlock', 'UnavailableDomain' @@ -52,6 +58,8 @@ def log_target_from_history(type, attributes) truncate(attributes['text'].is_a?(Array) ? attributes['text'].last : attributes['text']) when 'IpBlock' "#{attributes['ip']}/#{attributes['ip'].prefix} (#{I18n.t("simple_form.labels.ip_block.severities.#{attributes['severity']}")})" + when 'Instance' + attributes['domain'] end end end diff --git a/app/helpers/admin/dashboard_helper.rb b/app/helpers/admin/dashboard_helper.rb index 4ee2cdef473f54..c21d4134190ad2 100644 --- a/app/helpers/admin/dashboard_helper.rb +++ b/app/helpers/admin/dashboard_helper.rb @@ -1,10 +1,41 @@ # frozen_string_literal: true module Admin::DashboardHelper - def feature_hint(feature, enabled) - indicator = safe_join([enabled ? t('simple_form.yes') : t('simple_form.no'), fa_icon('power-off fw')], ' ') - class_names = enabled ? 'pull-right positive-hint' : 'pull-right neutral-hint' + def relevant_account_ip(account, ip_query) + ips = account.user.present? ? account.user.ips.to_a : [] - safe_join([feature, content_tag(:span, indicator, class: class_names)]) + matched_ip = begin + ip_query_addr = IPAddr.new(ip_query) + ips.find { |ip| ip_query_addr.include?(ip.ip) } || ips.first + rescue IPAddr::Error + ips.first + end + + if matched_ip + link_to matched_ip.ip, admin_accounts_path(ip: matched_ip.ip) + else + '-' + end + end + + def relevant_account_timestamp(account) + timestamp, exact = begin + if account.user_current_sign_in_at && account.user_current_sign_in_at < 24.hours.ago + [account.user_current_sign_in_at, true] + elsif account.user_current_sign_in_at + [account.user_current_sign_in_at, false] + elsif account.user_pending? + [account.user_created_at, true] + elsif account.last_status_at.present? + [account.last_status_at, true] + else + [nil, false] + end + end + + return '-' if timestamp.nil? + return t('generic.today') unless exact + + content_tag(:time, l(timestamp), class: 'time-ago', datetime: timestamp.iso8601, title: l(timestamp)) end end diff --git a/app/helpers/admin/filter_helper.rb b/app/helpers/admin/filter_helper.rb index ba0ca963895efb..140fc73ede4124 100644 --- a/app/helpers/admin/filter_helper.rb +++ b/app/helpers/admin/filter_helper.rb @@ -5,12 +5,16 @@ module Admin::FilterHelper AccountFilter::KEYS, CustomEmojiFilter::KEYS, ReportFilter::KEYS, - TagFilter::KEYS, + Trends::TagFilter::KEYS, + Trends::PreviewCardProviderFilter::KEYS, + Trends::PreviewCardFilter::KEYS, + Trends::StatusFilter::KEYS, InstanceFilter::KEYS, InviteFilter::KEYS, RelationshipFilter::KEYS, AnnouncementFilter::KEYS, Admin::ActionLogFilter::KEYS, + Admin::StatusFilter::KEYS, ].flatten.freeze def filter_link_to(text, link_to_params, link_class_params = link_to_params) diff --git a/app/helpers/admin/trends/statuses_helper.rb b/app/helpers/admin/trends/statuses_helper.rb new file mode 100644 index 00000000000000..214c1e2a6854e8 --- /dev/null +++ b/app/helpers/admin/trends/statuses_helper.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module Admin::Trends::StatusesHelper + def one_line_preview(status) + text = begin + if status.local? + status.text.split("\n").first + else + Nokogiri::HTML(status.text).css('html > body > *').first&.text + end + end + + return '' if text.blank? + + prerender_custom_emojis(h(text), status.emojis) + end +end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index a39e8e5bf85e59..9984820da7a450 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -9,9 +9,9 @@ module ApplicationHelper RTL_LOCALES = %i( ar + ckb fa he - ku ).freeze def friendly_number_to_human(number, **options) @@ -19,8 +19,11 @@ def friendly_number_to_human(number, **options) # is looked up from the locales definition, and rails-i18n comes with # values that don't seem to make much sense for many languages, so # override these values with a default of 3 digits of precision. - options[:precision] = 3 - options[:strip_insignificant_zeros] = true + options = options.merge( + precision: 3, + strip_insignificant_zeros: true, + significant: true + ) number_to_human(number, **options) end @@ -50,13 +53,39 @@ def closed_registrations? end def available_sign_up_path - if closed_registrations? + if closed_registrations? || omniauth_only? 'https://joinmastodon.org/#getting-started' else new_user_registration_path end end + def omniauth_only? + ENV['OMNIAUTH_ONLY'] == 'true' + end + + def link_to_login(name = nil, html_options = nil, &block) + target = new_user_session_path + + html_options = name if block_given? + + if omniauth_only? && Devise.mappings[:user].omniauthable? && User.omniauth_providers.size == 1 + target = omniauth_authorize_path(:user, User.omniauth_providers[0]) + html_options ||= {} + html_options[:method] = :post + end + + if block_given? + link_to(target, html_options, &block) + else + link_to(name, target, html_options) + end + end + + def provider_sign_in_link(provider) + link_to I18n.t("auth.providers.#{provider}", default: provider.to_s.chomp('_oauth2').capitalize), omniauth_authorize_path(:user, provider), class: "button button-#{provider}", method: :post + end + def open_deletion? Setting.open_deletion end @@ -137,6 +166,10 @@ def react_component(name, props = {}, &block) end end + def react_admin_component(name, props = {}) + content_tag(:div, nil, data: { 'admin-component': name.to_s.camelcase, props: Oj.dump({ locale: I18n.locale }.merge(props)) }) + end + def body_classes output = (@body_classes || '').split(' ') output << "theme-#{current_theme.parameterize}" @@ -194,4 +227,23 @@ def render_initial_state content_tag(:script, json_escape(json).html_safe, id: 'initial-state', type: 'application/json') # rubocop:enable Rails/OutputSafety end + + def grouped_scopes(scopes) + scope_parser = ScopeParser.new + scope_transformer = ScopeTransformer.new + + scopes.each_with_object({}) do |str, h| + scope = scope_transformer.apply(scope_parser.parse(str)) + + if h[scope.key] + h[scope.key].merge!(scope) + else + h[scope.key] = scope + end + end.values + end + + def prerender_custom_emojis(html, custom_emojis) + EmojiFormatter.new(html, custom_emojis, animate: prefers_autoplay?).to_s + end end diff --git a/app/helpers/context_helper.rb b/app/helpers/context_helper.rb index 2c7541ad4d9bba..08cfa9c6d57f79 100644 --- a/app/helpers/context_helper.rb +++ b/app/helpers/context_helper.rb @@ -18,7 +18,6 @@ module ContextHelper atom_uri: { 'ostatus' => 'http://ostatus.org#', 'atomUri' => 'ostatus:atomUri' }, conversation: { 'ostatus' => 'http://ostatus.org#', 'inReplyToAtomUri' => 'ostatus:inReplyToAtomUri', 'conversation' => 'ostatus:conversation' }, focal_point: { 'toot' => 'http://joinmastodon.org/ns#', 'focalPoint' => { '@container' => '@list', '@id' => 'toot:focalPoint' } }, - identity_proof: { 'toot' => 'http://joinmastodon.org/ns#', 'IdentityProof' => 'toot:IdentityProof' }, blurhash: { 'toot' => 'http://joinmastodon.org/ns#', 'blurhash' => 'toot:blurhash' }, discoverable: { 'toot' => 'http://joinmastodon.org/ns#', 'discoverable' => 'toot:discoverable' }, voters_count: { 'toot' => 'http://joinmastodon.org/ns#', 'votersCount' => 'toot:votersCount' }, diff --git a/app/helpers/formatting_helper.rb b/app/helpers/formatting_helper.rb new file mode 100644 index 00000000000000..a58dd608f84dd3 --- /dev/null +++ b/app/helpers/formatting_helper.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module FormattingHelper + def html_aware_format(text, local, options = {}) + HtmlAwareFormatter.new(text, local, options).to_s + end + + def linkify(text, options = {}) + TextFormatter.new(text, options).to_s + end + + def extract_status_plain_text(status) + PlainTextFormatter.new(status.text, status.local?).to_s + end + module_function :extract_status_plain_text + + def status_content_format(status) + html_aware_format(status.text, status.local?, preloaded_accounts: [status.account] + (status.respond_to?(:active_mentions) ? status.active_mentions.map(&:account) : [])) + end + + def account_bio_format(account) + html_aware_format(account.note, account.local?) + end + + def account_field_value_format(field, with_rel_me: true) + html_aware_format(field.value, field.account.local?, with_rel_me: with_rel_me, with_domains: true, multiline: false) + end +end diff --git a/app/helpers/jsonld_helper.rb b/app/helpers/jsonld_helper.rb index d9924be3bdac77..102e4b13281ad7 100644 --- a/app/helpers/jsonld_helper.rb +++ b/app/helpers/jsonld_helper.rb @@ -15,6 +15,14 @@ def first_of_value(value) value.is_a?(Array) ? value.first : value end + def uri_from_bearcap(str) + if str&.start_with?('bear:') + Addressable::URI.parse(str).query_values['u'] + else + str + end + end + # The url attribute can be a string, an array of strings, or an array of objects. # The objects could include a mimeType. Not-included mimeType means it's text/html. def url_to_href(value, preferred_type = nil) @@ -36,7 +44,13 @@ def url_to_href(value, preferred_type = nil) end def as_array(value) - value.is_a?(Array) ? value : [value] + if value.nil? + [] + elsif value.is_a?(Array) + value + else + [value] + end end def value_or_id(value) @@ -48,7 +62,7 @@ def supported_context?(json) end def unsupported_uri_scheme?(uri) - !uri.start_with?('http://', 'https://') + uri.nil? || !uri.start_with?('http://', 'https://') end def invalid_origin?(url) diff --git a/app/helpers/languages_helper.rb b/app/helpers/languages_helper.rb new file mode 100644 index 00000000000000..d39bb6c930d820 --- /dev/null +++ b/app/helpers/languages_helper.rb @@ -0,0 +1,257 @@ +# frozen_string_literal: true + +module LanguagesHelper + ISO_639_1 = { + aa: ['Afar', 'Afaraf'].freeze, + ab: ['Abkhaz', 'аҧсуа бызшәа'].freeze, + ae: ['Avestan', 'avesta'].freeze, + af: ['Afrikaans', 'Afrikaans'].freeze, + ak: ['Akan', 'Akan'].freeze, + am: ['Amharic', 'አማርኛ'].freeze, + an: ['Aragonese', 'aragonés'].freeze, + ar: ['Arabic', 'اللغة العربية'].freeze, + as: ['Assamese', 'অসমীয়া'].freeze, + av: ['Avaric', 'авар мацӀ'].freeze, + ay: ['Aymara', 'aymar aru'].freeze, + az: ['Azerbaijani', 'azərbaycan dili'].freeze, + ba: ['Bashkir', 'башҡорт теле'].freeze, + be: ['Belarusian', 'беларуская мова'].freeze, + bg: ['Bulgarian', 'български език'].freeze, + bh: ['Bihari', 'भोजपुरी'].freeze, + bi: ['Bislama', 'Bislama'].freeze, + bm: ['Bambara', 'bamanankan'].freeze, + bn: ['Bengali', 'বাংলা'].freeze, + bo: ['Tibetan', 'བོད་ཡིག'].freeze, + br: ['Breton', 'brezhoneg'].freeze, + bs: ['Bosnian', 'bosanski jezik'].freeze, + ca: ['Catalan', 'Català'].freeze, + ce: ['Chechen', 'нохчийн мотт'].freeze, + ch: ['Chamorro', 'Chamoru'].freeze, + co: ['Corsican', 'corsu'].freeze, + cr: ['Cree', 'ᓀᐦᐃᔭᐍᐏᐣ'].freeze, + cs: ['Czech', 'čeština'].freeze, + cu: ['Old Church Slavonic', 'ѩзыкъ словѣньскъ'].freeze, + cv: ['Chuvash', 'чӑваш чӗлхи'].freeze, + cy: ['Welsh', 'Cymraeg'].freeze, + da: ['Danish', 'dansk'].freeze, + de: ['German', 'Deutsch'].freeze, + dv: ['Divehi', 'Dhivehi'].freeze, + dz: ['Dzongkha', 'རྫོང་ཁ'].freeze, + ee: ['Ewe', 'Eʋegbe'].freeze, + el: ['Greek', 'Ελληνικά'].freeze, + en: ['English', 'English'].freeze, + eo: ['Esperanto', 'Esperanto'].freeze, + es: ['Spanish', 'Español'].freeze, + et: ['Estonian', 'eesti'].freeze, + eu: ['Basque', 'euskara'].freeze, + fa: ['Persian', 'فارسی'].freeze, + ff: ['Fula', 'Fulfulde'].freeze, + fi: ['Finnish', 'suomi'].freeze, + fj: ['Fijian', 'Vakaviti'].freeze, + fo: ['Faroese', 'føroyskt'].freeze, + fr: ['French', 'Français'].freeze, + fy: ['Western Frisian', 'Frysk'].freeze, + ga: ['Irish', 'Gaeilge'].freeze, + gd: ['Scottish Gaelic', 'Gàidhlig'].freeze, + gl: ['Galician', 'galego'].freeze, + gu: ['Gujarati', 'ગુજરાતી'].freeze, + gv: ['Manx', 'Gaelg'].freeze, + ha: ['Hausa', 'هَوُسَ'].freeze, + he: ['Hebrew', 'עברית'].freeze, + hi: ['Hindi', 'हिन्दी'].freeze, + ho: ['Hiri Motu', 'Hiri Motu'].freeze, + hr: ['Croatian', 'Hrvatski'].freeze, + ht: ['Haitian', 'Kreyòl ayisyen'].freeze, + hu: ['Hungarian', 'magyar'].freeze, + hy: ['Armenian', 'Հայերեն'].freeze, + hz: ['Herero', 'Otjiherero'].freeze, + ia: ['Interlingua', 'Interlingua'].freeze, + id: ['Indonesian', 'Bahasa Indonesia'].freeze, + ie: ['Interlingue', 'Interlingue'].freeze, + ig: ['Igbo', 'Asụsụ Igbo'].freeze, + ii: ['Nuosu', 'ꆈꌠ꒿ Nuosuhxop'].freeze, + ik: ['Inupiaq', 'Iñupiaq'].freeze, + io: ['Ido', 'Ido'].freeze, + is: ['Icelandic', 'Íslenska'].freeze, + it: ['Italian', 'Italiano'].freeze, + iu: ['Inuktitut', 'ᐃᓄᒃᑎᑐᑦ'].freeze, + ja: ['Japanese', '日本語'].freeze, + jv: ['Javanese', 'basa Jawa'].freeze, + ka: ['Georgian', 'ქართული'].freeze, + kg: ['Kongo', 'Kikongo'].freeze, + ki: ['Kikuyu', 'Gĩkũyũ'].freeze, + kj: ['Kwanyama', 'Kuanyama'].freeze, + kk: ['Kazakh', 'қазақ тілі'].freeze, + kl: ['Kalaallisut', 'kalaallisut'].freeze, + km: ['Khmer', 'ខេមរភាសា'].freeze, + kn: ['Kannada', 'ಕನ್ನಡ'].freeze, + ko: ['Korean', '한국어'].freeze, + kr: ['Kanuri', 'Kanuri'].freeze, + ks: ['Kashmiri', 'कश्मीरी'].freeze, + ku: ['Kurmanji (Kurdish)', 'Kurmancî'].freeze, + kv: ['Komi', 'коми кыв'].freeze, + kw: ['Cornish', 'Kernewek'].freeze, + ky: ['Kyrgyz', 'Кыргызча'].freeze, + la: ['Latin', 'latine'].freeze, + lb: ['Luxembourgish', 'Lëtzebuergesch'].freeze, + lg: ['Ganda', 'Luganda'].freeze, + li: ['Limburgish', 'Limburgs'].freeze, + ln: ['Lingala', 'Lingála'].freeze, + lo: ['Lao', 'ພາສາ'].freeze, + lt: ['Lithuanian', 'lietuvių kalba'].freeze, + lu: ['Luba-Katanga', 'Tshiluba'].freeze, + lv: ['Latvian', 'latviešu valoda'].freeze, + mg: ['Malagasy', 'fiteny malagasy'].freeze, + mh: ['Marshallese', 'Kajin M̧ajeļ'].freeze, + mi: ['Māori', 'te reo Māori'].freeze, + mk: ['Macedonian', 'македонски јазик'].freeze, + ml: ['Malayalam', 'മലയാളം'].freeze, + mn: ['Mongolian', 'Монгол хэл'].freeze, + mr: ['Marathi', 'मराठी'].freeze, + ms: ['Malay', 'Bahasa Melayu'].freeze, + mt: ['Maltese', 'Malti'].freeze, + my: ['Burmese', 'ဗမာစာ'].freeze, + na: ['Nauru', 'Ekakairũ Naoero'].freeze, + nb: ['Norwegian Bokmål', 'Norsk bokmål'].freeze, + nd: ['Northern Ndebele', 'isiNdebele'].freeze, + ne: ['Nepali', 'नेपाली'].freeze, + ng: ['Ndonga', 'Owambo'].freeze, + nl: ['Dutch', 'Nederlands'].freeze, + nn: ['Norwegian Nynorsk', 'Norsk Nynorsk'].freeze, + no: ['Norwegian', 'Norsk'].freeze, + nr: ['Southern Ndebele', 'isiNdebele'].freeze, + nv: ['Navajo', 'Diné bizaad'].freeze, + ny: ['Chichewa', 'chiCheŵa'].freeze, + oc: ['Occitan', 'occitan'].freeze, + oj: ['Ojibwe', 'ᐊᓂᔑᓈᐯᒧᐎᓐ'].freeze, + om: ['Oromo', 'Afaan Oromoo'].freeze, + or: ['Oriya', 'ଓଡ଼ିଆ'].freeze, + os: ['Ossetian', 'ирон æвзаг'].freeze, + pa: ['Panjabi', 'ਪੰਜਾਬੀ'].freeze, + pi: ['Pāli', 'पाऴि'].freeze, + pl: ['Polish', 'Polski'].freeze, + ps: ['Pashto', 'پښتو'].freeze, + pt: ['Portuguese', 'Português'].freeze, + qu: ['Quechua', 'Runa Simi'].freeze, + rm: ['Romansh', 'rumantsch grischun'].freeze, + rn: ['Kirundi', 'Ikirundi'].freeze, + ro: ['Romanian', 'Română'].freeze, + ru: ['Russian', 'Русский'].freeze, + rw: ['Kinyarwanda', 'Ikinyarwanda'].freeze, + sa: ['Sanskrit', 'संस्कृतम्'].freeze, + sc: ['Sardinian', 'sardu'].freeze, + sd: ['Sindhi', 'सिन्धी'].freeze, + se: ['Northern Sami', 'Davvisámegiella'].freeze, + sg: ['Sango', 'yângâ tî sängö'].freeze, + si: ['Sinhala', 'සිංහල'].freeze, + sk: ['Slovak', 'slovenčina'].freeze, + sl: ['Slovenian', 'slovenščina'].freeze, + sn: ['Shona', 'chiShona'].freeze, + so: ['Somali', 'Soomaaliga'].freeze, + sq: ['Albanian', 'Shqip'].freeze, + sr: ['Serbian', 'српски језик'].freeze, + ss: ['Swati', 'SiSwati'].freeze, + st: ['Southern Sotho', 'Sesotho'].freeze, + su: ['Sundanese', 'Basa Sunda'].freeze, + sv: ['Swedish', 'Svenska'].freeze, + sw: ['Swahili', 'Kiswahili'].freeze, + ta: ['Tamil', 'தமிழ்'].freeze, + te: ['Telugu', 'తెలుగు'].freeze, + tg: ['Tajik', 'тоҷикӣ'].freeze, + th: ['Thai', 'ไทย'].freeze, + ti: ['Tigrinya', 'ትግርኛ'].freeze, + tk: ['Turkmen', 'Türkmen'].freeze, + tl: ['Tagalog', 'Wikang Tagalog'].freeze, + tn: ['Tswana', 'Setswana'].freeze, + to: ['Tonga', 'faka Tonga'].freeze, + tr: ['Turkish', 'Türkçe'].freeze, + ts: ['Tsonga', 'Xitsonga'].freeze, + tt: ['Tatar', 'татар теле'].freeze, + tw: ['Twi', 'Twi'].freeze, + ty: ['Tahitian', 'Reo Tahiti'].freeze, + ug: ['Uyghur', 'ئۇيغۇرچە‎'].freeze, + uk: ['Ukrainian', 'Українська'].freeze, + ur: ['Urdu', 'اردو'].freeze, + uz: ['Uzbek', 'Ўзбек'].freeze, + ve: ['Venda', 'Tshivenḓa'].freeze, + vi: ['Vietnamese', 'Tiếng Việt'].freeze, + vo: ['Volapük', 'Volapük'].freeze, + wa: ['Walloon', 'walon'].freeze, + wo: ['Wolof', 'Wollof'].freeze, + xh: ['Xhosa', 'isiXhosa'].freeze, + yi: ['Yiddish', 'ייִדיש'].freeze, + yo: ['Yoruba', 'Yorùbá'].freeze, + za: ['Zhuang', 'Saɯ cueŋƅ'].freeze, + zh: ['Chinese', '中文'].freeze, + zu: ['Zulu', 'isiZulu'].freeze, + }.freeze + + ISO_639_3 = { + ast: ['Asturian', 'Asturianu'].freeze, + ckb: ['Sorani (Kurdish)', 'سۆرانی'].freeze, + kab: ['Kabyle', 'Taqbaylit'].freeze, + kmr: ['Kurmanji (Kurdish)', 'Kurmancî'].freeze, + zgh: ['Standard Moroccan Tamazight', 'ⵜⴰⵎⴰⵣⵉⵖⵜ'].freeze, + }.freeze + + SUPPORTED_LOCALES = {}.merge(ISO_639_1).merge(ISO_639_3).freeze + + # For ISO-639-1 and ISO-639-3 language codes, we have their official + # names, but for some translations, we need the names of the + # regional variants specifically + REGIONAL_LOCALE_NAMES = { + 'es-AR': 'Español (Argentina)', + 'es-MX': 'Español (México)', + 'pt-BR': 'Português (Brasil)', + 'pt-PT': 'Português (Portugal)', + 'sr-Latn': 'Srpski (latinica)', + 'zh-CN': '简体中文', + 'zh-HK': '繁體中文(香港)', + 'zh-TW': '繁體中文(臺灣)', + }.freeze + + def native_locale_name(locale) + if locale.blank? || locale == 'und' + I18n.t('generic.none') + elsif (supported_locale = SUPPORTED_LOCALES[locale.to_sym]) + supported_locale[1] + elsif (regional_locale = REGIONAL_LOCALE_NAMES[locale.to_sym]) + regional_locale + else + locale + end + end + + def standard_locale_name(locale) + if locale.blank? + I18n.t('generic.none') + elsif (supported_locale = SUPPORTED_LOCALES[locale.to_sym]) + supported_locale[0] + else + locale + end + end + + def valid_locale_or_nil(str) + return if str.blank? + + code, = str.to_s.split(/[_-]/) # Strip out the region from e.g. en_US or ja-JP + + return unless valid_locale?(code) + + code + end + + def valid_locale_cascade(*arr) + arr.each do |str| + locale = valid_locale_or_nil(str) + return locale if locale.present? + end + + nil + end + + def valid_locale?(locale) + locale.present? && SUPPORTED_LOCALES.key?(locale.to_sym) + end +end diff --git a/app/helpers/routing_helper.rb b/app/helpers/routing_helper.rb index fb24a1b28ce7cd..f95f46a5609755 100644 --- a/app/helpers/routing_helper.rb +++ b/app/helpers/routing_helper.rb @@ -2,6 +2,7 @@ module RoutingHelper extend ActiveSupport::Concern + include Rails.application.routes.url_helpers include ActionView::Helpers::AssetTagHelper include Webpacker::Helper @@ -22,8 +23,6 @@ def full_pack_url(source, **options) full_asset_url(asset_pack_path(source, **options)) end - private - def use_storage? Rails.configuration.x.use_s3 || Rails.configuration.x.use_swift end diff --git a/app/helpers/settings_helper.rb b/app/helpers/settings_helper.rb index 0ebfab75d60502..3d5592867cfe26 100644 --- a/app/helpers/settings_helper.rb +++ b/app/helpers/settings_helper.rb @@ -1,94 +1,8 @@ # frozen_string_literal: true module SettingsHelper - HUMAN_LOCALES = { - af: 'Afrikaans', - ar: 'العربية', - ast: 'Asturianu', - bg: 'Български', - bn: 'বাংলা', - br: 'Breton', - ca: 'Català', - co: 'Corsu', - cs: 'Čeština', - cy: 'Cymraeg', - da: 'Dansk', - de: 'Deutsch', - el: 'Ελληνικά', - en: 'English', - eo: 'Esperanto', - 'es-AR': 'Español (Argentina)', - 'es-MX': 'Español (México)', - es: 'Español', - et: 'Eesti', - eu: 'Euskara', - fa: 'فارسی', - fi: 'Suomi', - fr: 'Français', - ga: 'Gaeilge', - gd: 'Gàidhlig', - gl: 'Galego', - he: 'עברית', - hi: 'हिन्दी', - hr: 'Hrvatski', - hu: 'Magyar', - hy: 'Հայերեն', - id: 'Bahasa Indonesia', - io: 'Ido', - is: 'Íslenska', - it: 'Italiano', - ja: '日本語', - ka: 'ქართული', - kab: 'Taqbaylit', - kk: 'Қазақша', - kn: 'ಕನ್ನಡ', - ko: '한국어', - ku: 'سۆرانی', - lt: 'Lietuvių', - lv: 'Latviešu', - mk: 'Македонски', - ml: 'മലയാളം', - mr: 'मराठी', - ms: 'Bahasa Melayu', - nl: 'Nederlands', - nn: 'Nynorsk', - no: 'Norsk', - oc: 'Occitan', - pl: 'Polski', - 'pt-BR': 'Português (Brasil)', - 'pt-PT': 'Português (Portugal)', - pt: 'Português', - ro: 'Română', - ru: 'Русский', - sa: 'संस्कृतम्', - sc: 'Sardu', - si: 'සිංහල', - sk: 'Slovenčina', - sl: 'Slovenščina', - sq: 'Shqip', - 'sr-Latn': 'Srpski (latinica)', - sr: 'Српски', - sv: 'Svenska', - ta: 'தமிழ்', - te: 'తెలుగు', - th: 'ไทย', - tr: 'Türkçe', - uk: 'Українська', - ur: 'اُردُو', - vi: 'Tiếng Việt', - zgh: 'ⵜⴰⵎⴰⵣⵉⵖⵜ', - 'zh-CN': '简体中文', - 'zh-HK': '繁體中文(香港)', - 'zh-TW': '繁體中文(臺灣)', - zh: '中文', - }.freeze - - def human_locale(locale) - HUMAN_LOCALES[locale] - end - def filterable_languages - LanguageDetector.instance.language_names.select(&HUMAN_LOCALES.method(:key?)) + LanguagesHelper::SUPPORTED_LOCALES.keys end def hash_to_object(hash) diff --git a/app/helpers/statuses_helper.rb b/app/helpers/statuses_helper.rb index 25f079e9df8286..e92b4c839000de 100644 --- a/app/helpers/statuses_helper.rb +++ b/app/helpers/statuses_helper.rb @@ -113,26 +113,12 @@ def sensitized?(status, account) end end - private - - def simplified_text(text) - text.dup.tap do |new_text| - URI.extract(new_text).each do |url| - new_text.gsub!(url, '') - end - - new_text.gsub!(Account::MENTION_RE, '') - new_text.gsub!(Tag::HASHTAG_RE, '') - new_text.gsub!(/\s+/, '') - end - end - def embedded_view? params[:controller] == EMBEDDED_CONTROLLER && params[:action] == EMBEDDED_ACTION end def render_video_component(status, **options) - video = status.media_attachments.first + video = status.ordered_media_attachments.first meta = video.file.meta || {} @@ -150,12 +136,12 @@ def render_video_component(status, **options) }.merge(**options) react_component :video, component_params do - render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments } + render partial: 'statuses/attachment_list', locals: { attachments: status.ordered_media_attachments } end end def render_audio_component(status, **options) - audio = status.media_attachments.first + audio = status.ordered_media_attachments.first meta = audio.file.meta || {} @@ -170,7 +156,7 @@ def render_audio_component(status, **options) }.merge(**options) react_component :audio, component_params do - render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments } + render partial: 'statuses/attachment_list', locals: { attachments: status.ordered_media_attachments } end end @@ -178,11 +164,11 @@ def render_media_gallery_component(status, **options) component_params = { sensitive: sensitized?(status, current_account), autoplay: prefers_autoplay?, - media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json }, + media: status.ordered_media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json }, }.merge(**options) react_component :media_gallery, component_params do - render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments } + render partial: 'statuses/attachment_list', locals: { attachments: status.ordered_media_attachments } end end diff --git a/app/javascript/mastodon/actions/accounts.js b/app/javascript/mastodon/actions/accounts.js index 58b63660260971..ce7bb6d5f09fd8 100644 --- a/app/javascript/mastodon/actions/accounts.js +++ b/app/javascript/mastodon/actions/accounts.js @@ -5,6 +5,10 @@ export const ACCOUNT_FETCH_REQUEST = 'ACCOUNT_FETCH_REQUEST'; export const ACCOUNT_FETCH_SUCCESS = 'ACCOUNT_FETCH_SUCCESS'; export const ACCOUNT_FETCH_FAIL = 'ACCOUNT_FETCH_FAIL'; +export const ACCOUNT_LOOKUP_REQUEST = 'ACCOUNT_LOOKUP_REQUEST'; +export const ACCOUNT_LOOKUP_SUCCESS = 'ACCOUNT_LOOKUP_SUCCESS'; +export const ACCOUNT_LOOKUP_FAIL = 'ACCOUNT_LOOKUP_FAIL'; + export const ACCOUNT_FOLLOW_REQUEST = 'ACCOUNT_FOLLOW_REQUEST'; export const ACCOUNT_FOLLOW_SUCCESS = 'ACCOUNT_FOLLOW_SUCCESS'; export const ACCOUNT_FOLLOW_FAIL = 'ACCOUNT_FOLLOW_FAIL'; @@ -87,6 +91,34 @@ export function fetchAccount(id) { }; }; +export const lookupAccount = acct => (dispatch, getState) => { + dispatch(lookupAccountRequest(acct)); + + api(getState).get('/api/v1/accounts/lookup', { params: { acct } }).then(response => { + dispatch(fetchRelationships([response.data.id])); + dispatch(importFetchedAccount(response.data)); + dispatch(lookupAccountSuccess()); + }).catch(error => { + dispatch(lookupAccountFail(acct, error)); + }); +}; + +export const lookupAccountRequest = (acct) => ({ + type: ACCOUNT_LOOKUP_REQUEST, + acct, +}); + +export const lookupAccountSuccess = () => ({ + type: ACCOUNT_LOOKUP_SUCCESS, +}); + +export const lookupAccountFail = (acct, error) => ({ + type: ACCOUNT_LOOKUP_FAIL, + acct, + error, + skipAlert: true, +}); + export function fetchAccountRequest(id) { return { type: ACCOUNT_FETCH_REQUEST, diff --git a/app/javascript/mastodon/actions/compose.js b/app/javascript/mastodon/actions/compose.js index 891403969e2f1d..b0a217550ff3c3 100644 --- a/app/javascript/mastodon/actions/compose.js +++ b/app/javascript/mastodon/actions/compose.js @@ -9,6 +9,7 @@ import { importFetchedAccounts } from './importer'; import { updateTimeline } from './timelines'; import { showAlertForError } from './alerts'; import { showAlert } from './alerts'; +import { openModal } from './modal'; import { defineMessages } from 'react-intl'; let cancelFetchComposeSuggestionsAccounts, cancelFetchComposeSuggestionsTags; @@ -36,6 +37,7 @@ export const THUMBNAIL_UPLOAD_PROGRESS = 'THUMBNAIL_UPLOAD_PROGRESS'; export const COMPOSE_SUGGESTIONS_CLEAR = 'COMPOSE_SUGGESTIONS_CLEAR'; export const COMPOSE_SUGGESTIONS_READY = 'COMPOSE_SUGGESTIONS_READY'; export const COMPOSE_SUGGESTION_SELECT = 'COMPOSE_SUGGESTION_SELECT'; +export const COMPOSE_SUGGESTION_IGNORE = 'COMPOSE_SUGGESTION_IGNORE'; export const COMPOSE_SUGGESTION_TAGS_UPDATE = 'COMPOSE_SUGGESTION_TAGS_UPDATE'; export const COMPOSE_TAG_HISTORY_UPDATE = 'COMPOSE_TAG_HISTORY_UPDATE'; @@ -63,6 +65,13 @@ export const COMPOSE_POLL_OPTION_CHANGE = 'COMPOSE_POLL_OPTION_CHANGE'; export const COMPOSE_POLL_OPTION_REMOVE = 'COMPOSE_POLL_OPTION_REMOVE'; export const COMPOSE_POLL_SETTINGS_CHANGE = 'COMPOSE_POLL_SETTINGS_CHANGE'; +export const INIT_MEDIA_EDIT_MODAL = 'INIT_MEDIA_EDIT_MODAL'; + +export const COMPOSE_CHANGE_MEDIA_DESCRIPTION = 'COMPOSE_CHANGE_MEDIA_DESCRIPTION'; +export const COMPOSE_CHANGE_MEDIA_FOCUS = 'COMPOSE_CHANGE_MEDIA_FOCUS'; + +export const COMPOSE_SET_STATUS = 'COMPOSE_SET_STATUS'; + const messages = defineMessages({ uploadErrorLimit: { id: 'upload_error.limit', defaultMessage: 'File upload limit exceeded.' }, uploadErrorPoll: { id: 'upload_error.poll', defaultMessage: 'File upload not allowed with polls.' }, @@ -72,10 +81,19 @@ const COMPOSE_PANEL_BREAKPOINT = 600 + (285 * 1) + (10 * 1); export const ensureComposeIsVisible = (getState, routerHistory) => { if (!getState().getIn(['compose', 'mounted']) && window.innerWidth < COMPOSE_PANEL_BREAKPOINT) { - routerHistory.push('/statuses/new'); + routerHistory.push('/publish'); } }; +export function setComposeToStatus(status, text, spoiler_text) { + return{ + type: COMPOSE_SET_STATUS, + status, + text, + spoiler_text, + }; +}; + export function changeCompose(text) { return { type: COMPOSE_CHANGE, @@ -130,8 +148,9 @@ export function directCompose(account, routerHistory) { export function submitCompose(routerHistory) { return function (dispatch, getState) { - const status = getState().getIn(['compose', 'text'], ''); - const media = getState().getIn(['compose', 'media_attachments']); + const status = getState().getIn(['compose', 'text'], ''); + const media = getState().getIn(['compose', 'media_attachments']); + const statusId = getState().getIn(['compose', 'id'], null); if ((!status || !status.length) && media.size === 0) { return; @@ -139,20 +158,23 @@ export function submitCompose(routerHistory) { dispatch(submitComposeRequest()); - api(getState).post('/api/v1/statuses', { - status, - in_reply_to_id: getState().getIn(['compose', 'in_reply_to'], null), - media_ids: media.map(item => item.get('id')), - sensitive: getState().getIn(['compose', 'sensitive']), - spoiler_text: getState().getIn(['compose', 'spoiler']) ? getState().getIn(['compose', 'spoiler_text'], '') : '', - visibility: getState().getIn(['compose', 'privacy']), - poll: getState().getIn(['compose', 'poll'], null), - }, { + api(getState).request({ + url: statusId === null ? '/api/v1/statuses' : `/api/v1/statuses/${statusId}`, + method: statusId === null ? 'post' : 'put', + data: { + status, + in_reply_to_id: getState().getIn(['compose', 'in_reply_to'], null), + media_ids: media.map(item => item.get('id')), + sensitive: getState().getIn(['compose', 'sensitive']), + spoiler_text: getState().getIn(['compose', 'spoiler']) ? getState().getIn(['compose', 'spoiler_text'], '') : '', + visibility: getState().getIn(['compose', 'privacy']), + poll: getState().getIn(['compose', 'poll'], null), + }, headers: { 'Idempotency-Key': getState().getIn(['compose', 'idempotencyKey']), }, }).then(function (response) { - if (routerHistory && routerHistory.location.pathname === '/statuses/new' && window.history.state) { + if (routerHistory && (routerHistory.location.pathname === '/publish' || routerHistory.location.pathname === '/statuses/new') && window.history.state) { routerHistory.goBack(); } @@ -169,11 +191,11 @@ export function submitCompose(routerHistory) { } }; - if (response.data.visibility !== 'direct') { + if (statusId === null && response.data.visibility !== 'direct') { insertIfOnline('home'); } - if (response.data.in_reply_to_id === null && response.data.visibility === 'public') { + if (statusId === null && response.data.in_reply_to_id === null && response.data.visibility === 'public') { insertIfOnline('community'); insertIfOnline('public'); insertIfOnline(`account:${response.data.account.id}`); @@ -245,12 +267,15 @@ export function uploadCompose(files) { if (status === 200) { dispatch(uploadComposeSuccess(data, f)); } else if (status === 202) { + let tryCount = 1; const poll = () => { api(getState).get(`/api/v1/media/${data.id}`).then(response => { if (response.status === 200) { dispatch(uploadComposeSuccess(response.data, f)); } else if (response.status === 206) { - setTimeout(() => poll(), 1000); + let retryAfter = (Math.log2(tryCount) || 1) * 1000; + tryCount += 1; + setTimeout(() => poll(), retryAfter); } }).catch(error => dispatch(uploadComposeFail(error))); }; @@ -306,6 +331,32 @@ export const uploadThumbnailFail = error => ({ skipLoading: true, }); +export function initMediaEditModal(id) { + return dispatch => { + dispatch({ + type: INIT_MEDIA_EDIT_MODAL, + id, + }); + + dispatch(openModal('FOCAL_POINT', { id })); + }; +}; + +export function onChangeMediaDescription(description) { + return { + type: COMPOSE_CHANGE_MEDIA_DESCRIPTION, + description, + }; +}; + +export function onChangeMediaFocus(focusX, focusY) { + return { + type: COMPOSE_CHANGE_MEDIA_FOCUS, + focusX, + focusY, + }; +}; + export function changeUploadCompose(id, params) { return (dispatch, getState) => { dispatch(changeUploadComposeRequest()); @@ -502,13 +553,25 @@ export function selectComposeSuggestion(position, token, suggestion, path) { startPosition = position; } - dispatch({ - type: COMPOSE_SUGGESTION_SELECT, - position: startPosition, - token, - completion, - path, - }); + // We don't want to replace hashtags that vary only in case due to accessibility, but we need to fire off an event so that + // the suggestions are dismissed and the cursor moves forward. + if (suggestion.type !== 'hashtag' || token.slice(1).localeCompare(suggestion.name, undefined, { sensitivity: 'accent' }) !== 0) { + dispatch({ + type: COMPOSE_SUGGESTION_SELECT, + position: startPosition, + token, + completion, + path, + }); + } else { + dispatch({ + type: COMPOSE_SUGGESTION_IGNORE, + position: startPosition, + token, + completion, + path, + }); + } }; }; diff --git a/app/javascript/mastodon/actions/history.js b/app/javascript/mastodon/actions/history.js new file mode 100644 index 00000000000000..c142aaf617e2cb --- /dev/null +++ b/app/javascript/mastodon/actions/history.js @@ -0,0 +1,37 @@ +import api from '../api'; +import { importFetchedAccounts } from './importer'; + +export const HISTORY_FETCH_REQUEST = 'HISTORY_FETCH_REQUEST'; +export const HISTORY_FETCH_SUCCESS = 'HISTORY_FETCH_SUCCESS'; +export const HISTORY_FETCH_FAIL = 'HISTORY_FETCH_FAIL'; + +export const fetchHistory = statusId => (dispatch, getState) => { + const loading = getState().getIn(['history', statusId, 'loading']); + + if (loading) { + return; + } + + dispatch(fetchHistoryRequest(statusId)); + + api(getState).get(`/api/v1/statuses/${statusId}/history`).then(({ data }) => { + dispatch(importFetchedAccounts(data.map(x => x.account))); + dispatch(fetchHistorySuccess(statusId, data)); + }).catch(error => dispatch(fetchHistoryFail(error))); +}; + +export const fetchHistoryRequest = statusId => ({ + type: HISTORY_FETCH_REQUEST, + statusId, +}); + +export const fetchHistorySuccess = (statusId, history) => ({ + type: HISTORY_FETCH_SUCCESS, + statusId, + history, +}); + +export const fetchHistoryFail = error => ({ + type: HISTORY_FETCH_FAIL, + error, +}); diff --git a/app/javascript/mastodon/actions/identity_proofs.js b/app/javascript/mastodon/actions/identity_proofs.js deleted file mode 100644 index 10398395660345..00000000000000 --- a/app/javascript/mastodon/actions/identity_proofs.js +++ /dev/null @@ -1,31 +0,0 @@ -import api from '../api'; - -export const IDENTITY_PROOFS_ACCOUNT_FETCH_REQUEST = 'IDENTITY_PROOFS_ACCOUNT_FETCH_REQUEST'; -export const IDENTITY_PROOFS_ACCOUNT_FETCH_SUCCESS = 'IDENTITY_PROOFS_ACCOUNT_FETCH_SUCCESS'; -export const IDENTITY_PROOFS_ACCOUNT_FETCH_FAIL = 'IDENTITY_PROOFS_ACCOUNT_FETCH_FAIL'; - -export const fetchAccountIdentityProofs = accountId => (dispatch, getState) => { - dispatch(fetchAccountIdentityProofsRequest(accountId)); - - api(getState).get(`/api/v1/accounts/${accountId}/identity_proofs`) - .then(({ data }) => dispatch(fetchAccountIdentityProofsSuccess(accountId, data))) - .catch(err => dispatch(fetchAccountIdentityProofsFail(accountId, err))); -}; - -export const fetchAccountIdentityProofsRequest = id => ({ - type: IDENTITY_PROOFS_ACCOUNT_FETCH_REQUEST, - id, -}); - -export const fetchAccountIdentityProofsSuccess = (accountId, identity_proofs) => ({ - type: IDENTITY_PROOFS_ACCOUNT_FETCH_SUCCESS, - accountId, - identity_proofs, -}); - -export const fetchAccountIdentityProofsFail = (accountId, err) => ({ - type: IDENTITY_PROOFS_ACCOUNT_FETCH_FAIL, - accountId, - err, - skipNotFound: true, -}); diff --git a/app/javascript/mastodon/actions/importer/normalizer.js b/app/javascript/mastodon/actions/importer/normalizer.js index 5002292b9125a3..ca76e3494d15a9 100644 --- a/app/javascript/mastodon/actions/importer/normalizer.js +++ b/app/javascript/mastodon/actions/importer/normalizer.js @@ -54,9 +54,10 @@ export function normalizeStatus(status, normalOldStatus) { normalStatus.poll = status.poll.id; } - // Only calculate these values when status first encountered - // Otherwise keep the ones already in the reducer - if (normalOldStatus) { + // Only calculate these values when status first encountered and + // when the underlying values change. Otherwise keep the ones + // already in the reducer + if (normalOldStatus && normalOldStatus.get('content') === normalStatus.content && normalOldStatus.get('spoiler_text') === normalStatus.spoiler_text) { normalStatus.search_index = normalOldStatus.get('search_index'); normalStatus.contentHtml = normalOldStatus.get('contentHtml'); normalStatus.spoilerHtml = normalOldStatus.get('spoilerHtml'); @@ -71,7 +72,7 @@ export function normalizeStatus(status, normalOldStatus) { } const spoilerText = normalStatus.spoiler_text || ''; - const searchContent = ([spoilerText, status.content].concat((status.poll && status.poll.options) ? status.poll.options.map(option => option.title) : [])).join('\n\n').replace(//g, '\n').replace(/<\/p>

/g, '\n\n'); + const searchContent = ([spoilerText, status.content].concat((status.poll && status.poll.options) ? status.poll.options.map(option => option.title) : [])).concat(status.media_attachments.map(att => att.description)).join('\n\n').replace(//g, '\n').replace(/<\/p>

/g, '\n\n'); const emojiMap = makeEmojiMap(normalStatus); normalStatus.search_index = domParser.parseFromString(searchContent, 'text/html').documentElement.textContent; diff --git a/app/javascript/mastodon/actions/modal.js b/app/javascript/mastodon/actions/modal.js index 3d0299db58af52..3e576fab8e783c 100644 --- a/app/javascript/mastodon/actions/modal.js +++ b/app/javascript/mastodon/actions/modal.js @@ -9,9 +9,10 @@ export function openModal(type, props) { }; }; -export function closeModal(type) { +export function closeModal(type, options = { ignoreFocus: false }) { return { type: MODAL_CLOSE, modalType: type, + ignoreFocus: options.ignoreFocus, }; }; diff --git a/app/javascript/mastodon/actions/notifications.js b/app/javascript/mastodon/actions/notifications.js index 3464ac995979d3..00e8d74d7c5134 100644 --- a/app/javascript/mastodon/actions/notifications.js +++ b/app/javascript/mastodon/actions/notifications.js @@ -1,6 +1,6 @@ import api, { getLinks } from '../api'; import IntlMessageFormat from 'intl-messageformat'; -import { fetchRelationships } from './accounts'; +import { fetchFollowRequests, fetchRelationships } from './accounts'; import { importFetchedAccount, importFetchedAccounts, @@ -34,7 +34,6 @@ export const NOTIFICATIONS_LOAD_PENDING = 'NOTIFICATIONS_LOAD_PENDING'; export const NOTIFICATIONS_MOUNT = 'NOTIFICATIONS_MOUNT'; export const NOTIFICATIONS_UNMOUNT = 'NOTIFICATIONS_UNMOUNT'; - export const NOTIFICATIONS_MARK_AS_READ = 'NOTIFICATIONS_MARK_AS_READ'; export const NOTIFICATIONS_SET_BROWSER_SUPPORT = 'NOTIFICATIONS_SET_BROWSER_SUPPORT'; @@ -46,7 +45,7 @@ defineMessages({ }); const fetchRelatedRelationships = (dispatch, notifications) => { - const accountIds = notifications.filter(item => item.type === 'follow').map(item => item.account.id); + const accountIds = notifications.filter(item => ['follow', 'follow_request', 'admin.sign_up'].indexOf(item.type) !== -1).map(item => item.account.id); if (accountIds.length > 0) { dispatch(fetchRelationships(accountIds)); @@ -78,6 +77,10 @@ export function updateNotifications(notification, intlMessages, intlLocale) { filtered = regex && regex.test(searchIndex); } + if (['follow_request'].includes(notification.type)) { + dispatch(fetchFollowRequests()); + } + dispatch(submitMarkers()); if (showInColumn) { @@ -120,7 +123,18 @@ export function updateNotifications(notification, intlMessages, intlLocale) { const excludeTypesFromSettings = state => state.getIn(['settings', 'notifications', 'shows']).filter(enabled => !enabled).keySeq().toJS(); const excludeTypesFromFilter = filter => { - const allTypes = ImmutableList(['follow', 'follow_request', 'favourite', 'reblog', 'mention', 'poll']); + const allTypes = ImmutableList([ + 'follow', + 'follow_request', + 'favourite', + 'reblog', + 'mention', + 'poll', + 'status', + 'update', + 'admin.sign_up', + ]); + return allTypes.filterNot(item => item === filter).toJS(); }; diff --git a/app/javascript/mastodon/actions/reports.js b/app/javascript/mastodon/actions/reports.js index afa0c341218b4b..fbe5b3791bcf66 100644 --- a/app/javascript/mastodon/actions/reports.js +++ b/app/javascript/mastodon/actions/reports.js @@ -1,89 +1,38 @@ import api from '../api'; -import { openModal, closeModal } from './modal'; - -export const REPORT_INIT = 'REPORT_INIT'; -export const REPORT_CANCEL = 'REPORT_CANCEL'; +import { openModal } from './modal'; export const REPORT_SUBMIT_REQUEST = 'REPORT_SUBMIT_REQUEST'; export const REPORT_SUBMIT_SUCCESS = 'REPORT_SUBMIT_SUCCESS'; export const REPORT_SUBMIT_FAIL = 'REPORT_SUBMIT_FAIL'; -export const REPORT_STATUS_TOGGLE = 'REPORT_STATUS_TOGGLE'; -export const REPORT_COMMENT_CHANGE = 'REPORT_COMMENT_CHANGE'; -export const REPORT_FORWARD_CHANGE = 'REPORT_FORWARD_CHANGE'; - -export function initReport(account, status) { - return dispatch => { - dispatch({ - type: REPORT_INIT, - account, - status, - }); - - dispatch(openModal('REPORT')); - }; -}; - -export function cancelReport() { - return { - type: REPORT_CANCEL, - }; -}; - -export function toggleStatusReport(statusId, checked) { - return { - type: REPORT_STATUS_TOGGLE, - statusId, - checked, - }; -}; - -export function submitReport() { - return (dispatch, getState) => { - dispatch(submitReportRequest()); - - api(getState).post('/api/v1/reports', { - account_id: getState().getIn(['reports', 'new', 'account_id']), - status_ids: getState().getIn(['reports', 'new', 'status_ids']), - comment: getState().getIn(['reports', 'new', 'comment']), - forward: getState().getIn(['reports', 'new', 'forward']), - }).then(response => { - dispatch(closeModal()); - dispatch(submitReportSuccess(response.data)); - }).catch(error => dispatch(submitReportFail(error))); - }; -}; - -export function submitReportRequest() { - return { - type: REPORT_SUBMIT_REQUEST, - }; -}; - -export function submitReportSuccess(report) { - return { - type: REPORT_SUBMIT_SUCCESS, - report, - }; -}; - -export function submitReportFail(error) { - return { - type: REPORT_SUBMIT_FAIL, - error, - }; -}; - -export function changeReportComment(comment) { - return { - type: REPORT_COMMENT_CHANGE, - comment, - }; -}; - -export function changeReportForward(forward) { - return { - type: REPORT_FORWARD_CHANGE, - forward, - }; -}; +export const initReport = (account, status) => dispatch => + dispatch(openModal('REPORT', { + accountId: account.get('id'), + statusId: status?.get('id'), + })); + +export const submitReport = (params, onSuccess, onFail) => (dispatch, getState) => { + dispatch(submitReportRequest()); + + api(getState).post('/api/v1/reports', params).then(response => { + dispatch(submitReportSuccess(response.data)); + if (onSuccess) onSuccess(); + }).catch(error => { + dispatch(submitReportFail(error)); + if (onFail) onFail(); + }); +}; + +export const submitReportRequest = () => ({ + type: REPORT_SUBMIT_REQUEST, +}); + +export const submitReportSuccess = report => ({ + type: REPORT_SUBMIT_SUCCESS, + report, +}); + +export const submitReportFail = error => ({ + type: REPORT_SUBMIT_FAIL, + error, +}); diff --git a/app/javascript/mastodon/actions/rules.js b/app/javascript/mastodon/actions/rules.js new file mode 100644 index 00000000000000..34e60a121e0aad --- /dev/null +++ b/app/javascript/mastodon/actions/rules.js @@ -0,0 +1,27 @@ +import api from '../api'; + +export const RULES_FETCH_REQUEST = 'RULES_FETCH_REQUEST'; +export const RULES_FETCH_SUCCESS = 'RULES_FETCH_SUCCESS'; +export const RULES_FETCH_FAIL = 'RULES_FETCH_FAIL'; + +export const fetchRules = () => (dispatch, getState) => { + dispatch(fetchRulesRequest()); + + api(getState) + .get('/api/v1/instance').then(({ data }) => dispatch(fetchRulesSuccess(data.rules))) + .catch(err => dispatch(fetchRulesFail(err))); +}; + +const fetchRulesRequest = () => ({ + type: RULES_FETCH_REQUEST, +}); + +const fetchRulesSuccess = rules => ({ + type: RULES_FETCH_SUCCESS, + rules, +}); + +const fetchRulesFail = error => ({ + type: RULES_FETCH_FAIL, + error, +}); diff --git a/app/javascript/mastodon/actions/statuses.js b/app/javascript/mastodon/actions/statuses.js index 3fc7c07023d627..adc24eabfbfc04 100644 --- a/app/javascript/mastodon/actions/statuses.js +++ b/app/javascript/mastodon/actions/statuses.js @@ -2,7 +2,7 @@ import api from '../api'; import { deleteFromTimelines } from './timelines'; import { importFetchedStatus, importFetchedStatuses, importFetchedAccount } from './importer'; -import { ensureComposeIsVisible } from './compose'; +import { ensureComposeIsVisible, setComposeToStatus } from './compose'; export const STATUS_FETCH_REQUEST = 'STATUS_FETCH_REQUEST'; export const STATUS_FETCH_SUCCESS = 'STATUS_FETCH_SUCCESS'; @@ -30,6 +30,10 @@ export const STATUS_COLLAPSE = 'STATUS_COLLAPSE'; export const REDRAFT = 'REDRAFT'; +export const STATUS_FETCH_SOURCE_REQUEST = 'STATUS_FETCH_SOURCE_REQUEST'; +export const STATUS_FETCH_SOURCE_SUCCESS = 'STATUS_FETCH_SOURCE_SUCCESS'; +export const STATUS_FETCH_SOURCE_FAIL = 'STATUS_FETCH_SOURCE_FAIL'; + export function fetchStatusRequest(id, skipLoading) { return { type: STATUS_FETCH_REQUEST, @@ -84,6 +88,37 @@ export function redraft(status, raw_text) { }; }; +export const editStatus = (id, routerHistory) => (dispatch, getState) => { + let status = getState().getIn(['statuses', id]); + + if (status.get('poll')) { + status = status.set('poll', getState().getIn(['polls', status.get('poll')])); + } + + dispatch(fetchStatusSourceRequest()); + + api(getState).get(`/api/v1/statuses/${id}/source`).then(response => { + dispatch(fetchStatusSourceSuccess()); + ensureComposeIsVisible(getState, routerHistory); + dispatch(setComposeToStatus(status, response.data.text, response.data.spoiler_text)); + }).catch(error => { + dispatch(fetchStatusSourceFail(error)); + }); +}; + +export const fetchStatusSourceRequest = () => ({ + type: STATUS_FETCH_SOURCE_REQUEST, +}); + +export const fetchStatusSourceSuccess = () => ({ + type: STATUS_FETCH_SOURCE_SUCCESS, +}); + +export const fetchStatusSourceFail = error => ({ + type: STATUS_FETCH_SOURCE_FAIL, + error, +}); + export function deleteStatus(id, routerHistory, withRedraft = false) { return (dispatch, getState) => { let status = getState().getIn(['statuses', id]); @@ -131,6 +166,9 @@ export function deleteStatusFail(id, error) { }; }; +export const updateStatus = status => dispatch => + dispatch(importFetchedStatus(status)); + export function fetchContext(id) { return (dispatch, getState) => { dispatch(fetchContextRequest(id)); diff --git a/app/javascript/mastodon/actions/streaming.js b/app/javascript/mastodon/actions/streaming.js index beb5c6a4a9de6c..d76f045c878926 100644 --- a/app/javascript/mastodon/actions/streaming.js +++ b/app/javascript/mastodon/actions/streaming.js @@ -7,9 +7,14 @@ import { expandHomeTimeline, connectTimeline, disconnectTimeline, + fillHomeTimelineGaps, + fillPublicTimelineGaps, + fillCommunityTimelineGaps, + fillListTimelineGaps, } from './timelines'; import { updateNotifications, expandNotifications } from './notifications'; import { updateConversations } from './conversations'; +import { updateStatus } from './statuses'; import { fetchAnnouncements, updateAnnouncements, @@ -34,6 +39,7 @@ const randomUpTo = max => * @param {Object.} params * @param {Object} options * @param {function(Function, Function): void} [options.fallback] + * @param {function(): void} [options.fillGaps] * @param {function(object): boolean} [options.accept] * @return {function(): void} */ @@ -60,6 +66,10 @@ export const connectTimelineStream = (timelineId, channelName, params = {}, opti clearTimeout(pollingId); pollingId = null; } + + if (options.fillGaps) { + dispatch(options.fillGaps()); + } }, onDisconnect() { @@ -75,6 +85,9 @@ export const connectTimelineStream = (timelineId, channelName, params = {}, opti case 'update': dispatch(updateTimeline(timelineId, JSON.parse(data.payload), options.accept)); break; + case 'status.update': + dispatch(updateStatus(JSON.parse(data.payload))); + break; case 'delete': dispatch(deleteFromTimelines(data.payload)); break; @@ -115,7 +128,7 @@ const refreshHomeTimelineAndNotification = (dispatch, done) => { * @return {function(): void} */ export const connectUserStream = () => - connectTimelineStream('home', 'user', {}, { fallback: refreshHomeTimelineAndNotification }); + connectTimelineStream('home', 'user', {}, { fallback: refreshHomeTimelineAndNotification, fillGaps: fillHomeTimelineGaps }); /** * @param {Object} options @@ -123,7 +136,7 @@ export const connectUserStream = () => * @return {function(): void} */ export const connectCommunityStream = ({ onlyMedia } = {}) => - connectTimelineStream(`community${onlyMedia ? ':media' : ''}`, `public:local${onlyMedia ? ':media' : ''}`); + connectTimelineStream(`community${onlyMedia ? ':media' : ''}`, `public:local${onlyMedia ? ':media' : ''}`, {}, { fillGaps: () => (fillCommunityTimelineGaps({ onlyMedia })) }); /** * @param {Object} options @@ -132,7 +145,7 @@ export const connectCommunityStream = ({ onlyMedia } = {}) => * @return {function(): void} */ export const connectPublicStream = ({ onlyMedia, onlyRemote } = {}) => - connectTimelineStream(`public${onlyRemote ? ':remote' : ''}${onlyMedia ? ':media' : ''}`, `public${onlyRemote ? ':remote' : ''}${onlyMedia ? ':media' : ''}`); + connectTimelineStream(`public${onlyRemote ? ':remote' : ''}${onlyMedia ? ':media' : ''}`, `public${onlyRemote ? ':remote' : ''}${onlyMedia ? ':media' : ''}`, {}, { fillGaps: () => fillPublicTimelineGaps({ onlyMedia, onlyRemote }) }); /** * @param {string} columnId @@ -155,4 +168,4 @@ export const connectDirectStream = () => * @return {function(): void} */ export const connectListStream = listId => - connectTimelineStream(`list:${listId}`, 'list', { list: listId }); + connectTimelineStream(`list:${listId}`, 'list', { list: listId }, { fillGaps: () => fillListTimelineGaps(listId) }); diff --git a/app/javascript/mastodon/actions/timelines.js b/app/javascript/mastodon/actions/timelines.js index 31ae09e4ac5013..44fedd5c2732d0 100644 --- a/app/javascript/mastodon/actions/timelines.js +++ b/app/javascript/mastodon/actions/timelines.js @@ -124,6 +124,22 @@ export function expandTimeline(timelineId, path, params = {}, done = noOp) { }; }; +export function fillTimelineGaps(timelineId, path, params = {}, done = noOp) { + return (dispatch, getState) => { + const timeline = getState().getIn(['timelines', timelineId], ImmutableMap()); + const items = timeline.get('items'); + const nullIndexes = items.map((statusId, index) => statusId === null ? index : null); + const gaps = nullIndexes.map(index => index > 0 ? items.get(index - 1) : null); + + // Only expand at most two gaps to avoid doing too many requests + done = gaps.take(2).reduce((done, maxId) => { + return (() => dispatch(expandTimeline(timelineId, path, { ...params, maxId }, done))); + }, done); + + done(); + }; +} + export const expandHomeTimeline = ({ maxId } = {}, done = noOp) => expandTimeline('home', '/api/v1/timelines/home', { max_id: maxId }, done); export const expandPublicTimeline = ({ maxId, onlyMedia, onlyRemote } = {}, done = noOp) => expandTimeline(`public${onlyRemote ? ':remote' : ''}${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { remote: !!onlyRemote, max_id: maxId, only_media: !!onlyMedia }, done); export const expandCommunityTimeline = ({ maxId, onlyMedia } = {}, done = noOp) => expandTimeline(`community${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { local: true, max_id: maxId, only_media: !!onlyMedia }, done); @@ -141,6 +157,11 @@ export const expandHashtagTimeline = (hashtag, { maxId, tags, local } = }, done); }; +export const fillHomeTimelineGaps = (done = noOp) => fillTimelineGaps('home', '/api/v1/timelines/home', {}, done); +export const fillPublicTimelineGaps = ({ onlyMedia, onlyRemote } = {}, done = noOp) => fillTimelineGaps(`public${onlyRemote ? ':remote' : ''}${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { remote: !!onlyRemote, only_media: !!onlyMedia }, done); +export const fillCommunityTimelineGaps = ({ onlyMedia } = {}, done = noOp) => fillTimelineGaps(`community${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { local: true, only_media: !!onlyMedia }, done); +export const fillListTimelineGaps = (id, done = noOp) => fillTimelineGaps(`list:${id}`, `/api/v1/timelines/list/${id}`, {}, done); + export function expandTimelineRequest(timeline, isLoadingMore) { return { type: TIMELINE_EXPAND_REQUEST, @@ -184,6 +205,7 @@ export function connectTimeline(timeline) { return { type: TIMELINE_CONNECT, timeline, + usePendingItems: preferPendingItems, }; }; diff --git a/app/javascript/mastodon/actions/trends.js b/app/javascript/mastodon/actions/trends.js index 853e4f60aec711..edda0b5b5d0eb5 100644 --- a/app/javascript/mastodon/actions/trends.js +++ b/app/javascript/mastodon/actions/trends.js @@ -1,32 +1,139 @@ -import api from '../api'; +import api, { getLinks } from '../api'; +import { importFetchedStatuses } from './importer'; -export const TRENDS_FETCH_REQUEST = 'TRENDS_FETCH_REQUEST'; -export const TRENDS_FETCH_SUCCESS = 'TRENDS_FETCH_SUCCESS'; -export const TRENDS_FETCH_FAIL = 'TRENDS_FETCH_FAIL'; +export const TRENDS_TAGS_FETCH_REQUEST = 'TRENDS_TAGS_FETCH_REQUEST'; +export const TRENDS_TAGS_FETCH_SUCCESS = 'TRENDS_TAGS_FETCH_SUCCESS'; +export const TRENDS_TAGS_FETCH_FAIL = 'TRENDS_TAGS_FETCH_FAIL'; -export const fetchTrends = () => (dispatch, getState) => { - dispatch(fetchTrendsRequest()); +export const TRENDS_LINKS_FETCH_REQUEST = 'TRENDS_LINKS_FETCH_REQUEST'; +export const TRENDS_LINKS_FETCH_SUCCESS = 'TRENDS_LINKS_FETCH_SUCCESS'; +export const TRENDS_LINKS_FETCH_FAIL = 'TRENDS_LINKS_FETCH_FAIL'; + +export const TRENDS_STATUSES_FETCH_REQUEST = 'TRENDS_STATUSES_FETCH_REQUEST'; +export const TRENDS_STATUSES_FETCH_SUCCESS = 'TRENDS_STATUSES_FETCH_SUCCESS'; +export const TRENDS_STATUSES_FETCH_FAIL = 'TRENDS_STATUSES_FETCH_FAIL'; + +export const TRENDS_STATUSES_EXPAND_REQUEST = 'TRENDS_STATUSES_EXPAND_REQUEST'; +export const TRENDS_STATUSES_EXPAND_SUCCESS = 'TRENDS_STATUSES_EXPAND_SUCCESS'; +export const TRENDS_STATUSES_EXPAND_FAIL = 'TRENDS_STATUSES_EXPAND_FAIL'; + +export const fetchTrendingHashtags = () => (dispatch, getState) => { + dispatch(fetchTrendingHashtagsRequest()); + + api(getState) + .get('/api/v1/trends/tags') + .then(({ data }) => dispatch(fetchTrendingHashtagsSuccess(data))) + .catch(err => dispatch(fetchTrendingHashtagsFail(err))); +}; + +export const fetchTrendingHashtagsRequest = () => ({ + type: TRENDS_TAGS_FETCH_REQUEST, + skipLoading: true, +}); + +export const fetchTrendingHashtagsSuccess = trends => ({ + type: TRENDS_TAGS_FETCH_SUCCESS, + trends, + skipLoading: true, +}); + +export const fetchTrendingHashtagsFail = error => ({ + type: TRENDS_TAGS_FETCH_FAIL, + error, + skipLoading: true, + skipAlert: true, +}); + +export const fetchTrendingLinks = () => (dispatch, getState) => { + dispatch(fetchTrendingLinksRequest()); api(getState) - .get('/api/v1/trends') - .then(({ data }) => dispatch(fetchTrendsSuccess(data))) - .catch(err => dispatch(fetchTrendsFail(err))); + .get('/api/v1/trends/links') + .then(({ data }) => dispatch(fetchTrendingLinksSuccess(data))) + .catch(err => dispatch(fetchTrendingLinksFail(err))); }; -export const fetchTrendsRequest = () => ({ - type: TRENDS_FETCH_REQUEST, +export const fetchTrendingLinksRequest = () => ({ + type: TRENDS_LINKS_FETCH_REQUEST, skipLoading: true, }); -export const fetchTrendsSuccess = trends => ({ - type: TRENDS_FETCH_SUCCESS, +export const fetchTrendingLinksSuccess = trends => ({ + type: TRENDS_LINKS_FETCH_SUCCESS, trends, skipLoading: true, }); -export const fetchTrendsFail = error => ({ - type: TRENDS_FETCH_FAIL, +export const fetchTrendingLinksFail = error => ({ + type: TRENDS_LINKS_FETCH_FAIL, error, skipLoading: true, skipAlert: true, }); + +export const fetchTrendingStatuses = () => (dispatch, getState) => { + if (getState().getIn(['status_lists', 'trending', 'isLoading'])) { + return; + } + + dispatch(fetchTrendingStatusesRequest()); + + api(getState).get('/api/v1/trends/statuses').then(response => { + const next = getLinks(response).refs.find(link => link.rel === 'next'); + dispatch(importFetchedStatuses(response.data)); + dispatch(fetchTrendingStatusesSuccess(response.data, next ? next.uri : null)); + }).catch(err => dispatch(fetchTrendingStatusesFail(err))); +}; + +export const fetchTrendingStatusesRequest = () => ({ + type: TRENDS_STATUSES_FETCH_REQUEST, + skipLoading: true, +}); + +export const fetchTrendingStatusesSuccess = (statuses, next) => ({ + type: TRENDS_STATUSES_FETCH_SUCCESS, + statuses, + next, + skipLoading: true, +}); + +export const fetchTrendingStatusesFail = error => ({ + type: TRENDS_STATUSES_FETCH_FAIL, + error, + skipLoading: true, + skipAlert: true, +}); + + +export const expandTrendingStatuses = () => (dispatch, getState) => { + const url = getState().getIn(['status_lists', 'trending', 'next'], null); + + if (url === null || getState().getIn(['status_lists', 'trending', 'isLoading'])) { + return; + } + + dispatch(expandTrendingStatusesRequest()); + + api(getState).get(url).then(response => { + const next = getLinks(response).refs.find(link => link.rel === 'next'); + dispatch(importFetchedStatuses(response.data)); + dispatch(expandTrendingStatusesSuccess(response.data, next ? next.uri : null)); + }).catch(error => { + dispatch(expandTrendingStatusesFail(error)); + }); +}; + +export const expandTrendingStatusesRequest = () => ({ + type: TRENDS_STATUSES_EXPAND_REQUEST, +}); + +export const expandTrendingStatusesSuccess = (statuses, next) => ({ + type: TRENDS_STATUSES_EXPAND_SUCCESS, + statuses, + next, +}); + +export const expandTrendingStatusesFail = error => ({ + type: TRENDS_STATUSES_EXPAND_FAIL, + error, +}); diff --git a/app/javascript/mastodon/api.js b/app/javascript/mastodon/api.js index 98d59de433f4bf..645ef65006d80a 100644 --- a/app/javascript/mastodon/api.js +++ b/app/javascript/mastodon/api.js @@ -12,21 +12,35 @@ export const getLinks = response => { return LinkHeader.parse(value); }; -let csrfHeader = {}; +const csrfHeader = {}; -function setCSRFHeader() { +const setCSRFHeader = () => { const csrfToken = document.querySelector('meta[name=csrf-token]'); + if (csrfToken) { csrfHeader['X-CSRF-Token'] = csrfToken.content; } -} +}; ready(setCSRFHeader); +const authorizationHeaderFromState = getState => { + const accessToken = getState && getState().getIn(['meta', 'access_token'], ''); + + if (!accessToken) { + return {}; + } + + return { + 'Authorization': `Bearer ${accessToken}`, + }; +}; + export default getState => axios.create({ - headers: Object.assign(csrfHeader, getState ? { - 'Authorization': `Bearer ${getState().getIn(['meta', 'access_token'], '')}`, - } : {}), + headers: { + ...csrfHeader, + ...authorizationHeaderFromState(getState), + }, transformResponse: [function (data) { try { diff --git a/app/javascript/mastodon/components/account.js b/app/javascript/mastodon/components/account.js index a85d683a7bad18..62b5843a939db4 100644 --- a/app/javascript/mastodon/components/account.js +++ b/app/javascript/mastodon/components/account.js @@ -118,7 +118,7 @@ class Account extends ImmutablePureComponent { return (

- +
{mute_expires_at} diff --git a/app/javascript/mastodon/components/admin/Counter.js b/app/javascript/mastodon/components/admin/Counter.js new file mode 100644 index 00000000000000..5a5b2b869e078e --- /dev/null +++ b/app/javascript/mastodon/components/admin/Counter.js @@ -0,0 +1,117 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import api from 'mastodon/api'; +import { FormattedNumber } from 'react-intl'; +import { Sparklines, SparklinesCurve } from 'react-sparklines'; +import classNames from 'classnames'; +import Skeleton from 'mastodon/components/skeleton'; + +const percIncrease = (a, b) => { + let percent; + + if (b !== 0) { + if (a !== 0) { + percent = (b - a) / a; + } else { + percent = 1; + } + } else if (b === 0 && a === 0) { + percent = 0; + } else { + percent = - 1; + } + + return percent; +}; + +export default class Counter extends React.PureComponent { + + static propTypes = { + measure: PropTypes.string.isRequired, + start_at: PropTypes.string.isRequired, + end_at: PropTypes.string.isRequired, + label: PropTypes.string.isRequired, + href: PropTypes.string, + params: PropTypes.object, + target: PropTypes.string, + }; + + state = { + loading: true, + data: null, + }; + + componentDidMount () { + const { measure, start_at, end_at, params } = this.props; + + api().post('/api/v1/admin/measures', { keys: [measure], start_at, end_at, [measure]: params }).then(res => { + this.setState({ + loading: false, + data: res.data, + }); + }).catch(err => { + console.error(err); + }); + } + + render () { + const { label, href, target } = this.props; + const { loading, data } = this.state; + + let content; + + if (loading) { + content = ( + + + + + ); + } else { + const measure = data[0]; + const percentChange = measure.previous_total && percIncrease(measure.previous_total * 1, measure.total * 1); + + content = ( + + {measure.human_value || } + {measure.previous_total && ( 0, negative: percentChange < 0 })}>{percentChange > 0 && '+'})} + + ); + } + + const inner = ( + +
+ {content} +
+ +
+ {label} +
+ +
+ {!loading && ( + x.value * 1)}> + + + )} +
+
+ ); + + if (href) { + return ( +
+ {inner} + + ); + } else { + return ( +
+ {inner} +
+ ); + } + } + +} diff --git a/app/javascript/mastodon/components/admin/Dimension.js b/app/javascript/mastodon/components/admin/Dimension.js new file mode 100644 index 00000000000000..977c8208df18c4 --- /dev/null +++ b/app/javascript/mastodon/components/admin/Dimension.js @@ -0,0 +1,93 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import api from 'mastodon/api'; +import { FormattedNumber } from 'react-intl'; +import { roundTo10 } from 'mastodon/utils/numbers'; +import Skeleton from 'mastodon/components/skeleton'; + +export default class Dimension extends React.PureComponent { + + static propTypes = { + dimension: PropTypes.string.isRequired, + start_at: PropTypes.string.isRequired, + end_at: PropTypes.string.isRequired, + limit: PropTypes.number.isRequired, + label: PropTypes.string.isRequired, + params: PropTypes.object, + }; + + state = { + loading: true, + data: null, + }; + + componentDidMount () { + const { start_at, end_at, dimension, limit, params } = this.props; + + api().post('/api/v1/admin/dimensions', { keys: [dimension], start_at, end_at, limit, [dimension]: params }).then(res => { + this.setState({ + loading: false, + data: res.data, + }); + }).catch(err => { + console.error(err); + }); + } + + render () { + const { label, limit } = this.props; + const { loading, data } = this.state; + + let content; + + if (loading) { + content = ( + + + {Array.from(Array(limit)).map((_, i) => ( + + + + + + ))} + +
+ + + +
+ ); + } else { + const sum = data[0].data.reduce((sum, cur) => sum + (cur.value * 1), 0); + + content = ( + + + {data[0].data.map(item => ( + + + + + + ))} + +
+ + {item.human_key} + + {typeof item.human_value !== 'undefined' ? item.human_value : } +
+ ); + } + + return ( +
+

{label}

+ + {content} +
+ ); + } + +} diff --git a/app/javascript/mastodon/components/admin/ReportReasonSelector.js b/app/javascript/mastodon/components/admin/ReportReasonSelector.js new file mode 100644 index 00000000000000..1f91d25175a02b --- /dev/null +++ b/app/javascript/mastodon/components/admin/ReportReasonSelector.js @@ -0,0 +1,159 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import api from 'mastodon/api'; +import { injectIntl, defineMessages } from 'react-intl'; +import classNames from 'classnames'; + +const messages = defineMessages({ + other: { id: 'report.categories.other', defaultMessage: 'Other' }, + spam: { id: 'report.categories.spam', defaultMessage: 'Spam' }, + violation: { id: 'report.categories.violation', defaultMessage: 'Content violates one or more server rules' }, +}); + +class Category extends React.PureComponent { + + static propTypes = { + id: PropTypes.string.isRequired, + text: PropTypes.string.isRequired, + selected: PropTypes.bool, + disabled: PropTypes.bool, + onSelect: PropTypes.func, + children: PropTypes.node, + }; + + handleClick = () => { + const { id, disabled, onSelect } = this.props; + + if (!disabled) { + onSelect(id); + } + }; + + render () { + const { id, text, disabled, selected, children } = this.props; + + return ( +
+ {selected && } + +
+ + {text} +
+ + {(selected && children) && ( +
+ {children} +
+ )} +
+ ); + } + +} + +class Rule extends React.PureComponent { + + static propTypes = { + id: PropTypes.string.isRequired, + text: PropTypes.string.isRequired, + selected: PropTypes.bool, + disabled: PropTypes.bool, + onToggle: PropTypes.func, + }; + + handleClick = () => { + const { id, disabled, onToggle } = this.props; + + if (!disabled) { + onToggle(id); + } + }; + + render () { + const { id, text, disabled, selected } = this.props; + + return ( +
+ + {selected && } + {text} +
+ ); + } + +} + +export default @injectIntl +class ReportReasonSelector extends React.PureComponent { + + static propTypes = { + id: PropTypes.string.isRequired, + category: PropTypes.string.isRequired, + rule_ids: PropTypes.arrayOf(PropTypes.string), + disabled: PropTypes.bool, + intl: PropTypes.object.isRequired, + }; + + state = { + category: this.props.category, + rule_ids: this.props.rule_ids || [], + rules: [], + }; + + componentDidMount() { + api().get('/api/v1/instance').then(res => { + this.setState({ + rules: res.data.rules, + }); + }).catch(err => { + console.error(err); + }); + } + + _save = () => { + const { id, disabled } = this.props; + const { category, rule_ids } = this.state; + + if (disabled) { + return; + } + + api().put(`/api/v1/admin/reports/${id}`, { + category, + rule_ids, + }).catch(err => { + console.error(err); + }); + }; + + handleSelect = id => { + this.setState({ category: id }, () => this._save()); + }; + + handleToggle = id => { + const { rule_ids } = this.state; + + if (rule_ids.includes(id)) { + this.setState({ rule_ids: rule_ids.filter(x => x !== id ) }, () => this._save()); + } else { + this.setState({ rule_ids: [...rule_ids, id] }, () => this._save()); + } + }; + + render () { + const { disabled, intl } = this.props; + const { rules, category, rule_ids } = this.state; + + return ( +
+ + + + {rules.map(rule => )} + +
+ ); + } + +} diff --git a/app/javascript/mastodon/components/admin/Retention.js b/app/javascript/mastodon/components/admin/Retention.js new file mode 100644 index 00000000000000..47c9e715136ced --- /dev/null +++ b/app/javascript/mastodon/components/admin/Retention.js @@ -0,0 +1,151 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import api from 'mastodon/api'; +import { FormattedMessage, FormattedNumber, FormattedDate } from 'react-intl'; +import classNames from 'classnames'; +import { roundTo10 } from 'mastodon/utils/numbers'; + +const dateForCohort = cohort => { + switch(cohort.frequency) { + case 'day': + return ; + default: + return ; + } +}; + +export default class Retention extends React.PureComponent { + + static propTypes = { + start_at: PropTypes.string, + end_at: PropTypes.string, + frequency: PropTypes.string, + }; + + state = { + loading: true, + data: null, + }; + + componentDidMount () { + const { start_at, end_at, frequency } = this.props; + + api().post('/api/v1/admin/retention', { start_at, end_at, frequency }).then(res => { + this.setState({ + loading: false, + data: res.data, + }); + }).catch(err => { + console.error(err); + }); + } + + render () { + const { loading, data } = this.state; + const { frequency } = this.props; + + let content; + + if (loading) { + content = ; + } else { + content = ( + + + + + + + + {data[0].data.slice(1).map((retention, i) => ( + + ))} + + + + + + + + {data[0].data.slice(1).map((retention, i) => { + const average = data.reduce((sum, cohort, k) => cohort.data[i + 1] ? sum + (cohort.data[i + 1].rate - sum)/(k + 1) : sum, 0); + + return ( + + ); + })} + + + + + {data.slice(0, -1).map(cohort => ( + + + + + + {cohort.data.slice(1).map(retention => ( + + ))} + + ))} + +
+
+ +
+
+
+ +
+
+
+ {i + 1} +
+
+
+ +
+
+
+ sum + ((cohort.data[0].value * 1) - sum) / (i + 1), 0)} maximumFractionDigits={0} /> +
+
+
+ +
+
+
+ {dateForCohort(cohort)} +
+
+
+ +
+
+
+ +
+
+ ); + } + + let title = null; + switch(frequency) { + case 'day': + title = ; + break; + default: + title = ; + }; + + return ( +
+

{title}

+ + {content} +
+ ); + } + +} diff --git a/app/javascript/mastodon/components/admin/Trends.js b/app/javascript/mastodon/components/admin/Trends.js new file mode 100644 index 00000000000000..635bdf37d56d1c --- /dev/null +++ b/app/javascript/mastodon/components/admin/Trends.js @@ -0,0 +1,73 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import api from 'mastodon/api'; +import { FormattedMessage } from 'react-intl'; +import classNames from 'classnames'; +import Hashtag from 'mastodon/components/hashtag'; + +export default class Trends extends React.PureComponent { + + static propTypes = { + limit: PropTypes.number.isRequired, + }; + + state = { + loading: true, + data: null, + }; + + componentDidMount () { + const { limit } = this.props; + + api().get('/api/v1/admin/trends/tags', { params: { limit } }).then(res => { + this.setState({ + loading: false, + data: res.data, + }); + }).catch(err => { + console.error(err); + }); + } + + render () { + const { limit } = this.props; + const { loading, data } = this.state; + + let content; + + if (loading) { + content = ( +
+ {Array.from(Array(limit)).map((_, i) => ( + + ))} +
+ ); + } else { + content = ( +
+ {data.map(hashtag => ( + day.uses)} + className={classNames(hashtag.requires_review && 'trends__item--requires-review', !hashtag.trendable && !hashtag.requires_review && 'trends__item--disabled')} + /> + ))} +
+ ); + } + + return ( +
+

+ + {content} +
+ ); + } + +} diff --git a/app/javascript/mastodon/components/attachment_list.js b/app/javascript/mastodon/components/attachment_list.js index ebd696583525ec..0e23889dedc75e 100644 --- a/app/javascript/mastodon/components/attachment_list.js +++ b/app/javascript/mastodon/components/attachment_list.js @@ -2,6 +2,8 @@ import React from 'react'; import ImmutablePropTypes from 'react-immutable-proptypes'; import PropTypes from 'prop-types'; import ImmutablePureComponent from 'react-immutable-pure-component'; +import { FormattedMessage } from 'react-intl'; +import classNames from 'classnames'; import Icon from 'mastodon/components/icon'; const filename = url => url.split('/').pop().split('#')[0].split('?')[0]; @@ -16,29 +18,13 @@ export default class AttachmentList extends ImmutablePureComponent { render () { const { media, compact } = this.props; - if (compact) { - return ( -
-
    - {media.map(attachment => { - const displayUrl = attachment.get('remote_url') || attachment.get('url'); - - return ( -
  • - {filename(displayUrl)} -
  • - ); - })} -
-
- ); - } - return ( -
-
- -
+
+ {!compact && ( +
+ +
+ )}
    {media.map(attachment => { @@ -46,7 +32,11 @@ export default class AttachmentList extends ImmutablePureComponent { return (
  • - {filename(displayUrl)} + + {compact && } + {compact && ' ' } + {displayUrl ? filename(displayUrl) : } +
  • ); })} diff --git a/app/javascript/mastodon/components/check.js b/app/javascript/mastodon/components/check.js new file mode 100644 index 00000000000000..ee2ef1595a2d03 --- /dev/null +++ b/app/javascript/mastodon/components/check.js @@ -0,0 +1,9 @@ +import React from 'react'; + +const Check = () => ( + + + +); + +export default Check; diff --git a/app/javascript/mastodon/components/column_header.js b/app/javascript/mastodon/components/column_header.js index 236e922969d770..cbbc490a83b9d7 100644 --- a/app/javascript/mastodon/components/column_header.js +++ b/app/javascript/mastodon/components/column_header.js @@ -119,8 +119,8 @@ class ColumnHeader extends React.PureComponent { moveButtons = (
    - - + +
    ); } else if (multiColumn && this.props.onPin) { @@ -141,8 +141,8 @@ class ColumnHeader extends React.PureComponent { ]; if (multiColumn) { - collapsedContent.push(moveButtons); collapsedContent.push(pinButton); + collapsedContent.push(moveButtons); } if (children || (multiColumn && this.props.onPin)) { diff --git a/app/javascript/mastodon/components/dropdown_menu.js b/app/javascript/mastodon/components/dropdown_menu.js index 7d0588901c767f..4b4ad83555ff08 100644 --- a/app/javascript/mastodon/components/dropdown_menu.js +++ b/app/javascript/mastodon/components/dropdown_menu.js @@ -6,6 +6,8 @@ import Overlay from 'react-overlays/lib/Overlay'; import Motion from '../features/ui/util/optional_motion'; import spring from 'react-motion/lib/spring'; import { supportsPassiveEvents } from 'detect-passive-events'; +import classNames from 'classnames'; +import { CircularProgress } from 'mastodon/components/loading_indicator'; const listenerOptions = supportsPassiveEvents ? { passive: true } : false; let id = 0; @@ -17,13 +19,18 @@ class DropdownMenu extends React.PureComponent { }; static propTypes = { - items: PropTypes.array.isRequired, + items: PropTypes.oneOfType([PropTypes.array, ImmutablePropTypes.list]).isRequired, + loading: PropTypes.bool, + scrollable: PropTypes.bool, onClose: PropTypes.func.isRequired, style: PropTypes.object, placement: PropTypes.string, arrowOffsetLeft: PropTypes.string, arrowOffsetTop: PropTypes.string, openedViaKeyboard: PropTypes.bool, + renderItem: PropTypes.func, + renderHeader: PropTypes.func, + onItemClick: PropTypes.func.isRequired, }; static defaultProps = { @@ -45,9 +52,11 @@ class DropdownMenu extends React.PureComponent { document.addEventListener('click', this.handleDocumentClick, false); document.addEventListener('keydown', this.handleKeyDown, false); document.addEventListener('touchend', this.handleDocumentClick, listenerOptions); + if (this.focusedItem && this.props.openedViaKeyboard) { this.focusedItem.focus({ preventScroll: true }); } + this.setState({ mounted: true }); } @@ -66,7 +75,7 @@ class DropdownMenu extends React.PureComponent { } handleKeyDown = e => { - const items = Array.from(this.node.getElementsByTagName('a')); + const items = Array.from(this.node.querySelectorAll('a, button')); const index = items.indexOf(document.activeElement); let element = null; @@ -109,21 +118,11 @@ class DropdownMenu extends React.PureComponent { } handleClick = e => { - const i = Number(e.currentTarget.getAttribute('data-index')); - const { action, to } = this.props.items[i]; - - this.props.onClose(); - - if (typeof action === 'function') { - e.preventDefault(); - action(e); - } else if (to) { - e.preventDefault(); - this.context.router.history.push(to); - } + const { onItemClick } = this.props; + onItemClick(e); } - renderItem (option, i) { + renderItem = (option, i) => { if (option === null) { return
  • ; } @@ -140,9 +139,11 @@ class DropdownMenu extends React.PureComponent { } render () { - const { items, style, placement, arrowOffsetLeft, arrowOffsetTop } = this.props; + const { items, style, placement, arrowOffsetLeft, arrowOffsetTop, scrollable, renderHeader, loading } = this.props; const { mounted } = this.state; + let renderItem = this.props.renderItem || this.renderItem; + return ( {({ opacity, scaleX, scaleY }) => ( @@ -152,9 +153,23 @@ class DropdownMenu extends React.PureComponent {
    -
      - {items.map((option, i) => this.renderItem(option, i))} -
    +
    + {loading && ( + + )} + + {!loading && renderHeader && ( +
    + {renderHeader(items)} +
    + )} + + {!loading && ( +
      + {items.map((option, i) => renderItem(option, i, { onClick: this.handleClick, onKeyPress: this.handleItemKeyPress }))} +
    + )} +
    )} @@ -170,11 +185,14 @@ export default class Dropdown extends React.PureComponent { }; static propTypes = { - icon: PropTypes.string.isRequired, - items: PropTypes.array.isRequired, - size: PropTypes.number.isRequired, + children: PropTypes.node, + icon: PropTypes.string, + items: PropTypes.oneOfType([PropTypes.array, ImmutablePropTypes.list]).isRequired, + loading: PropTypes.bool, + size: PropTypes.number, title: PropTypes.string, disabled: PropTypes.bool, + scrollable: PropTypes.bool, status: ImmutablePropTypes.map, isUserTouching: PropTypes.func, onOpen: PropTypes.func.isRequired, @@ -182,6 +200,9 @@ export default class Dropdown extends React.PureComponent { dropdownPlacement: PropTypes.string, openDropdownId: PropTypes.number, openedViaKeyboard: PropTypes.bool, + renderItem: PropTypes.func, + renderHeader: PropTypes.func, + onItemClick: PropTypes.func, }; static defaultProps = { @@ -237,17 +258,21 @@ export default class Dropdown extends React.PureComponent { } handleItemClick = e => { + const { onItemClick } = this.props; const i = Number(e.currentTarget.getAttribute('data-index')); - const { action, to } = this.props.items[i]; + const item = this.props.items[i]; this.handleClose(); - if (typeof action === 'function') { + if (typeof onItemClick === 'function') { + e.preventDefault(); + onItemClick(item, i); + } else if (item && typeof item.action === 'function') { e.preventDefault(); - action(); - } else if (to) { + item.action(); + } else if (item && item.to) { e.preventDefault(); - this.context.router.history.push(to); + this.context.router.history.push(item.to); } } @@ -265,29 +290,67 @@ export default class Dropdown extends React.PureComponent { } } + close = () => { + this.handleClose(); + } + render () { - const { icon, items, size, title, disabled, dropdownPlacement, openDropdownId, openedViaKeyboard } = this.props; + const { + icon, + items, + size, + title, + disabled, + loading, + scrollable, + dropdownPlacement, + openDropdownId, + openedViaKeyboard, + children, + renderItem, + renderHeader, + } = this.props; + const open = this.state.id === openDropdownId; + const button = children ? React.cloneElement(React.Children.only(children), { + ref: this.setTargetRef, + onClick: this.handleClick, + onMouseDown: this.handleMouseDown, + onKeyDown: this.handleButtonKeyDown, + onKeyPress: this.handleKeyPress, + }) : ( + + ); + return ( -
    - + + {button} - + -
    + ); } diff --git a/app/javascript/mastodon/components/edited_timestamp/containers/dropdown_menu_container.js b/app/javascript/mastodon/components/edited_timestamp/containers/dropdown_menu_container.js new file mode 100644 index 00000000000000..e30c18372e9a2d --- /dev/null +++ b/app/javascript/mastodon/components/edited_timestamp/containers/dropdown_menu_container.js @@ -0,0 +1,27 @@ +import { connect } from 'react-redux'; +import { openDropdownMenu, closeDropdownMenu } from 'mastodon/actions/dropdown_menu'; +import { fetchHistory } from 'mastodon/actions/history'; +import DropdownMenu from 'mastodon/components/dropdown_menu'; + +const mapStateToProps = (state, { statusId }) => ({ + dropdownPlacement: state.getIn(['dropdown_menu', 'placement']), + openDropdownId: state.getIn(['dropdown_menu', 'openId']), + openedViaKeyboard: state.getIn(['dropdown_menu', 'keyboard']), + items: state.getIn(['history', statusId, 'items']), + loading: state.getIn(['history', statusId, 'loading']), +}); + +const mapDispatchToProps = (dispatch, { statusId }) => ({ + + onOpen (id, onItemClick, dropdownPlacement, keyboard) { + dispatch(fetchHistory(statusId)); + dispatch(openDropdownMenu(id, dropdownPlacement, keyboard)); + }, + + onClose (id) { + dispatch(closeDropdownMenu(id)); + }, + +}); + +export default connect(mapStateToProps, mapDispatchToProps)(DropdownMenu); diff --git a/app/javascript/mastodon/components/edited_timestamp/index.js b/app/javascript/mastodon/components/edited_timestamp/index.js new file mode 100644 index 00000000000000..bebf938865bc98 --- /dev/null +++ b/app/javascript/mastodon/components/edited_timestamp/index.js @@ -0,0 +1,70 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { FormattedMessage, injectIntl } from 'react-intl'; +import Icon from 'mastodon/components/icon'; +import DropdownMenu from './containers/dropdown_menu_container'; +import { connect } from 'react-redux'; +import { openModal } from 'mastodon/actions/modal'; +import RelativeTimestamp from 'mastodon/components/relative_timestamp'; +import InlineAccount from 'mastodon/components/inline_account'; + +const mapDispatchToProps = (dispatch, { statusId }) => ({ + + onItemClick (index) { + dispatch(openModal('COMPARE_HISTORY', { index, statusId })); + }, + +}); + +export default @connect(null, mapDispatchToProps) +@injectIntl +class EditedTimestamp extends React.PureComponent { + + static propTypes = { + statusId: PropTypes.string.isRequired, + timestamp: PropTypes.string.isRequired, + intl: PropTypes.object.isRequired, + onItemClick: PropTypes.func.isRequired, + }; + + handleItemClick = (item, i) => { + const { onItemClick } = this.props; + onItemClick(i); + }; + + renderHeader = items => { + return ( + + ); + } + + renderItem = (item, index, { onClick, onKeyPress }) => { + const formattedDate = ; + const formattedName = ; + + const label = item.get('original') ? ( + + ) : ( + + ); + + return ( +
  • + +
  • + ); + } + + render () { + const { timestamp, intl, statusId } = this.props; + + return ( + + + + ); + } + +} diff --git a/app/javascript/mastodon/components/hashtag.js b/app/javascript/mastodon/components/hashtag.js index 75fcf20e3d95df..7f442d189ca561 100644 --- a/app/javascript/mastodon/components/hashtag.js +++ b/app/javascript/mastodon/components/hashtag.js @@ -6,6 +6,8 @@ import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; import Permalink from './permalink'; import ShortNumber from 'mastodon/components/short_number'; +import Skeleton from 'mastodon/components/skeleton'; +import classNames from 'classnames'; class SilentErrorBoundary extends React.Component { @@ -36,7 +38,7 @@ class SilentErrorBoundary extends React.Component { * * @type {(displayNumber: JSX.Element, pluralReady: number) => JSX.Element} */ -const accountsCountRenderer = (displayNumber, pluralReady) => ( +export const accountsCountRenderer = (displayNumber, pluralReady) => ( ( /> ); -const Hashtag = ({ hashtag }) => ( -
    +export const ImmutableHashtag = ({ hashtag }) => ( + day.get('uses')).toArray()} + /> +); + +ImmutableHashtag.propTypes = { + hashtag: ImmutablePropTypes.map.isRequired, +}; + +const Hashtag = ({ name, href, to, people, uses, history, className }) => ( +
    - - #{hashtag.get('name')} + + {name ? #{name} : } - + {typeof people !== 'undefined' ? : }
    - + {typeof uses !== 'undefined' ? : }
    - day.get('uses')) - .toArray()} - > + 0)}> @@ -94,7 +89,13 @@ const Hashtag = ({ hashtag }) => ( ); Hashtag.propTypes = { - hashtag: ImmutablePropTypes.map.isRequired, + name: PropTypes.string, + href: PropTypes.string, + to: PropTypes.string, + people: PropTypes.number, + uses: PropTypes.number, + history: PropTypes.arrayOf(PropTypes.number), + className: PropTypes.string, }; export default Hashtag; diff --git a/app/javascript/mastodon/components/icon_button.js b/app/javascript/mastodon/components/icon_button.js index 7ec39198a4cb48..6a653675b83cd3 100644 --- a/app/javascript/mastodon/components/icon_button.js +++ b/app/javascript/mastodon/components/icon_button.js @@ -27,6 +27,7 @@ export default class IconButton extends React.PureComponent { tabIndex: PropTypes.string, counter: PropTypes.number, obfuscateCount: PropTypes.bool, + href: PropTypes.string, }; static defaultProps = { @@ -102,6 +103,7 @@ export default class IconButton extends React.PureComponent { title, counter, obfuscateCount, + href, } = this.props; const { @@ -123,6 +125,20 @@ export default class IconButton extends React.PureComponent { style.width = 'auto'; } + let contents = ( + + + ); + + if (href) { + contents = ( + + {contents} + + ); + } + return ( ); } diff --git a/app/javascript/mastodon/components/inline_account.js b/app/javascript/mastodon/components/inline_account.js new file mode 100644 index 00000000000000..a1b4955904b001 --- /dev/null +++ b/app/javascript/mastodon/components/inline_account.js @@ -0,0 +1,34 @@ +import React from 'react'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import { connect } from 'react-redux'; +import { makeGetAccount } from 'mastodon/selectors'; +import Avatar from 'mastodon/components/avatar'; + +const makeMapStateToProps = () => { + const getAccount = makeGetAccount(); + + const mapStateToProps = (state, { accountId }) => ({ + account: getAccount(state, accountId), + }); + + return mapStateToProps; +}; + +export default @connect(makeMapStateToProps) +class InlineAccount extends React.PureComponent { + + static propTypes = { + account: ImmutablePropTypes.map.isRequired, + }; + + render () { + const { account } = this.props; + + return ( + + {account.get('username')} + + ); + } + +} diff --git a/app/javascript/mastodon/components/loading_indicator.js b/app/javascript/mastodon/components/loading_indicator.js index d6a5adb6fe7c0f..33c59d94c02f49 100644 --- a/app/javascript/mastodon/components/loading_indicator.js +++ b/app/javascript/mastodon/components/loading_indicator.js @@ -1,10 +1,31 @@ import React from 'react'; -import { FormattedMessage } from 'react-intl'; +import PropTypes from 'prop-types'; + +export const CircularProgress = ({ size, strokeWidth }) => { + const viewBox = `0 0 ${size} ${size}`; + const radius = (size - strokeWidth) / 2; + + return ( + + + + ); +}; + +CircularProgress.propTypes = { + size: PropTypes.number.isRequired, + strokeWidth: PropTypes.number.isRequired, +}; const LoadingIndicator = () => (
    -
    - +
    ); diff --git a/app/javascript/mastodon/components/media_attachments.js b/app/javascript/mastodon/components/media_attachments.js new file mode 100644 index 00000000000000..d27720de41c1c2 --- /dev/null +++ b/app/javascript/mastodon/components/media_attachments.js @@ -0,0 +1,116 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import ImmutablePureComponent from 'react-immutable-pure-component'; +import { MediaGallery, Video, Audio } from 'mastodon/features/ui/util/async-components'; +import Bundle from 'mastodon/features/ui/components/bundle'; +import noop from 'lodash/noop'; + +export default class MediaAttachments extends ImmutablePureComponent { + + static propTypes = { + status: ImmutablePropTypes.map.isRequired, + height: PropTypes.number, + width: PropTypes.number, + }; + + static defaultProps = { + height: 110, + width: 239, + }; + + updateOnProps = [ + 'status', + ]; + + renderLoadingMediaGallery = () => { + const { height, width } = this.props; + + return ( +
    + ); + } + + renderLoadingVideoPlayer = () => { + const { height, width } = this.props; + + return ( +
    + ); + } + + renderLoadingAudioPlayer = () => { + const { height, width } = this.props; + + return ( +
    + ); + } + + render () { + const { status, width, height } = this.props; + const mediaAttachments = status.get('media_attachments'); + + if (mediaAttachments.size === 0) { + return null; + } + + if (mediaAttachments.getIn([0, 'type']) === 'audio') { + const audio = mediaAttachments.get(0); + + return ( + + {Component => ( + + )} + + ); + } else if (mediaAttachments.getIn([0, 'type']) === 'video') { + const video = mediaAttachments.get(0); + + return ( + + {Component => ( + + )} + + ); + } else { + return ( + + {Component => ( + + )} + + ); + } + } + +} diff --git a/app/javascript/mastodon/components/modal_root.js b/app/javascript/mastodon/components/modal_root.js index 755c46fd6b20e6..b894aeaf989272 100644 --- a/app/javascript/mastodon/components/modal_root.js +++ b/app/javascript/mastodon/components/modal_root.js @@ -18,6 +18,7 @@ export default class ModalRoot extends React.PureComponent { g: PropTypes.number, b: PropTypes.number, }), + ignoreFocus: PropTypes.bool, }; activeElement = this.props.children ? document.activeElement : null; @@ -72,7 +73,9 @@ export default class ModalRoot extends React.PureComponent { // immediately selectable, we have to wait for observers to run, as // described in https://github.com/WICG/inert#performance-and-gotchas Promise.resolve().then(() => { - this.activeElement.focus({ preventScroll: true }); + if (!this.props.ignoreFocus) { + this.activeElement.focus({ preventScroll: true }); + } this.activeElement = null; }).catch(console.error); diff --git a/app/javascript/mastodon/components/poll.js b/app/javascript/mastodon/components/poll.js index 477f56e1300ffe..85aa28816ca2bc 100644 --- a/app/javascript/mastodon/components/poll.js +++ b/app/javascript/mastodon/components/poll.js @@ -12,8 +12,18 @@ import RelativeTimestamp from './relative_timestamp'; import Icon from 'mastodon/components/icon'; const messages = defineMessages({ - closed: { id: 'poll.closed', defaultMessage: 'Closed' }, - voted: { id: 'poll.voted', defaultMessage: 'You voted for this answer', description: 'Tooltip of the "voted" checkmark in polls' }, + closed: { + id: 'poll.closed', + defaultMessage: 'Closed', + }, + voted: { + id: 'poll.voted', + defaultMessage: 'You voted for this answer', + }, + votes: { + id: 'poll.votes', + defaultMessage: '{votes, plural, one {# vote} other {# votes}}', + }, }); const makeEmojiMap = record => record.get('emojis').reduce((obj, emoji) => { @@ -148,9 +158,16 @@ class Poll extends ImmutablePureComponent { data-index={optionIndex} /> )} - {showResults && - {Math.round(percent)}% - } + {showResults && ( + + {Math.round(percent)}% + + )} { } }; -export const timeAgoString = (intl, date, now, year, timeGiven = true) => { +export const timeAgoString = (intl, date, now, year, timeGiven, short) => { const delta = now - date.getTime(); let relativeTime; @@ -74,16 +79,16 @@ export const timeAgoString = (intl, date, now, year, timeGiven = true) => { if (delta < DAY && !timeGiven) { relativeTime = intl.formatMessage(messages.today); } else if (delta < 10 * SECOND) { - relativeTime = intl.formatMessage(messages.just_now); + relativeTime = intl.formatMessage(short ? messages.just_now : messages.just_now_full); } else if (delta < 7 * DAY) { if (delta < MINUTE) { - relativeTime = intl.formatMessage(messages.seconds, { number: Math.floor(delta / SECOND) }); + relativeTime = intl.formatMessage(short ? messages.seconds : messages.seconds_full, { number: Math.floor(delta / SECOND) }); } else if (delta < HOUR) { - relativeTime = intl.formatMessage(messages.minutes, { number: Math.floor(delta / MINUTE) }); + relativeTime = intl.formatMessage(short ? messages.minutes : messages.minutes_full, { number: Math.floor(delta / MINUTE) }); } else if (delta < DAY) { - relativeTime = intl.formatMessage(messages.hours, { number: Math.floor(delta / HOUR) }); + relativeTime = intl.formatMessage(short ? messages.hours : messages.hours_full, { number: Math.floor(delta / HOUR) }); } else { - relativeTime = intl.formatMessage(messages.days, { number: Math.floor(delta / DAY) }); + relativeTime = intl.formatMessage(short ? messages.days : messages.days_full, { number: Math.floor(delta / DAY) }); } } else if (date.getFullYear() === year) { relativeTime = intl.formatDate(date, shortDateFormatOptions); @@ -124,6 +129,7 @@ class RelativeTimestamp extends React.Component { timestamp: PropTypes.string.isRequired, year: PropTypes.number.isRequired, futureDate: PropTypes.bool, + short: PropTypes.bool, }; state = { @@ -132,6 +138,7 @@ class RelativeTimestamp extends React.Component { static defaultProps = { year: (new Date()).getFullYear(), + short: true, }; shouldComponentUpdate (nextProps, nextState) { @@ -176,11 +183,11 @@ class RelativeTimestamp extends React.Component { } render () { - const { timestamp, intl, year, futureDate } = this.props; + const { timestamp, intl, year, futureDate, short } = this.props; const timeGiven = timestamp.includes('T'); const date = new Date(timestamp); - const relativeTime = futureDate ? timeRemainingString(intl, date, this.state.now, timeGiven) : timeAgoString(intl, date, this.state.now, year, timeGiven); + const relativeTime = futureDate ? timeRemainingString(intl, date, this.state.now, timeGiven) : timeAgoString(intl, date, this.state.now, year, timeGiven, short); return (