diff --git a/.eslintrc.js b/.eslintrc.js index 1b36bcee25..ebe07f6e79 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -165,7 +165,7 @@ module.exports = defineConfig({ // }, // ], 'jsx-a11y/no-noninteractive-tabindex': 'off', - 'jsx-a11y/no-onchange': 'warn', + 'jsx-a11y/no-onchange': 'off', // recommended is full 'error' 'jsx-a11y/no-static-element-interactions': [ 'warn', diff --git a/.github/workflows/test-migrations-one-step.yml b/.github/workflows/test-migrations-one-step.yml index 5dca8e376d..1ff5cc06b9 100644 --- a/.github/workflows/test-migrations-one-step.yml +++ b/.github/workflows/test-migrations-one-step.yml @@ -78,23 +78,8 @@ jobs: - name: Create database run: './bin/rails db:create' - - name: Run migrations up to v2.0.0 - run: './bin/rails db:migrate VERSION=20171010025614' - - - name: Populate database with test data - run: './bin/rails tests:migrations:populate_v2' - - - name: Run migrations up to v2.4.0 - run: './bin/rails db:migrate VERSION=20180514140000' - - - name: Populate database with test data - run: './bin/rails tests:migrations:populate_v2_4' - - - name: Run migrations up to v2.4.3 - run: './bin/rails db:migrate VERSION=20180707154237' - - - name: Populate database with test data - run: './bin/rails tests:migrations:populate_v2_4_3' + - name: Run historical migrations with data population + run: './bin/rails tests:migrations:prepare_database' - name: Run all remaining migrations run: './bin/rails db:migrate' diff --git a/.github/workflows/test-migrations-two-step.yml b/.github/workflows/test-migrations-two-step.yml index 59485d285d..6698847315 100644 --- a/.github/workflows/test-migrations-two-step.yml +++ b/.github/workflows/test-migrations-two-step.yml @@ -45,6 +45,7 @@ jobs: --health-retries 5 ports: - 5432:5432 + redis: image: redis:7-alpine options: >- @@ -77,28 +78,11 @@ jobs: - name: Create database run: './bin/rails db:create' - - name: Run migrations up to v2.0.0 - run: './bin/rails db:migrate VERSION=20171010025614' - - - name: Populate database with test data - run: './bin/rails tests:migrations:populate_v2' - - - name: Run pre-deployment migrations up to v2.4.0 - run: './bin/rails db:migrate VERSION=20180514140000' + - name: Run historical migrations with data population + run: './bin/rails tests:migrations:prepare_database' env: SKIP_POST_DEPLOYMENT_MIGRATIONS: true - - name: Populate database with test data - run: './bin/rails tests:migrations:populate_v2_4' - - - name: Run migrations up to v2.4.3 - run: './bin/rails db:migrate VERSION=20180707154237' - env: - SKIP_POST_DEPLOYMENT_MIGRATIONS: true - - - name: Populate database with test data - run: './bin/rails tests:migrations:populate_v2_4_3' - - name: Run all remaining pre-deployment migrations run: './bin/rails db:migrate' env: diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 3d153e4c4c..8324722e5b 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,6 +1,6 @@ # This configuration was generated by # `rubocop --auto-gen-config --auto-gen-only-exclude --no-exclude-limit --no-offense-counts --no-auto-gen-timestamp` -# using RuboCop version 1.59.0. +# using RuboCop version 1.60.2. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new @@ -73,21 +73,6 @@ Rails/UniqueValidationWithoutIndex: - 'app/models/identity.rb' - 'app/models/webauthn_credential.rb' -# This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: exists, where -Rails/WhereExists: - Exclude: - - 'app/controllers/activitypub/inboxes_controller.rb' - - 'app/controllers/admin/email_domain_blocks_controller.rb' - - 'app/policies/status_policy.rb' - - 'app/serializers/rest/announcement_serializer.rb' - - 'app/workers/move_worker.rb' - - 'spec/models/account_spec.rb' - - 'spec/services/activitypub/process_collection_service_spec.rb' - - 'spec/services/purge_domain_service_spec.rb' - - 'spec/services/unallow_domain_service_spec.rb' - # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: AllowedMethods, AllowedPatterns. # AllowedMethods: ==, equal?, eql? @@ -139,10 +124,6 @@ Style/GlobalStdStream: # Configuration parameters: MinBodyLength, AllowConsecutiveConditionals. Style/GuardClause: Exclude: - - 'app/controllers/admin/confirmations_controller.rb' - - 'app/controllers/auth/confirmations_controller.rb' - - 'app/controllers/auth/passwords_controller.rb' - - 'app/controllers/settings/two_factor_authentication/webauthn_credentials_controller.rb' - 'app/lib/activitypub/activity/block.rb' - 'app/lib/request.rb' - 'app/lib/request_pool.rb' @@ -298,13 +279,6 @@ Style/StringLiterals: - 'config/routes.rb' - 'db/schema.rb' -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle, AllowSafeAssignment. -# SupportedStyles: require_parentheses, require_no_parentheses, require_parentheses_when_complex -Style/TernaryParentheses: - Exclude: - - 'config/environments/development.rb' - # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyleForMultiline. # SupportedStylesForMultiline: comma, consistent_comma, no_comma diff --git a/Gemfile b/Gemfile index 71fe3056a0..ea72d40c7b 100644 --- a/Gemfile +++ b/Gemfile @@ -123,7 +123,7 @@ group :test do gem 'database_cleaner-active_record' # Used to mock environment variables - gem 'climate_control', '~> 0.2' + gem 'climate_control' # Generating fake data for specs gem 'faker', '~> 3.2' diff --git a/Gemfile.lock b/Gemfile.lock index 57ea0cc970..4662df8773 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -185,7 +185,7 @@ GEM elasticsearch (>= 7.12.0, < 7.14.0) elasticsearch-dsl chunky_png (1.4.0) - climate_control (0.2.0) + climate_control (1.2.0) cocoon (1.2.15) color_diff (0.1) concurrent-ruby (1.2.3) @@ -360,7 +360,7 @@ GEM rainbow (>= 2.2.2, < 4.0) terminal-table (>= 1.5.1) idn-ruby (0.1.5) - io-console (0.7.1) + io-console (0.7.2) irb (1.11.1) rdoc reline (>= 0.4.2) @@ -445,7 +445,7 @@ GEM mime-types-data (3.2023.1205) mini_mime (1.1.5) mini_portile2 (2.8.5) - minitest (5.21.1) + minitest (5.21.2) msgpack (1.7.2) multi_json (1.15.0) multipart-post (2.3.0) @@ -636,7 +636,7 @@ GEM rspec-mocks (3.12.6) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.12.0) - rspec-rails (6.1.0) + rspec-rails (6.1.1) actionpack (>= 6.1) activesupport (>= 6.1) railties (>= 6.1) @@ -748,8 +748,8 @@ GEM temple (0.10.3) terminal-table (3.0.2) unicode-display_width (>= 1.1.1, < 3) - terrapin (0.6.0) - climate_control (>= 0.0.3, < 1.0) + terrapin (1.0.1) + climate_control test-prof (1.3.1) thor (1.3.0) tilt (2.3.0) @@ -838,7 +838,7 @@ DEPENDENCIES capybara (~> 3.39) charlock_holmes (~> 0.7.7) chewy (~> 7.3) - climate_control (~> 0.2) + climate_control cocoon (~> 1.2) color_diff (~> 0.1) concurrent-ruby diff --git a/app/controllers/activitypub/inboxes_controller.rb b/app/controllers/activitypub/inboxes_controller.rb index 5ee85474e7..ba85e0a722 100644 --- a/app/controllers/activitypub/inboxes_controller.rb +++ b/app/controllers/activitypub/inboxes_controller.rb @@ -24,7 +24,7 @@ class ActivityPub::InboxesController < ActivityPub::BaseController def unknown_affected_account? json = Oj.load(body, mode: :strict) - json.is_a?(Hash) && %w(Delete Update).include?(json['type']) && json['actor'].present? && json['actor'] == value_or_id(json['object']) && !Account.where(uri: json['actor']).exists? + json.is_a?(Hash) && %w(Delete Update).include?(json['type']) && json['actor'].present? && json['actor'] == value_or_id(json['object']) && !Account.exists?(uri: json['actor']) rescue Oj::ParseError false end diff --git a/app/controllers/admin/confirmations_controller.rb b/app/controllers/admin/confirmations_controller.rb index 7ccf5c9012..702550eecc 100644 --- a/app/controllers/admin/confirmations_controller.rb +++ b/app/controllers/admin/confirmations_controller.rb @@ -3,7 +3,7 @@ module Admin class ConfirmationsController < BaseController before_action :set_user - before_action :check_confirmation, only: [:resend] + before_action :redirect_confirmed_user, only: [:resend], if: :user_confirmed? def create authorize @user, :confirm? @@ -25,11 +25,13 @@ module Admin private - def check_confirmation - if @user.confirmed? - flash[:error] = I18n.t('admin.accounts.resend_confirmation.already_confirmed') - redirect_to admin_accounts_path - end + def redirect_confirmed_user + flash[:error] = I18n.t('admin.accounts.resend_confirmation.already_confirmed') + redirect_to admin_accounts_path + end + + def user_confirmed? + @user.confirmed? end end end diff --git a/app/controllers/admin/email_domain_blocks_controller.rb b/app/controllers/admin/email_domain_blocks_controller.rb index ff754bc0b4..faa0a061a6 100644 --- a/app/controllers/admin/email_domain_blocks_controller.rb +++ b/app/controllers/admin/email_domain_blocks_controller.rb @@ -38,7 +38,7 @@ module Admin log_action :create, @email_domain_block (@email_domain_block.other_domains || []).uniq.each do |domain| - next if EmailDomainBlock.where(domain: domain).exists? + next if EmailDomainBlock.exists?(domain: domain) other_email_domain_block = EmailDomainBlock.create!(domain: domain, allow_with_approval: @email_domain_block.allow_with_approval, parent: @email_domain_block) log_action :create, other_email_domain_block diff --git a/app/controllers/admin/export_domain_blocks_controller.rb b/app/controllers/admin/export_domain_blocks_controller.rb index ffc4478172..9caafd9684 100644 --- a/app/controllers/admin/export_domain_blocks_controller.rb +++ b/app/controllers/admin/export_domain_blocks_controller.rb @@ -49,7 +49,7 @@ module Admin next end - @warning_domains = Instance.where(domain: @domain_blocks.map(&:domain)).where('EXISTS (SELECT 1 FROM follows JOIN accounts ON follows.account_id = accounts.id OR follows.target_account_id = accounts.id WHERE accounts.domain = instances.domain)').pluck(:domain) + @warning_domains = instances_from_imported_blocks.pluck(:domain) rescue ActionController::ParameterMissing flash.now[:alert] = I18n.t('admin.export_domain_blocks.no_file') set_dummy_import! @@ -58,6 +58,10 @@ module Admin private + def instances_from_imported_blocks + Instance.with_domain_follows(@domain_blocks.map(&:domain)) + end + def export_filename 'domain_blocks.csv' end diff --git a/app/controllers/auth/confirmations_controller.rb b/app/controllers/auth/confirmations_controller.rb index d9cd630905..7ca7be5f8e 100644 --- a/app/controllers/auth/confirmations_controller.rb +++ b/app/controllers/auth/confirmations_controller.rb @@ -7,7 +7,7 @@ class Auth::ConfirmationsController < Devise::ConfirmationsController before_action :set_body_classes before_action :set_confirmation_user!, only: [:show, :confirm_captcha] - before_action :require_unconfirmed! + before_action :redirect_confirmed_user, if: :signed_in_confirmed_user? before_action :extend_csp_for_captcha!, only: [:show, :confirm_captcha] before_action :require_captcha_if_needed!, only: [:show] @@ -65,10 +65,12 @@ class Auth::ConfirmationsController < Devise::ConfirmationsController @confirmation_user.nil? || @confirmation_user.confirmed? end - def require_unconfirmed! - if user_signed_in? && current_user.confirmed? && current_user.unconfirmed_email.blank? - redirect_to(current_user.approved? ? root_path : edit_user_registration_path) - end + def redirect_confirmed_user + redirect_to(current_user.approved? ? root_path : edit_user_registration_path) + end + + def signed_in_confirmed_user? + user_signed_in? && current_user.confirmed? && current_user.unconfirmed_email.blank? end def set_body_classes diff --git a/app/controllers/auth/passwords_controller.rb b/app/controllers/auth/passwords_controller.rb index a752194d5b..de001f062b 100644 --- a/app/controllers/auth/passwords_controller.rb +++ b/app/controllers/auth/passwords_controller.rb @@ -2,7 +2,7 @@ class Auth::PasswordsController < Devise::PasswordsController skip_before_action :check_self_destruct! - before_action :check_validity_of_reset_password_token, only: :edit + before_action :redirect_invalid_reset_token, only: :edit, unless: :reset_password_token_is_valid? before_action :set_body_classes layout 'auth' @@ -19,11 +19,9 @@ class Auth::PasswordsController < Devise::PasswordsController private - def check_validity_of_reset_password_token - unless reset_password_token_is_valid? - flash[:error] = I18n.t('auth.invalid_reset_password_token') - redirect_to new_password_path(resource_name) - end + def redirect_invalid_reset_token + flash[:error] = I18n.t('auth.invalid_reset_password_token') + redirect_to new_password_path(resource_name) end def set_body_classes diff --git a/app/controllers/redirect/accounts_controller.rb b/app/controllers/redirect/accounts_controller.rb index 98d2cc2b1f..713ccf2ca1 100644 --- a/app/controllers/redirect/accounts_controller.rb +++ b/app/controllers/redirect/accounts_controller.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class Redirect::AccountsController < ApplicationController +class Redirect::AccountsController < Redirect::BaseController private def set_resource 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 c86ede4f3a..9714d54f95 100644 --- a/app/controllers/settings/two_factor_authentication/webauthn_credentials_controller.rb +++ b/app/controllers/settings/two_factor_authentication/webauthn_credentials_controller.rb @@ -6,8 +6,8 @@ module Settings skip_before_action :check_self_destruct! skip_before_action :require_functional! - before_action :require_otp_enabled - before_action :require_webauthn_enabled, only: [:index, :destroy] + before_action :redirect_invalid_otp, unless: -> { current_user.otp_enabled? } + before_action :redirect_invalid_webauthn, only: [:index, :destroy], unless: -> { current_user.webauthn_enabled? } def index; end def new; end @@ -85,18 +85,14 @@ module Settings private - def require_otp_enabled - unless current_user.otp_enabled? - flash[:error] = t('webauthn_credentials.otp_required') - redirect_to settings_two_factor_authentication_methods_path - end + def redirect_invalid_otp + flash[:error] = t('webauthn_credentials.otp_required') + redirect_to settings_two_factor_authentication_methods_path end - def require_webauthn_enabled - unless current_user.webauthn_enabled? - flash[:error] = t('webauthn_credentials.not_enabled') - redirect_to settings_two_factor_authentication_methods_path - end + def redirect_invalid_webauthn + flash[:error] = t('webauthn_credentials.not_enabled') + redirect_to settings_two_factor_authentication_methods_path end end end diff --git a/app/javascript/images/warning-stripes.svg b/app/javascript/images/warning-stripes.svg new file mode 100755 index 0000000000..9d68acdada --- /dev/null +++ b/app/javascript/images/warning-stripes.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/javascript/mastodon/components/__tests__/__snapshots__/autosuggest_emoji-test.jsx.snap b/app/javascript/mastodon/components/__tests__/__snapshots__/autosuggest_emoji-test.jsx.snap index 1c37278483..dc955b7abe 100644 --- a/app/javascript/mastodon/components/__tests__/__snapshots__/autosuggest_emoji-test.jsx.snap +++ b/app/javascript/mastodon/components/__tests__/__snapshots__/autosuggest_emoji-test.jsx.snap @@ -9,7 +9,11 @@ exports[` renders emoji with custom url 1`] = ` className="emojione" src="http://example.com/emoji.png" /> - :foobar: +
+ :foobar: +
`; @@ -22,6 +26,10 @@ exports[` renders native emoji 1`] = ` className="emojione" src="/emoji/1f499.svg" /> - :foobar: +
+ :foobar: +
`; diff --git a/app/javascript/mastodon/components/account.jsx b/app/javascript/mastodon/components/account.jsx index 5146bf5a5b..f8808b5eef 100644 --- a/app/javascript/mastodon/components/account.jsx +++ b/app/javascript/mastodon/components/account.jsx @@ -37,10 +37,10 @@ class Account extends ImmutablePureComponent { static propTypes = { size: PropTypes.number, account: ImmutablePropTypes.record, - onFollow: PropTypes.func.isRequired, - onBlock: PropTypes.func.isRequired, - onMute: PropTypes.func.isRequired, - onMuteNotifications: PropTypes.func.isRequired, + onFollow: PropTypes.func, + onBlock: PropTypes.func, + onMute: PropTypes.func, + onMuteNotifications: PropTypes.func, intl: PropTypes.object.isRequired, hidden: PropTypes.bool, hideButtons: PropTypes.bool, diff --git a/app/javascript/mastodon/components/autosuggest_emoji.jsx b/app/javascript/mastodon/components/autosuggest_emoji.jsx index 28f628b4ac..adea1d9f4b 100644 --- a/app/javascript/mastodon/components/autosuggest_emoji.jsx +++ b/app/javascript/mastodon/components/autosuggest_emoji.jsx @@ -35,7 +35,7 @@ export default class AutosuggestEmoji extends PureComponent { alt={emoji.native || emoji.colons} /> - {emoji.colons} +
{emoji.colons}
); } diff --git a/app/javascript/mastodon/components/autosuggest_hashtag.tsx b/app/javascript/mastodon/components/autosuggest_hashtag.tsx index e83d493c2c..036946b0f1 100644 --- a/app/javascript/mastodon/components/autosuggest_hashtag.tsx +++ b/app/javascript/mastodon/components/autosuggest_hashtag.tsx @@ -1,5 +1,3 @@ -import { FormattedMessage } from 'react-intl'; - import { ShortNumber } from 'mastodon/components/short_number'; interface Props { @@ -16,27 +14,18 @@ interface Props { }; } -export const AutosuggestHashtag: React.FC = ({ tag }) => { - const weeklyUses = tag.history && ( - total + day.uses * 1, 0)} - /> - ); - - return ( -
-
- #{tag.name} -
- {tag.history !== undefined && ( -
- -
- )} +export const AutosuggestHashtag: React.FC = ({ tag }) => ( +
+
+ #{tag.name}
- ); -}; + + {tag.history !== undefined && ( +
+ total + day.uses * 1, 0)} + /> +
+ )} +
+); diff --git a/app/javascript/mastodon/components/autosuggest_input.jsx b/app/javascript/mastodon/components/autosuggest_input.jsx index 06cbb5d75b..f707a18e1d 100644 --- a/app/javascript/mastodon/components/autosuggest_input.jsx +++ b/app/javascript/mastodon/components/autosuggest_input.jsx @@ -5,6 +5,8 @@ import classNames from 'classnames'; import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePureComponent from 'react-immutable-pure-component'; +import Overlay from 'react-overlays/Overlay'; + import AutosuggestAccountContainer from '../features/compose/containers/autosuggest_account_container'; import AutosuggestEmoji from './autosuggest_emoji'; @@ -195,34 +197,37 @@ export default class AutosuggestInput extends ImmutablePureComponent { return (
- - -
- {suggestions.map(this.renderSuggestion)} -
+ + {({ props }) => ( +
+
+ {suggestions.map(this.renderSuggestion)} +
+
+ )} +
); } diff --git a/app/javascript/mastodon/components/autosuggest_textarea.jsx b/app/javascript/mastodon/components/autosuggest_textarea.jsx index 4d173af59d..07fbccacc9 100644 --- a/app/javascript/mastodon/components/autosuggest_textarea.jsx +++ b/app/javascript/mastodon/components/autosuggest_textarea.jsx @@ -5,6 +5,7 @@ import classNames from 'classnames'; import ImmutablePropTypes from 'react-immutable-proptypes'; +import Overlay from 'react-overlays/Overlay'; import Textarea from 'react-textarea-autosize'; import AutosuggestAccountContainer from '../features/compose/containers/autosuggest_account_container'; @@ -52,7 +53,6 @@ const AutosuggestTextarea = forwardRef(({ onFocus, autoFocus = true, lang, - children, }, textareaRef) => { const [suggestionsHidden, setSuggestionsHidden] = useState(true); @@ -183,40 +183,38 @@ const AutosuggestTextarea = forwardRef(({ ); }; - return [ -
-
-