From 2859790890cc53101d55e132ac05c9f59db2bad5 Mon Sep 17 00:00:00 2001 From: ysksn Date: Sun, 21 Jul 2019 10:40:08 +0900 Subject: [PATCH 01/24] Not to create an account if already exist (#11366) --- db/seeds.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/db/seeds.rb b/db/seeds.rb index 5f43fbac8b..b112cf0738 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -1,7 +1,8 @@ Doorkeeper::Application.create!(name: 'Web', superapp: true, redirect_uri: Doorkeeper.configuration.native_redirect_uri, scopes: 'read write follow') domain = ENV['LOCAL_DOMAIN'] || Rails.configuration.x.local_domain -Account.create!(id: -99, actor_type: 'Application', locked: true, username: domain) +account = Account.find_or_initialize_by(id: -99, actor_type: 'Application', locked: true, username: domain) +account.save! if Rails.env.development? admin = Account.where(username: 'admin').first_or_initialize(username: 'admin') From 4bd58b7f2da369a608eacb97f832728ddc139ce8 Mon Sep 17 00:00:00 2001 From: ThibG Date: Sun, 21 Jul 2019 03:40:27 +0200 Subject: [PATCH 02/24] Display custom emoji in bio field names (#11350) Already displayed in public pages, but not WebUI --- app/javascript/mastodon/actions/importer/normalizer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/mastodon/actions/importer/normalizer.js b/app/javascript/mastodon/actions/importer/normalizer.js index b250ee0765..5e7e78e698 100644 --- a/app/javascript/mastodon/actions/importer/normalizer.js +++ b/app/javascript/mastodon/actions/importer/normalizer.js @@ -22,7 +22,7 @@ export function normalizeAccount(account) { if (account.fields) { account.fields = account.fields.map(pair => ({ ...pair, - name_emojified: emojify(escapeTextContentForBrowser(pair.name)), + name_emojified: emojify(escapeTextContentForBrowser(pair.name), emojiMap), value_emojified: emojify(pair.value, emojiMap), value_plain: unescapeHTML(pair.value), })); From bd87e6667975bc3b5bfaf3e1cdff97e041ed4c98 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sun, 21 Jul 2019 04:08:00 +0200 Subject: [PATCH 03/24] Remove WebSub subscriptions (#11303) --- app/models/concerns/account_associations.rb | 3 - app/models/subscription.rb | 62 ----------------- app/policies/subscription_policy.rb | 7 -- app/services/suspend_account_service.rb | 1 - .../subscriptions_cleanup_scheduler.rb | 4 +- config/sidekiq.yml | 3 - .../20190715031050_drop_subscriptions.rb | 11 +++ db/schema.rb | 14 ---- spec/fabricators/subscription_fabricator.rb | 7 -- spec/models/subscription_spec.rb | 67 ------------------- spec/policies/subscription_policy_spec.rb | 24 ------- .../batched_remove_status_service_spec.rb | 3 - spec/services/remove_status_service_spec.rb | 3 - spec/services/suspend_account_service_spec.rb | 8 +-- 14 files changed, 14 insertions(+), 203 deletions(-) delete mode 100644 app/models/subscription.rb delete mode 100644 app/policies/subscription_policy.rb create mode 100644 db/post_migrate/20190715031050_drop_subscriptions.rb delete mode 100644 spec/fabricators/subscription_fabricator.rb delete mode 100644 spec/models/subscription_spec.rb delete mode 100644 spec/policies/subscription_policy_spec.rb diff --git a/app/models/concerns/account_associations.rb b/app/models/concerns/account_associations.rb index 0921e32522..1db7771c72 100644 --- a/app/models/concerns/account_associations.rb +++ b/app/models/concerns/account_associations.rb @@ -30,9 +30,6 @@ module AccountAssociations has_many :media_attachments, dependent: :destroy has_many :polls, dependent: :destroy - # PuSH subscriptions - has_many :subscriptions, dependent: :destroy - # Report relationships has_many :reports, dependent: :destroy, inverse_of: :account has_many :targeted_reports, class_name: 'Report', foreign_key: :target_account_id, dependent: :destroy, inverse_of: :target_account diff --git a/app/models/subscription.rb b/app/models/subscription.rb deleted file mode 100644 index 79b81828da..0000000000 --- a/app/models/subscription.rb +++ /dev/null @@ -1,62 +0,0 @@ -# frozen_string_literal: true -# == Schema Information -# -# Table name: subscriptions -# -# id :bigint(8) not null, primary key -# callback_url :string default(""), not null -# secret :string -# expires_at :datetime -# confirmed :boolean default(FALSE), not null -# created_at :datetime not null -# updated_at :datetime not null -# last_successful_delivery_at :datetime -# domain :string -# account_id :bigint(8) not null -# - -class Subscription < ApplicationRecord - MIN_EXPIRATION = 1.day.to_i - MAX_EXPIRATION = 30.days.to_i - - belongs_to :account - - validates :callback_url, presence: true - validates :callback_url, uniqueness: { scope: :account_id } - - scope :confirmed, -> { where(confirmed: true) } - scope :future_expiration, -> { where(arel_table[:expires_at].gt(Time.now.utc)) } - scope :expired, -> { where(arel_table[:expires_at].lt(Time.now.utc)) } - scope :active, -> { confirmed.future_expiration } - - def lease_seconds=(value) - self.expires_at = future_expiration(value) - end - - def lease_seconds - (expires_at - Time.now.utc).to_i - end - - def expired? - Time.now.utc > expires_at - end - - before_validation :set_min_expiration - - private - - def future_expiration(value) - Time.now.utc + future_offset(value).seconds - end - - def future_offset(seconds) - [ - [MIN_EXPIRATION, seconds.to_i].max, - MAX_EXPIRATION, - ].min - end - - def set_min_expiration - self.lease_seconds = 0 unless expires_at - end -end diff --git a/app/policies/subscription_policy.rb b/app/policies/subscription_policy.rb deleted file mode 100644 index ac9a8a6c44..0000000000 --- a/app/policies/subscription_policy.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -class SubscriptionPolicy < ApplicationPolicy - def index? - admin? - end -end diff --git a/app/services/suspend_account_service.rb b/app/services/suspend_account_service.rb index 0ebe0b562d..00cffcdfc8 100644 --- a/app/services/suspend_account_service.rb +++ b/app/services/suspend_account_service.rb @@ -24,7 +24,6 @@ class SuspendAccountService < BaseService report_notes scheduled_statuses status_pins - subscriptions ).freeze ASSOCIATIONS_ON_DESTROY = %w( diff --git a/app/workers/scheduler/subscriptions_cleanup_scheduler.rb b/app/workers/scheduler/subscriptions_cleanup_scheduler.rb index 5fba120f6a..75fe681a9c 100644 --- a/app/workers/scheduler/subscriptions_cleanup_scheduler.rb +++ b/app/workers/scheduler/subscriptions_cleanup_scheduler.rb @@ -5,7 +5,5 @@ class Scheduler::SubscriptionsCleanupScheduler sidekiq_options unique: :until_executed, retry: 0 - def perform - Subscription.expired.in_batches.delete_all - end + def perform; end end diff --git a/config/sidekiq.yml b/config/sidekiq.yml index 5c652792c6..7f41b6607b 100644 --- a/config/sidekiq.yml +++ b/config/sidekiq.yml @@ -21,9 +21,6 @@ user_cleanup_scheduler: cron: '<%= Random.rand(0..59) %> <%= Random.rand(4..6) %> * * *' class: Scheduler::UserCleanupScheduler - subscriptions_cleanup_scheduler: - cron: '<%= Random.rand(0..59) %> <%= Random.rand(1..3) %> * * 0' - class: Scheduler::SubscriptionsCleanupScheduler ip_cleanup_scheduler: cron: '<%= Random.rand(0..59) %> <%= Random.rand(3..5) %> * * *' class: Scheduler::IpCleanupScheduler diff --git a/db/post_migrate/20190715031050_drop_subscriptions.rb b/db/post_migrate/20190715031050_drop_subscriptions.rb new file mode 100644 index 0000000000..3719afe4a0 --- /dev/null +++ b/db/post_migrate/20190715031050_drop_subscriptions.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class DropSubscriptions < ActiveRecord::Migration[5.2] + def up + drop_table :subscriptions + end + + def down + raise ActiveRecord::IrreversibleMigration + end +end diff --git a/db/schema.rb b/db/schema.rb index a6a14827b9..6319dd932d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -648,19 +648,6 @@ ActiveRecord::Schema.define(version: 2019_07_15_164535) do t.index ["tag_id", "status_id"], name: "index_statuses_tags_on_tag_id_and_status_id", unique: true end - create_table "subscriptions", force: :cascade do |t| - t.string "callback_url", default: "", null: false - t.string "secret" - t.datetime "expires_at" - t.boolean "confirmed", default: false, null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.datetime "last_successful_delivery_at" - t.string "domain" - t.bigint "account_id", null: false - t.index ["account_id", "callback_url"], name: "index_subscriptions_on_account_id_and_callback_url", unique: true - end - create_table "tags", force: :cascade do |t| t.string "name", default: "", null: false t.datetime "created_at", null: false @@ -821,7 +808,6 @@ ActiveRecord::Schema.define(version: 2019_07_15_164535) do add_foreign_key "statuses", "statuses", column: "reblog_of_id", on_delete: :cascade add_foreign_key "statuses_tags", "statuses", on_delete: :cascade add_foreign_key "statuses_tags", "tags", name: "fk_3081861e21", on_delete: :cascade - add_foreign_key "subscriptions", "accounts", name: "fk_9847d1cbb5", on_delete: :cascade add_foreign_key "tombstones", "accounts", on_delete: :cascade add_foreign_key "user_invite_requests", "users", on_delete: :cascade add_foreign_key "users", "accounts", name: "fk_50500f500d", on_delete: :cascade diff --git a/spec/fabricators/subscription_fabricator.rb b/spec/fabricators/subscription_fabricator.rb deleted file mode 100644 index 347dab5dfd..0000000000 --- a/spec/fabricators/subscription_fabricator.rb +++ /dev/null @@ -1,7 +0,0 @@ -Fabricator(:subscription) do - account - callback_url "http://example.com/callback" - secret "foobar" - expires_at "2016-11-28 11:30:07" - confirmed false -end diff --git a/spec/models/subscription_spec.rb b/spec/models/subscription_spec.rb deleted file mode 100644 index b83979d137..0000000000 --- a/spec/models/subscription_spec.rb +++ /dev/null @@ -1,67 +0,0 @@ -require 'rails_helper' - -RSpec.describe Subscription, type: :model do - let(:alice) { Fabricate(:account, username: 'alice') } - - subject { Fabricate(:subscription, account: alice) } - - describe '#expired?' do - it 'return true when expires_at is past' do - subject.expires_at = 2.days.ago - expect(subject.expired?).to be true - end - - it 'return false when expires_at is future' do - subject.expires_at = 2.days.from_now - expect(subject.expired?).to be false - end - end - - describe 'lease_seconds' do - it 'returns the time remaining until expiration' do - datetime = 1.day.from_now - subscription = Subscription.new(expires_at: datetime) - travel_to(datetime - 12.hours) do - expect(subscription.lease_seconds).to eq(12.hours) - end - end - end - - describe 'lease_seconds=' do - it 'sets expires_at to min expiration when small value is provided' do - subscription = Subscription.new - datetime = 1.day.from_now - too_low = Subscription::MIN_EXPIRATION - 1000 - travel_to(datetime) do - subscription.lease_seconds = too_low - end - - expected = datetime + Subscription::MIN_EXPIRATION.seconds - expect(subscription.expires_at).to be_within(1.0).of(expected) - end - - it 'sets expires_at to value when valid value is provided' do - subscription = Subscription.new - datetime = 1.day.from_now - valid = Subscription::MIN_EXPIRATION + 1000 - travel_to(datetime) do - subscription.lease_seconds = valid - end - - expected = datetime + valid.seconds - expect(subscription.expires_at).to be_within(1.0).of(expected) - end - - it 'sets expires_at to max expiration when large value is provided' do - subscription = Subscription.new - datetime = 1.day.from_now - too_high = Subscription::MAX_EXPIRATION + 1000 - travel_to(datetime) do - subscription.lease_seconds = too_high - end - - expected = datetime + Subscription::MAX_EXPIRATION.seconds - expect(subscription.expires_at).to be_within(1.0).of(expected) - end - end -end diff --git a/spec/policies/subscription_policy_spec.rb b/spec/policies/subscription_policy_spec.rb deleted file mode 100644 index 21d60c15fc..0000000000 --- a/spec/policies/subscription_policy_spec.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' -require 'pundit/rspec' - -RSpec.describe SubscriptionPolicy do - let(:subject) { described_class } - let(:admin) { Fabricate(:user, admin: true).account } - let(:john) { Fabricate(:user).account } - - permissions :index? do - context 'admin?' do - it 'permits' do - expect(subject).to permit(admin, Subscription) - end - end - - context '!admin?' do - it 'denies' do - expect(subject).to_not permit(john, Subscription) - end - end - end -end diff --git a/spec/services/batched_remove_status_service_spec.rb b/spec/services/batched_remove_status_service_spec.rb index d52e7f4841..f84256f187 100644 --- a/spec/services/batched_remove_status_service_spec.rb +++ b/spec/services/batched_remove_status_service_spec.rb @@ -14,11 +14,8 @@ RSpec.describe BatchedRemoveStatusService, type: :service do before do allow(Redis.current).to receive_messages(publish: nil) - stub_request(:post, 'http://example.com/push').to_return(status: 200, body: '', headers: {}) - stub_request(:post, 'http://example.com/salmon').to_return(status: 200, body: '', headers: {}) stub_request(:post, 'http://example.com/inbox').to_return(status: 200) - Fabricate(:subscription, account: alice, callback_url: 'http://example.com/push', confirmed: true, expires_at: 30.days.from_now) jeff.user.update(current_sign_in_at: Time.zone.now) jeff.follow!(alice) hank.follow!(alice) diff --git a/spec/services/remove_status_service_spec.rb b/spec/services/remove_status_service_spec.rb index 48191d47c4..06676ec45e 100644 --- a/spec/services/remove_status_service_spec.rb +++ b/spec/services/remove_status_service_spec.rb @@ -10,12 +10,9 @@ RSpec.describe RemoveStatusService, type: :service do let!(:bill) { Fabricate(:account, username: 'bill', protocol: :activitypub, domain: 'example2.com', inbox_url: 'http://example2.com/inbox') } before do - stub_request(:post, 'http://example.com/push').to_return(status: 200, body: '', headers: {}) - stub_request(:post, 'http://example.com/salmon').to_return(status: 200, body: '', headers: {}) stub_request(:post, 'http://example.com/inbox').to_return(status: 200) stub_request(:post, 'http://example2.com/inbox').to_return(status: 200) - Fabricate(:subscription, account: alice, callback_url: 'http://example.com/push', confirmed: true, expires_at: 30.days.from_now) jeff.follow!(alice) hank.follow!(alice) diff --git a/spec/services/suspend_account_service_spec.rb b/spec/services/suspend_account_service_spec.rb index 896ac17a3b..eebbbc12ad 100644 --- a/spec/services/suspend_account_service_spec.rb +++ b/spec/services/suspend_account_service_spec.rb @@ -18,7 +18,6 @@ RSpec.describe SuspendAccountService, type: :service do let!(:favourite) { Fabricate(:favourite, account: account) } let!(:active_relationship) { Fabricate(:follow, account: account) } let!(:passive_relationship) { Fabricate(:follow, target_account: account) } - let!(:subscription) { Fabricate(:subscription, account: account) } let!(:remote_alice) { Fabricate(:account, inbox_url: 'https://alice.com/inbox', protocol: :activitypub) } let!(:remote_bob) { Fabricate(:account, inbox_url: 'https://bob.com/inbox', protocol: :activitypub) } @@ -31,9 +30,8 @@ RSpec.describe SuspendAccountService, type: :service do account.favourites, account.active_relationships, account.passive_relationships, - account.subscriptions ].map(&:count) - }.from([1, 1, 1, 1, 1, 1, 1]).to([0, 0, 0, 0, 0, 0, 0]) + }.from([1, 1, 1, 1, 1, 1]).to([0, 0, 0, 0, 0, 0]) end it 'sends a delete actor activity to all known inboxes' do @@ -62,7 +60,6 @@ RSpec.describe SuspendAccountService, type: :service do let!(:favourite) { Fabricate(:favourite, account: remote_bob) } let!(:active_relationship) { Fabricate(:follow, account: remote_bob, target_account: account) } let!(:passive_relationship) { Fabricate(:follow, target_account: remote_bob) } - let!(:subscription) { Fabricate(:subscription, account: remote_bob) } it 'deletes associated records' do is_expected.to change { @@ -73,9 +70,8 @@ RSpec.describe SuspendAccountService, type: :service do remote_bob.favourites, remote_bob.active_relationships, remote_bob.passive_relationships, - remote_bob.subscriptions ].map(&:count) - }.from([1, 1, 1, 1, 1, 1, 1]).to([0, 0, 0, 0, 0, 0, 0]) + }.from([1, 1, 1, 1, 1, 1]).to([0, 0, 0, 0, 0, 0]) end it 'sends a reject follow to follwer inboxes' do From bd1545de5ee9655130e5357bb9cb6449520a6292 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sun, 21 Jul 2019 18:08:02 +0200 Subject: [PATCH 04/24] Change locale detection to run once per session (#8657) Fix #6462 --- app/controllers/application_controller.rb | 6 +----- app/controllers/concerns/localized.rb | 13 ++++++++----- config/application.rb | 3 +++ spec/controllers/concerns/localized_spec.rb | 16 +++++----------- 4 files changed, 17 insertions(+), 21 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 51e9764d49..5863fe1681 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -138,11 +138,7 @@ class ApplicationController < ActionController::Base def respond_with_error(code) respond_to do |format| format.any { head code } - - format.html do - set_locale - render "errors/#{code}", layout: 'error', status: code - end + format.html { render "errors/#{code}", layout: 'error', status: code } end end diff --git a/app/controllers/concerns/localized.rb b/app/controllers/concerns/localized.rb index 145549bcd2..b43859d9d6 100644 --- a/app/controllers/concerns/localized.rb +++ b/app/controllers/concerns/localized.rb @@ -4,16 +4,19 @@ module Localized extend ActiveSupport::Concern included do - before_action :set_locale + around_action :set_locale end private def set_locale - I18n.locale = default_locale - I18n.locale = current_user.locale if user_signed_in? - rescue I18n::InvalidLocale - I18n.locale = default_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 end def default_locale diff --git a/config/application.rb b/config/application.rb index 4534ede497..f49deffbb6 100644 --- a/config/application.rb +++ b/config/application.rb @@ -114,6 +114,9 @@ module Mastodon Doorkeeper::AuthorizationsController.layout 'modal' Doorkeeper::AuthorizedApplicationsController.layout 'admin' Doorkeeper::Application.send :include, ApplicationExtension + Devise::FailureApp.send :include, AbstractController::Callbacks + Devise::FailureApp.send :include, HttpAcceptLanguage::EasyAccess + Devise::FailureApp.send :include, Localized end end end diff --git a/spec/controllers/concerns/localized_spec.rb b/spec/controllers/concerns/localized_spec.rb index 76c3de1183..7635d10e19 100644 --- a/spec/controllers/concerns/localized_spec.rb +++ b/spec/controllers/concerns/localized_spec.rb @@ -7,16 +7,10 @@ describe ApplicationController, type: :controller do include Localized def success - head 200 + render plain: I18n.locale, status: 200 end end - around do |example| - current_locale = I18n.locale - example.run - I18n.locale = current_locale - end - before do routes.draw { get 'success' => 'anonymous#success' } end @@ -25,19 +19,19 @@ describe ApplicationController, type: :controller do it 'sets available and preferred language' do request.headers['Accept-Language'] = 'ca-ES, fa' get 'success' - expect(I18n.locale).to eq :fa + expect(response.body).to eq 'fa' end it 'sets available and compatible language if none of available languages are preferred' do request.headers['Accept-Language'] = 'fa-IR' get 'success' - expect(I18n.locale).to eq :fa + expect(response.body).to eq 'fa' end it 'sets default locale if none of available languages are compatible' do request.headers['Accept-Language'] = '' get 'success' - expect(I18n.locale).to eq :en + expect(response.body).to eq 'en' end end @@ -48,7 +42,7 @@ describe ApplicationController, type: :controller do sign_in(user) get 'success' - expect(I18n.locale).to eq :ca + expect(response.body).to eq 'ca' end end From 043d52f785d8f3d0fa31cde8f5e4c1991888e887 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sun, 21 Jul 2019 18:10:07 +0200 Subject: [PATCH 05/24] Fix `alerts` booleans not being typecast correctly in push subscription (#11343) * Fix `alerts` booleans not being typecast correctly in push subscription Fix #10789 * Fix typo --- app/serializers/rest/web_push_subscription_serializer.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/serializers/rest/web_push_subscription_serializer.rb b/app/serializers/rest/web_push_subscription_serializer.rb index 7fd952a567..194cc0a8c7 100644 --- a/app/serializers/rest/web_push_subscription_serializer.rb +++ b/app/serializers/rest/web_push_subscription_serializer.rb @@ -4,7 +4,7 @@ class REST::WebPushSubscriptionSerializer < ActiveModel::Serializer attributes :id, :endpoint, :alerts, :server_key def alerts - object.data&.dig('alerts') || {} + (object.data&.dig('alerts') || {}).each_with_object({}) { |(k, v), h| h[k] = ActiveModel::Type::Boolean.new.cast(v) } end def server_key From 7de8c51873b51d8450f7a6597a43d454964d0407 Mon Sep 17 00:00:00 2001 From: ThibG Date: Sun, 21 Jul 2019 18:10:40 +0200 Subject: [PATCH 06/24] Play animated custom emoji on hover (#11348) * Play animated custom emoji on hover in status * Play animated custom emoji on hover in display names * Play animated custom emoji on hover in bios/bio fields * Add support for animation on hover on public pages emojis too * Fix tests * Code style cleanup --- .../mastodon/components/display_name.js | 44 ++++++++++++++++++- .../mastodon/components/status_content.js | 32 ++++++++++++++ .../features/account/components/header.js | 43 +++++++++++++++++- .../mastodon/features/emoji/emoji.js | 2 +- app/javascript/packs/public.js | 9 ++++ app/lib/formatter.rb | 15 ++++--- spec/lib/formatter_spec.rb | 26 +++++------ 7 files changed, 149 insertions(+), 22 deletions(-) diff --git a/app/javascript/mastodon/components/display_name.js b/app/javascript/mastodon/components/display_name.js index 6b9dd6f81b..70ef82789e 100644 --- a/app/javascript/mastodon/components/display_name.js +++ b/app/javascript/mastodon/components/display_name.js @@ -1,6 +1,7 @@ import React from 'react'; import ImmutablePropTypes from 'react-immutable-proptypes'; import PropTypes from 'prop-types'; +import { autoPlayGif } from 'mastodon/initial_state'; export default class DisplayName extends React.PureComponent { @@ -10,6 +11,47 @@ export default class DisplayName extends React.PureComponent { localDomain: PropTypes.string, }; + _updateEmojis () { + const node = this.node; + + if (!node || autoPlayGif) { + return; + } + + const emojis = node.querySelectorAll('.custom-emoji'); + + for (var i = 0; i < emojis.length; i++) { + let emoji = emojis[i]; + if (emoji.classList.contains('status-emoji')) { + continue; + } + emoji.classList.add('status-emoji'); + + emoji.addEventListener('mouseenter', this.handleEmojiMouseEnter, false); + emoji.addEventListener('mouseleave', this.handleEmojiMouseLeave, false); + } + } + + componentDidMount () { + this._updateEmojis(); + } + + componentDidUpdate () { + this._updateEmojis(); + } + + handleEmojiMouseEnter = ({ target }) => { + target.src = target.getAttribute('data-original'); + } + + handleEmojiMouseLeave = ({ target }) => { + target.src = target.getAttribute('data-static'); + } + + setRef = (c) => { + this.node = c; + } + render () { const { others, localDomain } = this.props; @@ -39,7 +81,7 @@ export default class DisplayName extends React.PureComponent { } return ( - + {displayName} {suffix} ); diff --git a/app/javascript/mastodon/components/status_content.js b/app/javascript/mastodon/components/status_content.js index 06f5b4aad7..8a05415afd 100644 --- a/app/javascript/mastodon/components/status_content.js +++ b/app/javascript/mastodon/components/status_content.js @@ -7,6 +7,7 @@ import Permalink from './permalink'; import classnames from 'classnames'; import PollContainer from 'mastodon/containers/poll_container'; import Icon from 'mastodon/components/icon'; +import { autoPlayGif } from 'mastodon/initial_state'; const MAX_HEIGHT = 642; // 20px * 32 (+ 2px padding at the top) @@ -71,12 +72,35 @@ export default class StatusContent extends React.PureComponent { } } + _updateStatusEmojis () { + const node = this.node; + + if (!node || autoPlayGif) { + return; + } + + const emojis = node.querySelectorAll('.custom-emoji'); + + for (var i = 0; i < emojis.length; i++) { + let emoji = emojis[i]; + if (emoji.classList.contains('status-emoji')) { + continue; + } + emoji.classList.add('status-emoji'); + + emoji.addEventListener('mouseenter', this.handleEmojiMouseEnter, false); + emoji.addEventListener('mouseleave', this.handleEmojiMouseLeave, false); + } + } + componentDidMount () { this._updateStatusLinks(); + this._updateStatusEmojis(); } componentDidUpdate () { this._updateStatusLinks(); + this._updateStatusEmojis(); } onMentionClick = (mention, e) => { @@ -95,6 +119,14 @@ export default class StatusContent extends React.PureComponent { } } + handleEmojiMouseEnter = ({ target }) => { + target.src = target.getAttribute('data-original'); + } + + handleEmojiMouseLeave = ({ target }) => { + target.src = target.getAttribute('data-static'); + } + handleMouseDown = (e) => { this.startXY = [e.clientX, e.clientY]; } diff --git a/app/javascript/mastodon/features/account/components/header.js b/app/javascript/mastodon/features/account/components/header.js index e5b60e33e1..cab67c6078 100644 --- a/app/javascript/mastodon/features/account/components/header.js +++ b/app/javascript/mastodon/features/account/components/header.js @@ -79,6 +79,47 @@ class Header extends ImmutablePureComponent { return !location.pathname.match(/\/(followers|following)\/?$/); } + _updateEmojis () { + const node = this.node; + + if (!node || autoPlayGif) { + return; + } + + const emojis = node.querySelectorAll('.custom-emoji'); + + for (var i = 0; i < emojis.length; i++) { + let emoji = emojis[i]; + if (emoji.classList.contains('status-emoji')) { + continue; + } + emoji.classList.add('status-emoji'); + + emoji.addEventListener('mouseenter', this.handleEmojiMouseEnter, false); + emoji.addEventListener('mouseleave', this.handleEmojiMouseLeave, false); + } + } + + componentDidMount () { + this._updateEmojis(); + } + + componentDidUpdate () { + this._updateEmojis(); + } + + handleEmojiMouseEnter = ({ target }) => { + target.src = target.getAttribute('data-original'); + } + + handleEmojiMouseLeave = ({ target }) => { + target.src = target.getAttribute('data-static'); + } + + setRef = (c) => { + this.node = c; + } + render () { const { account, intl, domain, identity_proofs } = this.props; @@ -200,7 +241,7 @@ class Header extends ImmutablePureComponent { const acct = account.get('acct').indexOf('@') === -1 && domain ? `${account.get('acct')}@${domain}` : account.get('acct'); return ( -
+
{info} diff --git a/app/javascript/mastodon/features/emoji/emoji.js b/app/javascript/mastodon/features/emoji/emoji.js index 01b5a66645..359bb7ffd4 100644 --- a/app/javascript/mastodon/features/emoji/emoji.js +++ b/app/javascript/mastodon/features/emoji/emoji.js @@ -29,7 +29,7 @@ const emojify = (str, customEmojis = {}) => { // if you want additional emoji handler, add statements below which set replacement and return true. if (shortname in customEmojis) { const filename = autoPlayGif ? customEmojis[shortname].url : customEmojis[shortname].static_url; - replacement = `${shortname}`; + replacement = `${shortname}`; return true; } return false; diff --git a/app/javascript/packs/public.js b/app/javascript/packs/public.js index 0c60d828e9..b58622a8d8 100644 --- a/app/javascript/packs/public.js +++ b/app/javascript/packs/public.js @@ -44,6 +44,12 @@ function main() { } }; + const getEmojiAnimationHandler = (swapTo) => { + return ({ target }) => { + target.src = target.getAttribute(swapTo); + }; + }; + ready(() => { const locale = document.documentElement.lang; @@ -108,6 +114,9 @@ function main() { if (parallaxComponents.length > 0 ) { new Rellax('.parallax', { speed: -1 }); } + + delegate(document, '.custom-emoji', 'mouseover', getEmojiAnimationHandler('data-original')); + delegate(document, '.custom-emoji', 'mouseout', getEmojiAnimationHandler('data-static')); }); delegate(document, '.webapp-btn', 'click', ({ target, button }) => { diff --git a/app/lib/formatter.rb b/app/lib/formatter.rb index 6c12399637..65059efa06 100644 --- a/app/lib/formatter.rb +++ b/app/lib/formatter.rb @@ -137,11 +137,7 @@ class Formatter def encode_custom_emojis(html, emojis, animate = false) return html if emojis.empty? - emoji_map = if animate - emojis.each_with_object({}) { |e, h| h[e.shortcode] = full_asset_url(e.image.url) } - else - emojis.each_with_object({}) { |e, h| h[e.shortcode] = full_asset_url(e.image.url(:static)) } - end + emoji_map = emojis.each_with_object({}) { |e, h| h[e.shortcode] = [full_asset_url(e.image.url), full_asset_url(e.image.url(:static))] } i = -1 tag_open_index = nil @@ -157,7 +153,14 @@ class Formatter emoji = emoji_map[shortcode] if emoji - replacement = "\":#{encode(shortcode)}:\"" + original_url, static_url = emoji + replacement = begin + if animate + "\":#{encode(shortcode)}:\"" + else + "\":#{encode(shortcode)}:\"" + end + end before_html = shortname_start_index.positive? ? html[0..shortname_start_index - 1] : '' html = before_html + replacement + html[i + 1..-1] i += replacement.size - (shortcode.size + 2) - 1 diff --git a/spec/lib/formatter_spec.rb b/spec/lib/formatter_spec.rb index 96d2fc7e06..b8108a2477 100644 --- a/spec/lib/formatter_spec.rb +++ b/spec/lib/formatter_spec.rb @@ -261,7 +261,7 @@ RSpec.describe Formatter do let(:text) { ':coolcat: Beep boop' } it 'converts the shortcode to an image tag' do - is_expected.to match(/:coolcat::coolcat::coolcat::coolcat: Beep boop
' } it 'converts the shortcode to an image tag' do - is_expected.to match(/

:coolcat::coolcat:Beep :coolcat: boop

' } it 'converts the shortcode to an image tag' do - is_expected.to match(/Beep :coolcat:Beep boop
:coolcat:

' } it 'converts the shortcode to an image tag' do - is_expected.to match(/
:coolcat::coolcat::coolcat::coolcat::coolcat: Beep boop
' } it 'converts shortcode to image tag' do - is_expected.to match(/

:coolcat::coolcat:Beep :coolcat: boop

' } it 'converts shortcode to image tag' do - is_expected.to match(/Beep :coolcat:Beep boop
:coolcat:

' } it 'converts shortcode to image tag' do - is_expected.to match(/
:coolcat::coolcat: Date: Sun, 21 Jul 2019 18:11:09 +0200 Subject: [PATCH 07/24] Fix boost to original audience not working on mobile (#11371) --- app/javascript/mastodon/containers/status_container.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/mastodon/containers/status_container.js b/app/javascript/mastodon/containers/status_container.js index 86324b8468..fa58589a61 100644 --- a/app/javascript/mastodon/containers/status_container.js +++ b/app/javascript/mastodon/containers/status_container.js @@ -77,7 +77,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({ }, onReblog (status, e) { - if (e.shiftKey || !boostModal) { + if ((e && e.shiftKey) || !boostModal) { this.onModalReblog(status); } else { dispatch(openModal('BOOST', { status, onReblog: this.onModalReblog })); From c669bb42baa213dde27d831bb34f0ce14cfb29dc Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sun, 21 Jul 2019 22:32:16 +0200 Subject: [PATCH 08/24] Add (back) rails-level JSON caching (#11333) --- app/controllers/accounts_controller.rb | 2 +- .../activitypub/collections_controller.rb | 2 +- .../api/v1/custom_emojis_controller.rb | 5 +- .../api/v1/instances/activity_controller.rb | 3 +- .../api/v1/instances/peers_controller.rb | 3 +- .../api/v1/instances_controller.rb | 5 +- app/controllers/application_controller.rb | 38 +------------- app/controllers/concerns/cache_concern.rb | 50 +++++++++++++++++++ app/controllers/emojis_controller.rb | 2 +- app/controllers/statuses_controller.rb | 4 +- 10 files changed, 64 insertions(+), 50 deletions(-) create mode 100644 app/controllers/concerns/cache_concern.rb diff --git a/app/controllers/accounts_controller.rb b/app/controllers/accounts_controller.rb index fc913c2ecd..058a00a21b 100644 --- a/app/controllers/accounts_controller.rb +++ b/app/controllers/accounts_controller.rb @@ -41,7 +41,7 @@ class AccountsController < ApplicationController format.json do expires_in 3.minutes, public: !(authorized_fetch_mode? && signed_request_account.present?) - render json: @account, content_type: 'application/activity+json', serializer: ActivityPub::ActorSerializer, adapter: ActivityPub::Adapter, fields: restrict_fields_to + render_with_cache json: @account, content_type: 'application/activity+json', serializer: ActivityPub::ActorSerializer, adapter: ActivityPub::Adapter, fields: restrict_fields_to end end end diff --git a/app/controllers/activitypub/collections_controller.rb b/app/controllers/activitypub/collections_controller.rb index fa925b204e..989fee385d 100644 --- a/app/controllers/activitypub/collections_controller.rb +++ b/app/controllers/activitypub/collections_controller.rb @@ -11,7 +11,7 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController def show expires_in 3.minutes, public: public_fetch_mode? - render json: collection_presenter, content_type: 'application/activity+json', serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter, skip_activities: true + render_with_cache json: collection_presenter, content_type: 'application/activity+json', serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter, skip_activities: true end private diff --git a/app/controllers/api/v1/custom_emojis_controller.rb b/app/controllers/api/v1/custom_emojis_controller.rb index b6877fb3c4..252f667ddf 100644 --- a/app/controllers/api/v1/custom_emojis_controller.rb +++ b/app/controllers/api/v1/custom_emojis_controller.rb @@ -6,8 +6,7 @@ class Api::V1::CustomEmojisController < Api::BaseController skip_before_action :set_cache_headers def index - render_cached_json('api:v1:custom_emojis', expires_in: 1.minute) do - ActiveModelSerializers::SerializableResource.new(CustomEmoji.local.where(disabled: false).includes(:category), each_serializer: REST::CustomEmojiSerializer) - end + expires_in 3.minutes, public: true + render_with_cache(each_serializer: REST::CustomEmojiSerializer) { CustomEmoji.local.where(disabled: false).includes(:category) } end end diff --git a/app/controllers/api/v1/instances/activity_controller.rb b/app/controllers/api/v1/instances/activity_controller.rb index 09edfe365b..d0080c5c2e 100644 --- a/app/controllers/api/v1/instances/activity_controller.rb +++ b/app/controllers/api/v1/instances/activity_controller.rb @@ -7,7 +7,8 @@ class Api::V1::Instances::ActivityController < Api::BaseController respond_to :json def show - render_cached_json('api:v1:instances:activity:show', expires_in: 1.day) { activity } + expires_in 1.day, public: true + render_with_cache json: :activity, expires_in: 1.day end private diff --git a/app/controllers/api/v1/instances/peers_controller.rb b/app/controllers/api/v1/instances/peers_controller.rb index a8891d126b..450e6502f5 100644 --- a/app/controllers/api/v1/instances/peers_controller.rb +++ b/app/controllers/api/v1/instances/peers_controller.rb @@ -7,7 +7,8 @@ class Api::V1::Instances::PeersController < Api::BaseController respond_to :json def index - render_cached_json('api:v1:instances:peers:index', expires_in: 1.day) { Account.remote.domains } + expires_in 1.day, public: true + render_with_cache(expires_in: 1.day) { Account.remote.domains } end private diff --git a/app/controllers/api/v1/instances_controller.rb b/app/controllers/api/v1/instances_controller.rb index 8c83a18014..b68c786153 100644 --- a/app/controllers/api/v1/instances_controller.rb +++ b/app/controllers/api/v1/instances_controller.rb @@ -5,8 +5,7 @@ class Api::V1::InstancesController < Api::BaseController skip_before_action :set_cache_headers def show - render_cached_json('api:v1:instances', expires_in: 5.minutes) do - ActiveModelSerializers::SerializableResource.new({}, serializer: REST::InstanceSerializer) - end + expires_in 3.minutes, public: true + render_with_cache json: {}, serializer: REST::InstanceSerializer end end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 5863fe1681..b8a1faf77e 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -10,6 +10,7 @@ class ApplicationController < ActionController::Base include Localized include UserTrackingConcern include SessionTrackingConcern + include CacheConcern helper_method :current_account helper_method :current_session @@ -115,47 +116,10 @@ class ApplicationController < ActionController::Base current_user.setting_theme end - def cache_collection(raw, klass) - return raw unless klass.respond_to?(:with_includes) - - raw = raw.cache_ids.to_a if raw.is_a?(ActiveRecord::Relation) - cached_keys_with_value = Rails.cache.read_multi(*raw).transform_keys(&:id) - uncached_ids = raw.map(&:id) - cached_keys_with_value.keys - - klass.reload_stale_associations!(cached_keys_with_value.values) if klass.respond_to?(:reload_stale_associations!) - - unless uncached_ids.empty? - uncached = klass.where(id: uncached_ids).with_includes.each_with_object({}) { |item, h| h[item.id] = item } - - uncached.each_value do |item| - Rails.cache.write(item, item) - end - end - - raw.map { |item| cached_keys_with_value[item.id] || uncached[item.id] }.compact - end - def respond_with_error(code) respond_to do |format| format.any { head code } format.html { render "errors/#{code}", layout: 'error', status: code } end end - - def render_cached_json(cache_key, **options) - options[:expires_in] ||= 3.minutes - cache_public = options.key?(:public) ? options.delete(:public) : true - content_type = options.delete(:content_type) || 'application/json' - - data = Rails.cache.fetch(cache_key, { raw: true }.merge(options)) do - yield.to_json - end - - expires_in options[:expires_in], public: cache_public - render json: data, content_type: content_type - end - - def set_cache_headers - response.headers['Vary'] = public_fetch_mode? ? 'Accept' : 'Accept, Signature' - end end diff --git a/app/controllers/concerns/cache_concern.rb b/app/controllers/concerns/cache_concern.rb new file mode 100644 index 0000000000..c7d25ae00c --- /dev/null +++ b/app/controllers/concerns/cache_concern.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +module CacheConcern + extend ActiveSupport::Concern + + def render_with_cache(**options) + raise ArgumentError, 'only JSON render calls are supported' unless options.key?(:json) || block_given? + + key = options.delete(:key) || [[params[:controller], params[:action]].join('/'), options[:json].respond_to?(:cache_key) ? options[:json].cache_key : nil, options[:fields].nil? ? nil : options[:fields].join(',')].compact.join(':') + expires_in = options.delete(:expires_in) || 3.minutes + body = Rails.cache.read(key, raw: true) + + if body + render(options.except(:json, :serializer, :each_serializer, :adapter, :fields).merge(json: body)) + else + if block_given? + options[:json] = yield + elsif options[:json].is_a?(Symbol) + options[:json] = send(options[:json]) + end + + render(options) + Rails.cache.write(key, response.body, expires_in: expires_in, raw: true) + end + end + + def set_cache_headers + response.headers['Vary'] = public_fetch_mode? ? 'Accept' : 'Accept, Signature' + end + + def cache_collection(raw, klass) + return raw unless klass.respond_to?(:with_includes) + + raw = raw.cache_ids.to_a if raw.is_a?(ActiveRecord::Relation) + cached_keys_with_value = Rails.cache.read_multi(*raw).transform_keys(&:id) + uncached_ids = raw.map(&:id) - cached_keys_with_value.keys + + klass.reload_stale_associations!(cached_keys_with_value.values) if klass.respond_to?(:reload_stale_associations!) + + unless uncached_ids.empty? + uncached = klass.where(id: uncached_ids).with_includes.each_with_object({}) { |item, h| h[item.id] = item } + + uncached.each_value do |item| + Rails.cache.write(item, item) + end + end + + raw.map { |item| cached_keys_with_value[item.id] || uncached[item.id] }.compact + end +end diff --git a/app/controllers/emojis_controller.rb b/app/controllers/emojis_controller.rb index fe4c19cada..41f1e1c5ca 100644 --- a/app/controllers/emojis_controller.rb +++ b/app/controllers/emojis_controller.rb @@ -8,7 +8,7 @@ class EmojisController < ApplicationController respond_to do |format| format.json do expires_in 3.minutes, public: true - render json: @emoji, content_type: 'application/activity+json', serializer: ActivityPub::EmojiSerializer, adapter: ActivityPub::Adapter + render_with_cache json: @emoji, content_type: 'application/activity+json', serializer: ActivityPub::EmojiSerializer, adapter: ActivityPub::Adapter end end end diff --git a/app/controllers/statuses_controller.rb b/app/controllers/statuses_controller.rb index 22e7519f9a..0693125ab1 100644 --- a/app/controllers/statuses_controller.rb +++ b/app/controllers/statuses_controller.rb @@ -32,14 +32,14 @@ class StatusesController < ApplicationController format.json do expires_in 3.minutes, public: @status.distributable? && public_fetch_mode? - render json: @status, content_type: 'application/activity+json', serializer: ActivityPub::NoteSerializer, adapter: ActivityPub::Adapter + render_with_cache json: @status, content_type: 'application/activity+json', serializer: ActivityPub::NoteSerializer, adapter: ActivityPub::Adapter end end end def activity expires_in 3.minutes, public: @status.distributable? && public_fetch_mode? - render json: @status, content_type: 'application/activity+json', serializer: ActivityPub::ActivitySerializer, adapter: ActivityPub::Adapter + render_with_cache json: @status, content_type: 'application/activity+json', serializer: ActivityPub::ActivitySerializer, adapter: ActivityPub::Adapter end def embed From 00fc17b2edceb9dba965d6421f3611e6c31defbc Mon Sep 17 00:00:00 2001 From: Daigo 3 Dango Date: Sun, 21 Jul 2019 18:16:30 -1000 Subject: [PATCH 09/24] Bind servers to 0.0.0.0 in Procfile (#11378) * Bind to 0.0.0.0 * Make Procfile common to main and streaming apps --- Procfile | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Procfile b/Procfile index b18e4b6be5..d48b0373b0 100644 --- a/Procfile +++ b/Procfile @@ -1,2 +1,14 @@ -web: bundle exec puma -C config/puma.rb +web: if [ "$RUN_STREAMING" != "true" ]; then BIND=0.0.0.0 bundle exec puma -C config/puma.rb; else BIND=0.0.0.0 node ./streaming; fi worker: bundle exec sidekiq + +# For the streaming API, you need a separate app that shares Postgres and Redis: +# +# heroku create +# heroku buildpacks:add heroku/nodejs +# heroku config:set RUN_STREAMING=true +# heroku addons:attach ::DATABASE +# heroku addons:attach ::REDIS +# +# and let the main app use the separate app: +# +# heroku config:set STREAMING_API_BASE_URL=wss://.herokuapp.com -a From 28a60cb04fcb1f42a1bc868ef7bf20cf3ab46d85 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2019 16:27:16 +0900 Subject: [PATCH 10/24] Bump @clusterws/cws from 0.14.0 to 0.15.0 (#11379) Bumps @clusterws/cws from 0.14.0 to 0.15.0. Signed-off-by: dependabot-preview[bot] --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 00ea65a125..a8e51bfef7 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,7 @@ "@babel/preset-env": "^7.4.5", "@babel/preset-react": "^7.0.0", "@babel/runtime": "^7.5.4", - "@clusterws/cws": "^0.14.0", + "@clusterws/cws": "^0.15.0", "array-includes": "^3.0.3", "autoprefixer": "^9.6.0", "axios": "^0.19.0", diff --git a/yarn.lock b/yarn.lock index 14e4b276b2..6653ad92ae 100644 --- a/yarn.lock +++ b/yarn.lock @@ -865,10 +865,10 @@ lodash "^4.17.11" to-fast-properties "^2.0.0" -"@clusterws/cws@^0.14.0": - version "0.14.0" - resolved "https://registry.yarnpkg.com/@clusterws/cws/-/cws-0.14.0.tgz#242824b6884454001340222a836db6f6c5e62bfb" - integrity sha512-knZj3KZNHIAGsX7TUc/0Q5gcx2bKMMcTPsAOZomLKdK5a4o/umKFlttWRH84Yr1nVlQy+UMO23qfDR8gRZ/4cw== +"@clusterws/cws@^0.15.0": + version "0.15.0" + resolved "https://registry.yarnpkg.com/@clusterws/cws/-/cws-0.15.0.tgz#1d585927252d1cd92e676c952fa6d69df14a0d07" + integrity sha512-41QpCngw86n41hIdU5Nx2QJmmxZuA9FPtDkjONrYpk27L7HjL1kj6J5oWEjbr14yXLfigZit3VY+oACDCGbiHw== "@cnakazawa/watch@^1.0.3": version "1.0.3" From 44b04358b39673bbfcf9d2264584880dfb44115c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2019 16:27:44 +0900 Subject: [PATCH 11/24] Bump intl-relativeformat from 6.4.2 to 6.4.3 (#11380) Bumps [intl-relativeformat](https://github.com/formatjs/formatjs) from 6.4.2 to 6.4.3. - [Release notes](https://github.com/formatjs/formatjs/releases) - [Commits](https://github.com/formatjs/formatjs/compare/intl-relativeformat@6.4.2...intl-relativeformat@6.4.3) Signed-off-by: dependabot-preview[bot] --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index a8e51bfef7..9078c257c0 100644 --- a/package.json +++ b/package.json @@ -103,7 +103,7 @@ "intersection-observer": "^0.7.0", "intl": "^1.2.5", "intl-messageformat": "^2.2.0", - "intl-relativeformat": "^6.4.2", + "intl-relativeformat": "^6.4.3", "is-nan": "^1.2.1", "js-yaml": "^3.13.1", "lodash": "^4.17.14", diff --git a/yarn.lock b/yarn.lock index 6653ad92ae..9568055ed0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5085,10 +5085,10 @@ intl-relativeformat@^2.1.0: dependencies: intl-messageformat "^2.0.0" -intl-relativeformat@^6.4.2: - version "6.4.2" - resolved "https://registry.yarnpkg.com/intl-relativeformat/-/intl-relativeformat-6.4.2.tgz#431f9818449f5b48c209610ff1428d0c663c667f" - integrity sha512-yaOimRUQEn1wOfVGk43H+EVCrxQ5WFEvtYBx4Ffa6QpEHIi6UOuvshx6RltuqIF5UM8xdF4SkzFHXXOnYXlgBA== +intl-relativeformat@^6.4.3: + version "6.4.3" + resolved "https://registry.yarnpkg.com/intl-relativeformat/-/intl-relativeformat-6.4.3.tgz#cb5559e1e257cc2e763583502012a354bb777efe" + integrity sha512-VxZXZfhuX/zBVfxzE/J6kPUpsyWKYjqtZ3jVGZwr6wzK5BOLVpe1vSlwCQX56w5UjlpL63fS8Nxq0kgTyf1gJA== intl@^1.2.5: version "1.2.5" From e2b3437a84809b98c4f859a84ad39158d8ecb61b Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2019 17:36:55 +0900 Subject: [PATCH 12/24] Bump babel-eslint from 10.0.1 to 10.0.2 (#11381) Bumps [babel-eslint](https://github.com/babel/babel-eslint) from 10.0.1 to 10.0.2. - [Release notes](https://github.com/babel/babel-eslint/releases) - [Commits](https://github.com/babel/babel-eslint/compare/v10.0.1...v10.0.2) Signed-off-by: dependabot-preview[bot] --- package.json | 2 +- yarn.lock | 76 +++++++--------------------------------------------- 2 files changed, 10 insertions(+), 68 deletions(-) diff --git a/package.json b/package.json index 9078c257c0..a0cff044a6 100644 --- a/package.json +++ b/package.json @@ -166,7 +166,7 @@ "websocket.js": "^0.1.12" }, "devDependencies": { - "babel-eslint": "^10.0.1", + "babel-eslint": "^10.0.2", "babel-jest": "^24.8.0", "enzyme": "^3.10.0", "enzyme-adapter-react-16": "^1.14.0", diff --git a/yarn.lock b/yarn.lock index 9568055ed0..f37b663780 100644 --- a/yarn.lock +++ b/yarn.lock @@ -60,7 +60,7 @@ source-map "^0.5.0" trim-right "^1.0.1" -"@babel/generator@^7.2.2", "@babel/generator@^7.4.4": +"@babel/generator@^7.4.4": version "7.4.4" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.4.4.tgz#174a215eb843fc392c7edcaabeaa873de6e8f041" integrity sha512-53UOLK6TVNqKxf7RUh8NE851EHRxOOeVXKbK2bivdb+iziMyk03Sr4eaE9OELCbyZAAafAKPDwF2TPUES5QbxQ== @@ -229,7 +229,7 @@ "@babel/template" "^7.1.0" "@babel/types" "^7.0.0" -"@babel/helper-split-export-declaration@^7.0.0", "@babel/helper-split-export-declaration@^7.4.4": +"@babel/helper-split-export-declaration@^7.4.4": version "7.4.4" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz#ff94894a340be78f53f06af038b205c49d993677" integrity sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q== @@ -273,17 +273,7 @@ esutils "^2.0.2" js-tokens "^4.0.0" -"@babel/parser@^7.0.0": - version "7.2.3" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.2.3.tgz#32f5df65744b70888d17872ec106b02434ba1489" - integrity sha512-0LyEcVlfCoFmci8mXx8A5oIkpkOgyo8dRHtxBnK9RRBwxO2+JZPNsqtVEZQ7mJFPxnXF9lfmU24mHOPI0qnlkA== - -"@babel/parser@^7.1.0", "@babel/parser@^7.3.4": - version "7.3.4" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.3.4.tgz#a43357e4bbf4b92a437fb9e465c192848287f27c" - integrity sha512-tXZCqWtlOOP4wgCp6RjRvLmfuhnqTLy9VHwRochJBCP2nDm27JnnuFEnXFASVyQNHk36jD1tAammsCEEqgscIQ== - -"@babel/parser@^7.2.2", "@babel/parser@^7.2.3", "@babel/parser@^7.4.4", "@babel/parser@^7.4.5": +"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.2.2", "@babel/parser@^7.3.4", "@babel/parser@^7.4.4", "@babel/parser@^7.4.5": version "7.4.5" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.4.5.tgz#04af8d5d5a2b044a2a1bffacc1e5e6673544e872" integrity sha512-9mUqkL1FF5T7f0WDFfAoDdiMVPWsdD1gZYzSnaXsxUCUqzuch/8of9G3VUSNiZmMBoRxT3neyVsqeiL/ZPcjew== @@ -793,22 +783,7 @@ "@babel/parser" "^7.4.4" "@babel/types" "^7.4.4" -"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.5": - version "7.2.3" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.2.3.tgz#7ff50cefa9c7c0bd2d81231fdac122f3957748d8" - integrity sha512-Z31oUD/fJvEWVR0lNZtfgvVt512ForCTNKYcJBGbPb1QZfve4WGH8Wsy7+Mev33/45fhP/hwQtvgusNdcCMgSw== - dependencies: - "@babel/code-frame" "^7.0.0" - "@babel/generator" "^7.2.2" - "@babel/helper-function-name" "^7.1.0" - "@babel/helper-split-export-declaration" "^7.0.0" - "@babel/parser" "^7.2.3" - "@babel/types" "^7.2.2" - debug "^4.1.0" - globals "^11.1.0" - lodash "^4.17.10" - -"@babel/traverse@^7.1.0", "@babel/traverse@^7.4.4", "@babel/traverse@^7.4.5": +"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0", "@babel/traverse@^7.1.5", "@babel/traverse@^7.3.4", "@babel/traverse@^7.4.4", "@babel/traverse@^7.4.5": version "7.4.5" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.4.5.tgz#4e92d1728fd2f1897dafdd321efbff92156c3216" integrity sha512-Vc+qjynwkjRmIFGxy0KYoPj4FdVDxLej89kMHFsWScq999uX+pwcX4v9mWRjW0KcAYTPAuVQl2LKP1wEVLsp+A== @@ -823,22 +798,7 @@ globals "^11.1.0" lodash "^4.17.11" -"@babel/traverse@^7.3.4": - version "7.3.4" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.3.4.tgz#1330aab72234f8dea091b08c4f8b9d05c7119e06" - integrity sha512-TvTHKp6471OYEcE/91uWmhR6PrrYywQntCHSaZ8CM8Vmp+pjAusal4nGB2WCCQd0rvI7nOMKn9GnbcvTUz3/ZQ== - dependencies: - "@babel/code-frame" "^7.0.0" - "@babel/generator" "^7.3.4" - "@babel/helper-function-name" "^7.1.0" - "@babel/helper-split-export-declaration" "^7.0.0" - "@babel/parser" "^7.3.4" - "@babel/types" "^7.3.4" - debug "^4.1.0" - globals "^11.1.0" - lodash "^4.17.11" - -"@babel/types@^7.0.0", "@babel/types@^7.2.0", "@babel/types@^7.2.2", "@babel/types@^7.4.4": +"@babel/types@^7.0.0", "@babel/types@^7.0.0-beta.49", "@babel/types@^7.2.0", "@babel/types@^7.2.2", "@babel/types@^7.3.0", "@babel/types@^7.3.4", "@babel/types@^7.4.4": version "7.4.4" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.4.4.tgz#8db9e9a629bb7c29370009b4b779ed93fe57d5f0" integrity sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ== @@ -847,24 +807,6 @@ lodash "^4.17.11" to-fast-properties "^2.0.0" -"@babel/types@^7.0.0-beta.49": - version "7.2.2" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.2.2.tgz#44e10fc24e33af524488b716cdaee5360ea8ed1e" - integrity sha512-fKCuD6UFUMkR541eDWL+2ih/xFZBXPOg/7EQFeTluMDebfqR4jrpaCjLhkWlQS4hT6nRa2PMEgXKbRB5/H2fpg== - dependencies: - esutils "^2.0.2" - lodash "^4.17.10" - to-fast-properties "^2.0.0" - -"@babel/types@^7.3.0", "@babel/types@^7.3.4": - version "7.3.4" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.3.4.tgz#bf482eaeaffb367a28abbf9357a94963235d90ed" - integrity sha512-WEkp8MsLftM7O/ty580wAmZzN1nDmCACc5+jFzUt+GUFNNIi3LdRlueYz0YIlmJhlZx1QYDMZL5vdWCL0fNjFQ== - dependencies: - esutils "^2.0.2" - lodash "^4.17.11" - to-fast-properties "^2.0.0" - "@clusterws/cws@^0.15.0": version "0.15.0" resolved "https://registry.yarnpkg.com/@clusterws/cws/-/cws-0.15.0.tgz#1d585927252d1cd92e676c952fa6d69df14a0d07" @@ -1724,10 +1666,10 @@ axobject-query@^2.0.2: dependencies: ast-types-flow "0.0.7" -babel-eslint@^10.0.1: - version "10.0.1" - resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.0.1.tgz#919681dc099614cd7d31d45c8908695092a1faed" - integrity sha512-z7OT1iNV+TjOwHNLLyJk+HN+YVWX+CLE6fPD2SymJZOZQBs+QIexFjhm4keGTm8MW9xr4EC9Q0PbaLB24V5GoQ== +babel-eslint@^10.0.2: + version "10.0.2" + resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.0.2.tgz#182d5ac204579ff0881684b040560fdcc1558456" + integrity sha512-UdsurWPtgiPgpJ06ryUnuaSXC2s0WoSZnQmEpbAH65XZSdwowgN5MvyP7e88nW07FYXv72erVtpBkxyDVKhH1Q== dependencies: "@babel/code-frame" "^7.0.0" "@babel/parser" "^7.0.0" From fea903f574cd59e6938c775427727337d7f929c3 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2019 17:37:11 +0900 Subject: [PATCH 13/24] Bump eslint from 5.16.0 to 6.1.0 (#11383) Bumps [eslint](https://github.com/eslint/eslint) from 5.16.0 to 6.1.0. - [Release notes](https://github.com/eslint/eslint/releases) - [Changelog](https://github.com/eslint/eslint/blob/master/CHANGELOG.md) - [Commits](https://github.com/eslint/eslint/compare/v5.16.0...v6.1.0) Signed-off-by: dependabot-preview[bot] --- package.json | 2 +- yarn.lock | 120 +++++++++++++++++++++++++++++---------------------- 2 files changed, 69 insertions(+), 53 deletions(-) diff --git a/package.json b/package.json index a0cff044a6..58d5948175 100644 --- a/package.json +++ b/package.json @@ -170,7 +170,7 @@ "babel-jest": "^24.8.0", "enzyme": "^3.10.0", "enzyme-adapter-react-16": "^1.14.0", - "eslint": "^5.16.0", + "eslint": "^6.1.0", "eslint-plugin-import": "~2.18.0", "eslint-plugin-jsx-a11y": "~6.2.3", "eslint-plugin-promise": "~4.2.1", diff --git a/yarn.lock b/yarn.lock index f37b663780..c7dfd4ed3b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1365,10 +1365,10 @@ ajv@^4.7.0: co "^4.6.0" json-stable-stringify "^1.0.1" -ajv@^6.1.0, ajv@^6.5.5, ajv@^6.9.1: - version "6.10.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.0.tgz#90d0d54439da587cd7e843bfb7045f50bd22bdf1" - integrity sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg== +ajv@^6.1.0, ajv@^6.10.0, ajv@^6.5.5, ajv@^6.9.1: + version "6.10.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52" + integrity sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw== dependencies: fast-deep-equal "^2.0.1" fast-json-stable-stringify "^2.0.0" @@ -3647,7 +3647,7 @@ eslint-scope@3.7.1: esrecurse "^4.1.0" estraverse "^4.1.1" -eslint-scope@^4.0.0, eslint-scope@^4.0.3: +eslint-scope@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== @@ -3655,6 +3655,14 @@ eslint-scope@^4.0.0, eslint-scope@^4.0.3: esrecurse "^4.1.0" estraverse "^4.1.1" +eslint-scope@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.0.0.tgz#e87c8887c73e8d1ec84f1ca591645c358bfc8fb9" + integrity sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw== + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + eslint-utils@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.3.1.tgz#9a851ba89ee7c460346f97cf8939c7298827e512" @@ -3704,47 +3712,48 @@ eslint@^2.7.0: text-table "~0.2.0" user-home "^2.0.0" -eslint@^5.16.0: - version "5.16.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.16.0.tgz#a1e3ac1aae4a3fbd8296fcf8f7ab7314cbb6abea" - integrity sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg== +eslint@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.1.0.tgz#06438a4a278b1d84fb107d24eaaa35471986e646" + integrity sha512-QhrbdRD7ofuV09IuE2ySWBz0FyXCq0rriLTZXZqaWSI79CVtHVRdkFuFTViiqzZhkCgfOh9USpriuGN2gIpZDQ== dependencies: "@babel/code-frame" "^7.0.0" - ajv "^6.9.1" + ajv "^6.10.0" chalk "^2.1.0" cross-spawn "^6.0.5" debug "^4.0.1" doctrine "^3.0.0" - eslint-scope "^4.0.3" + eslint-scope "^5.0.0" eslint-utils "^1.3.1" eslint-visitor-keys "^1.0.0" - espree "^5.0.1" + espree "^6.0.0" esquery "^1.0.1" esutils "^2.0.2" file-entry-cache "^5.0.1" functional-red-black-tree "^1.0.1" - glob "^7.1.2" + glob-parent "^5.0.0" globals "^11.7.0" ignore "^4.0.6" import-fresh "^3.0.0" imurmurhash "^0.1.4" - inquirer "^6.2.2" - js-yaml "^3.13.0" + inquirer "^6.4.1" + is-glob "^4.0.0" + js-yaml "^3.13.1" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.3.0" - lodash "^4.17.11" + lodash "^4.17.14" minimatch "^3.0.4" mkdirp "^0.5.1" natural-compare "^1.4.0" optionator "^0.8.2" - path-is-inside "^1.0.2" progress "^2.0.0" regexpp "^2.0.1" - semver "^5.5.1" - strip-ansi "^4.0.0" - strip-json-comments "^2.0.1" + semver "^6.1.2" + strip-ansi "^5.2.0" + strip-json-comments "^3.0.1" table "^5.2.3" text-table "^0.2.0" + v8-compile-cache "^2.0.3" espree@^3.1.6: version "3.5.4" @@ -3754,10 +3763,10 @@ espree@^3.1.6: acorn "^5.5.0" acorn-jsx "^3.0.0" -espree@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-5.0.1.tgz#5d6526fa4fc7f0788a5cf75b15f30323e2f81f7a" - integrity sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A== +espree@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-6.0.0.tgz#716fc1f5a245ef5b9a7fdb1d7b0d3f02322e75f6" + integrity sha512-lJvCS6YbCn3ImT3yKkPe0+tJ+mH6ljhGNjHQH9mRtiO6gjhVAOhVXW1yjnwqGwTkK3bGbye+hb00nFNmu0l/1Q== dependencies: acorn "^6.0.7" acorn-jsx "^5.0.0" @@ -4415,6 +4424,13 @@ glob-parent@^3.1.0: is-glob "^3.1.0" path-dirname "^1.0.0" +glob-parent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.0.0.tgz#1dc99f0f39b006d3e92c2c284068382f0c20e954" + integrity sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg== + dependencies: + is-glob "^4.0.1" + glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@~7.1.1: version "7.1.4" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" @@ -4961,10 +4977,10 @@ inquirer@^0.12.0: strip-ansi "^3.0.0" through "^2.3.6" -inquirer@^6.2.2: - version "6.4.1" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.4.1.tgz#7bd9e5ab0567cd23b41b0180b68e0cfa82fc3c0b" - integrity sha512-/Jw+qPZx4EDYsaT6uz7F4GJRNFMRdKNeUZw3ZnKV8lyuUgz/YWRCSUAJMZSVhSq4Ec0R2oYnyi6b3d4JXcL5Nw== +inquirer@^6.4.1: + version "6.5.0" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.0.tgz#2303317efc9a4ea7ec2e2df6f86569b734accf42" + integrity sha512-scfHejeG/lVZSpvCXpsB4j/wQNPM5JC8kiElOI0OUTwmc1RTpXr4H32/HOlQHcZiYl2z2VElwuCVDRG8vFmbnA== dependencies: ansi-escapes "^3.2.0" chalk "^2.4.2" @@ -4972,7 +4988,7 @@ inquirer@^6.2.2: cli-width "^2.0.0" external-editor "^3.0.3" figures "^2.0.0" - lodash "^4.17.11" + lodash "^4.17.12" mute-stream "0.0.7" run-async "^2.2.0" rxjs "^6.4.0" @@ -5229,10 +5245,10 @@ is-glob@^3.1.0: dependencies: is-extglob "^2.1.0" -is-glob@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.0.tgz#9521c76845cc2610a85203ddf080a958c2ffabc0" - integrity sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A= +is-glob@^4.0.0, is-glob@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== dependencies: is-extglob "^2.1.1" @@ -5830,7 +5846,7 @@ js-string-escape@1.0.1: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@^3.12.0, js-yaml@^3.13.0, js-yaml@^3.13.1, js-yaml@^3.4.6, js-yaml@^3.5.1, js-yaml@^3.5.4, js-yaml@^3.9.0: +js-yaml@^3.12.0, js-yaml@^3.13.1, js-yaml@^3.4.6, js-yaml@^3.5.1, js-yaml@^3.5.4, js-yaml@^3.9.0: version "3.13.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== @@ -6194,10 +6210,10 @@ lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -lodash@^4.0.0, lodash@^4.13.1, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.5, lodash@^4.3.0, lodash@~4.17.10: - version "4.17.14" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.14.tgz#9ce487ae66c96254fe20b599f21b6816028078ba" - integrity sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw== +lodash@^4.0.0, lodash@^4.13.1, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.5, lodash@^4.3.0, lodash@~4.17.10: + version "4.17.15" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" + integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== loglevel@^1.6.3: version "1.6.3" @@ -8958,15 +8974,10 @@ semver@4.3.2: resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.2.tgz#c7a07158a80bedd052355b770d82d6640f803be7" integrity sha1-x6BxWKgL7dBSNVt3DYLWZA+AO+c= -semver@^6.0.0: - version "6.1.3" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.1.3.tgz#ef997a1a024f67dd48a7f155df88bb7b5c6c3fc7" - integrity sha512-aymF+56WJJMyXQHcd4hlK4N75rwj5RQpfW8ePlQnJsTYOBLlLbcIErR/G1s9SkIvKBqOudR3KAx4wEqP+F1hNQ== - -semver@^6.1.0, semver@^6.1.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.1.1.tgz#53f53da9b30b2103cd4f15eab3a18ecbcb210c9b" - integrity sha512-rWYq2e5iYW+fFe/oPPtYJxYgjBm8sC4rmoGdUOgBB7VnwKt6HrL793l2voH1UlsyYZpJ4g0wfjnTEO1s1NP2eQ== +semver@^6.0.0, semver@^6.1.0, semver@^6.1.1, semver@^6.1.2: + version "6.2.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.2.0.tgz#4d813d9590aaf8a9192693d6c85b9344de5901db" + integrity sha512-jdFC1VdUGT/2Scgbimf7FSx9iJLXoqfglSF+gJeuNWVpiE37OIbc1jywR/GJyFdz3mnkz2/id0L0J/cr0izR5A== send@0.17.1: version "0.17.1" @@ -9494,16 +9505,21 @@ strip-eof@^1.0.0: resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= -strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" - integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= +strip-json-comments@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7" + integrity sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw== strip-json-comments@~1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-1.0.4.tgz#1e15fbcac97d3ee99bf2d73b4c656b082bbafb91" integrity sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E= +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + stylehacks@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-4.0.1.tgz#3186595d047ab0df813d213e51c8b94e0b9010f2" @@ -10030,7 +10046,7 @@ uuid@^3.0.1, uuid@^3.1.0, uuid@^3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== -v8-compile-cache@2.0.3: +v8-compile-cache@2.0.3, v8-compile-cache@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz#00f7494d2ae2b688cfe2899df6ed2c54bef91dbe" integrity sha512-CNmdbwQMBjwr9Gsmohvm0pbL954tJrNzf6gWL3K+QMQf00PF7ERGrEiLgjuU3mKreLC2MeGhUsNV9ybTbLgd3w== From 964ae8eee593687f922c873fa7b378bb6e3e39bb Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 22 Jul 2019 10:48:50 +0200 Subject: [PATCH 14/24] Change unconfirmed user login behaviour (#11375) Allow access to account settings, 2FA, authorized applications, and account deletions to unconfirmed and pending users, as well as users who had their accounts disabled. Suspended users cannot update their e-mail or password or delete their account. Display account status on account settings page, for example, when an account is frozen, limited, unconfirmed or pending review. After sign up, login users straight away and show a simple page that tells them the status of their account with links to account settings and logout, to reduce onboarding friction and allow users to correct wrongly typed e-mail addresses. Move the final sign-up step of SSO integrations to be the same as above to reduce code duplication. --- app/controllers/about_controller.rb | 2 +- app/controllers/api/base_controller.rb | 2 +- app/controllers/application_controller.rb | 6 +- .../auth/confirmations_controller.rb | 21 +------ .../auth/omniauth_callbacks_controller.rb | 2 +- .../auth/registrations_controller.rb | 9 ++- app/controllers/auth/sessions_controller.rb | 4 +- app/controllers/auth/setup_controller.rb | 58 +++++++++++++++++++ .../authorized_applications_controller.rb | 2 + .../settings/deletes_controller.rb | 7 +++ .../settings/sessions_controller.rb | 2 + .../confirmations_controller.rb | 2 + .../recovery_codes_controller.rb | 2 + .../two_factor_authentications_controller.rb | 2 + app/javascript/styles/mastodon/admin.scss | 58 +++++++++++-------- app/javascript/styles/mastodon/forms.scss | 7 +++ app/models/concerns/omniauthable.rb | 2 +- app/models/user.rb | 6 +- .../confirmations/finish_signup.html.haml | 15 ----- .../auth/registrations/_sessions.html.haml | 4 +- .../auth/registrations/_status.html.haml | 16 +++++ app/views/auth/registrations/edit.html.haml | 33 ++++++----- app/views/auth/setup/show.html.haml | 23 ++++++++ .../authorized_applications/index.html.haml | 2 +- config/locales/en.yml | 9 ++- config/routes.rb | 5 +- db/seeds.rb | 2 +- spec/controllers/api/base_controller_spec.rb | 42 +++++++++++++- .../application_controller_spec.rb | 4 +- .../auth/confirmations_controller_spec.rb | 41 ------------- .../auth/registrations_controller_spec.rb | 25 +++++--- .../auth/sessions_controller_spec.rb | 4 +- .../settings/deletes_controller_spec.rb | 17 ++++++ spec/features/log_in_spec.rb | 4 +- spec/models/user_spec.rb | 4 +- 35 files changed, 297 insertions(+), 147 deletions(-) create mode 100644 app/controllers/auth/setup_controller.rb delete mode 100644 app/views/auth/confirmations/finish_signup.html.haml create mode 100644 app/views/auth/registrations/_status.html.haml create mode 100644 app/views/auth/setup/show.html.haml diff --git a/app/controllers/about_controller.rb b/app/controllers/about_controller.rb index 33bac9bbc7..31cf177105 100644 --- a/app/controllers/about_controller.rb +++ b/app/controllers/about_controller.rb @@ -7,7 +7,7 @@ class AboutController < ApplicationController before_action :set_instance_presenter before_action :set_expires_in - skip_before_action :check_user_permissions, only: [:more, :terms] + skip_before_action :require_functional!, only: [:more, :terms] def show; end diff --git a/app/controllers/api/base_controller.rb b/app/controllers/api/base_controller.rb index eca558f421..6f33a1ea99 100644 --- a/app/controllers/api/base_controller.rb +++ b/app/controllers/api/base_controller.rb @@ -7,7 +7,7 @@ class Api::BaseController < ApplicationController include RateLimitHeaders skip_before_action :store_current_location - skip_before_action :check_user_permissions + skip_before_action :require_functional! before_action :set_cache_headers diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index b8a1faf77e..41ce1a0ca2 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -25,7 +25,7 @@ class ApplicationController < ActionController::Base rescue_from Mastodon::NotPermittedError, with: :forbidden before_action :store_current_location, except: :raise_not_found, unless: :devise_controller? - before_action :check_user_permissions, if: :user_signed_in? + before_action :require_functional!, if: :user_signed_in? def raise_not_found raise ActionController::RoutingError, "No route matches #{params[:unmatched_route]}" @@ -57,8 +57,8 @@ class ApplicationController < ActionController::Base forbidden unless current_user&.staff? end - def check_user_permissions - forbidden if current_user.disabled? || current_user.account.suspended? + def require_functional! + redirect_to edit_user_registration_path unless current_user.functional? end def after_sign_out_path_for(_resource_or_scope) diff --git a/app/controllers/auth/confirmations_controller.rb b/app/controllers/auth/confirmations_controller.rb index c28c7471c0..0d7c6e7c2d 100644 --- a/app/controllers/auth/confirmations_controller.rb +++ b/app/controllers/auth/confirmations_controller.rb @@ -4,34 +4,15 @@ class Auth::ConfirmationsController < Devise::ConfirmationsController layout 'auth' before_action :set_body_classes - before_action :set_user, only: [:finish_signup] - def finish_signup - return unless request.patch? && params[:user] - - if @user.update(user_params) - @user.skip_reconfirmation! - bypass_sign_in(@user) - redirect_to root_path, notice: I18n.t('devise.confirmations.send_instructions') - else - @show_errors = true - end - end + skip_before_action :require_functional! private - def set_user - @user = current_user - end - def set_body_classes @body_classes = 'lighter' end - def user_params - params.require(:user).permit(:email) - end - def after_confirmation_path_for(_resource_name, user) if user.created_by_application && truthy_param?(:redirect_to_app) user.created_by_application.redirect_uri diff --git a/app/controllers/auth/omniauth_callbacks_controller.rb b/app/controllers/auth/omniauth_callbacks_controller.rb index bbf63bed30..682c77016f 100644 --- a/app/controllers/auth/omniauth_callbacks_controller.rb +++ b/app/controllers/auth/omniauth_callbacks_controller.rb @@ -27,7 +27,7 @@ class Auth::OmniauthCallbacksController < Devise::OmniauthCallbacksController if resource.email_verified? root_path else - finish_signup_path + auth_setup_path(missing_email: '1') end end end diff --git a/app/controllers/auth/registrations_controller.rb b/app/controllers/auth/registrations_controller.rb index 83797cf1f7..019caf9c1a 100644 --- a/app/controllers/auth/registrations_controller.rb +++ b/app/controllers/auth/registrations_controller.rb @@ -9,6 +9,9 @@ class Auth::RegistrationsController < Devise::RegistrationsController before_action :set_sessions, 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] + + skip_before_action :require_functional!, only: [:edit, :update] def new super(&:build_invite_request) @@ -43,7 +46,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController end def after_sign_up_path_for(_resource) - new_user_session_path + auth_setup_path end def after_sign_in_path_for(_resource) @@ -102,4 +105,8 @@ class Auth::RegistrationsController < Devise::RegistrationsController def set_sessions @sessions = current_user.session_activations end + + def require_not_suspended! + forbidden if current_account.suspended? + end end diff --git a/app/controllers/auth/sessions_controller.rb b/app/controllers/auth/sessions_controller.rb index fb8615c313..7e6dbf19e8 100644 --- a/app/controllers/auth/sessions_controller.rb +++ b/app/controllers/auth/sessions_controller.rb @@ -6,8 +6,10 @@ class Auth::SessionsController < Devise::SessionsController layout 'auth' skip_before_action :require_no_authentication, only: [:create] - skip_before_action :check_user_permissions, only: [:destroy] + skip_before_action :require_functional! + prepend_before_action :authenticate_with_two_factor, if: :two_factor_enabled?, only: [:create] + before_action :set_instance_presenter, only: [:new] before_action :set_body_classes diff --git a/app/controllers/auth/setup_controller.rb b/app/controllers/auth/setup_controller.rb new file mode 100644 index 0000000000..46c5f29581 --- /dev/null +++ b/app/controllers/auth/setup_controller.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +class Auth::SetupController < ApplicationController + layout 'auth' + + before_action :authenticate_user! + before_action :require_unconfirmed_or_pending! + before_action :set_body_classes + before_action :set_user + + skip_before_action :require_functional! + + def show + flash.now[:notice] = begin + if @user.pending? + I18n.t('devise.registrations.signed_up_but_pending') + else + I18n.t('devise.registrations.signed_up_but_unconfirmed') + end + end + end + + def update + # This allows updating the e-mail without entering a password as is required + # on the account settings page; however, we only allow this for accounts + # that were not confirmed yet + + if @user.update(user_params) + redirect_to auth_setup_path, notice: I18n.t('devise.confirmations.send_instructions') + else + render :show + end + end + + helper_method :missing_email? + + private + + def require_unconfirmed_or_pending! + redirect_to root_path if current_user.confirmed? && current_user.approved? + end + + def set_user + @user = current_user + end + + def set_body_classes + @body_classes = 'lighter' + end + + def user_params + params.require(:user).permit(:email) + end + + def missing_email? + truthy_param?(:missing_email) + end +end diff --git a/app/controllers/oauth/authorized_applications_controller.rb b/app/controllers/oauth/authorized_applications_controller.rb index f3d2353669..fb8389034b 100644 --- a/app/controllers/oauth/authorized_applications_controller.rb +++ b/app/controllers/oauth/authorized_applications_controller.rb @@ -7,6 +7,8 @@ class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicatio before_action :authenticate_resource_owner! before_action :set_body_classes + skip_before_action :require_functional! + include Localized def destroy diff --git a/app/controllers/settings/deletes_controller.rb b/app/controllers/settings/deletes_controller.rb index dd19aadf63..97fe4d3281 100644 --- a/app/controllers/settings/deletes_controller.rb +++ b/app/controllers/settings/deletes_controller.rb @@ -5,6 +5,9 @@ class Settings::DeletesController < Settings::BaseController before_action :check_enabled_deletion before_action :authenticate_user! + before_action :require_not_suspended! + + skip_before_action :require_functional! def show @confirmation = Form::DeleteConfirmation.new @@ -29,4 +32,8 @@ class Settings::DeletesController < Settings::BaseController def delete_params params.require(:form_delete_confirmation).permit(:password) end + + def require_not_suspended! + forbidden if current_account.suspended? + end end diff --git a/app/controllers/settings/sessions_controller.rb b/app/controllers/settings/sessions_controller.rb index 84ebb21f2c..df5ace8036 100644 --- a/app/controllers/settings/sessions_controller.rb +++ b/app/controllers/settings/sessions_controller.rb @@ -4,6 +4,8 @@ class Settings::SessionsController < Settings::BaseController before_action :authenticate_user! before_action :set_session, only: :destroy + skip_before_action :require_functional! + def destroy @session.destroy! flash[:notice] = I18n.t('sessions.revoke_success') diff --git a/app/controllers/settings/two_factor_authentication/confirmations_controller.rb b/app/controllers/settings/two_factor_authentication/confirmations_controller.rb index 02652a36c9..3145e092da 100644 --- a/app/controllers/settings/two_factor_authentication/confirmations_controller.rb +++ b/app/controllers/settings/two_factor_authentication/confirmations_controller.rb @@ -8,6 +8,8 @@ module Settings before_action :authenticate_user! before_action :ensure_otp_secret + skip_before_action :require_functional! + def new prepare_two_factor_form end diff --git a/app/controllers/settings/two_factor_authentication/recovery_codes_controller.rb b/app/controllers/settings/two_factor_authentication/recovery_codes_controller.rb index 874bf532ba..09a759860e 100644 --- a/app/controllers/settings/two_factor_authentication/recovery_codes_controller.rb +++ b/app/controllers/settings/two_factor_authentication/recovery_codes_controller.rb @@ -7,6 +7,8 @@ module Settings before_action :authenticate_user! + skip_before_action :require_functional! + def create @recovery_codes = current_user.generate_otp_backup_codes! current_user.save! diff --git a/app/controllers/settings/two_factor_authentications_controller.rb b/app/controllers/settings/two_factor_authentications_controller.rb index e12c430746..6904076e42 100644 --- a/app/controllers/settings/two_factor_authentications_controller.rb +++ b/app/controllers/settings/two_factor_authentications_controller.rb @@ -7,6 +7,8 @@ module Settings before_action :authenticate_user! before_action :verify_otp_required, only: [:create] + skip_before_action :require_functional! + def show @confirmation = Form::TwoFactorConfirmation.new end diff --git a/app/javascript/styles/mastodon/admin.scss b/app/javascript/styles/mastodon/admin.scss index 373a102603..f625bc1398 100644 --- a/app/javascript/styles/mastodon/admin.scss +++ b/app/javascript/styles/mastodon/admin.scss @@ -204,29 +204,6 @@ $content-width: 840px; border: 0; } } - - .muted-hint { - color: $darker-text-color; - - a { - color: $highlight-text-color; - } - } - - .positive-hint { - color: $valid-value-color; - font-weight: 500; - } - - .negative-hint { - color: $error-value-color; - font-weight: 500; - } - - .neutral-hint { - color: $dark-text-color; - font-weight: 500; - } } @media screen and (max-width: $no-columns-breakpoint) { @@ -249,6 +226,41 @@ $content-width: 840px; } } +hr.spacer { + width: 100%; + border: 0; + margin: 20px 0; + height: 1px; +} + +.muted-hint { + color: $darker-text-color; + + a { + color: $highlight-text-color; + } +} + +.positive-hint { + color: $valid-value-color; + font-weight: 500; +} + +.negative-hint { + color: $error-value-color; + font-weight: 500; +} + +.neutral-hint { + color: $dark-text-color; + font-weight: 500; +} + +.warning-hint { + color: $gold-star; + font-weight: 500; +} + .filters { display: flex; flex-wrap: wrap; diff --git a/app/javascript/styles/mastodon/forms.scss b/app/javascript/styles/mastodon/forms.scss index 456ee4e0d3..ac99124ea8 100644 --- a/app/javascript/styles/mastodon/forms.scss +++ b/app/javascript/styles/mastodon/forms.scss @@ -300,6 +300,13 @@ code { } } + .input.static .label_input__wrapper { + font-size: 16px; + padding: 10px; + border: 1px solid $dark-text-color; + border-radius: 4px; + } + input[type=text], input[type=number], input[type=email], diff --git a/app/models/concerns/omniauthable.rb b/app/models/concerns/omniauthable.rb index 2830330839..b9c124841b 100644 --- a/app/models/concerns/omniauthable.rb +++ b/app/models/concerns/omniauthable.rb @@ -43,7 +43,7 @@ module Omniauthable # Check if the user exists with provided email if the provider gives us a # verified email. If no verified email was provided or the user already # exists, we assign a temporary email and ask the user to verify it on - # the next step via Auth::ConfirmationsController.finish_signup + # the next step via Auth::SetupController.show user = User.new(user_params_from_auth(auth)) user.account.avatar_remote_url = auth.info.image if auth.info.image =~ /\A#{URI.regexp(%w(http https))}\z/ diff --git a/app/models/user.rb b/app/models/user.rb index 31c99630c3..474c77293c 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -161,7 +161,11 @@ class User < ApplicationRecord end def active_for_authentication? - super && approved? + true + end + + def functional? + confirmed? && approved? && !disabled? && !account.suspended? end def inactive_message diff --git a/app/views/auth/confirmations/finish_signup.html.haml b/app/views/auth/confirmations/finish_signup.html.haml deleted file mode 100644 index 9d09b74e16..0000000000 --- a/app/views/auth/confirmations/finish_signup.html.haml +++ /dev/null @@ -1,15 +0,0 @@ -- content_for :page_title do - = t('auth.confirm_email') - -= simple_form_for(current_user, as: 'user', url: finish_signup_path, html: { role: 'form'}) do |f| - - if @show_errors && current_user.errors.any? - #error_explanation - - current_user.errors.full_messages.each do |msg| - = msg - %br - - .fields-group - = f.input :email, wrapper: :with_label, required: true, hint: false - - .actions - = f.submit t('auth.confirm_email'), class: 'button' diff --git a/app/views/auth/registrations/_sessions.html.haml b/app/views/auth/registrations/_sessions.html.haml index d7d96a1bb3..395e36a9fd 100644 --- a/app/views/auth/registrations/_sessions.html.haml +++ b/app/views/auth/registrations/_sessions.html.haml @@ -1,6 +1,8 @@ -%h4= t 'sessions.title' +%h3= t 'sessions.title' %p.muted-hint= t 'sessions.explanation' +%hr.spacer/ + .table-wrapper %table.table.inline-table %thead diff --git a/app/views/auth/registrations/_status.html.haml b/app/views/auth/registrations/_status.html.haml new file mode 100644 index 0000000000..b38a83d67d --- /dev/null +++ b/app/views/auth/registrations/_status.html.haml @@ -0,0 +1,16 @@ +%h3= t('auth.status.account_status') + +- if @user.account.suspended? + %span.negative-hint= t('user_mailer.warning.explanation.suspend') +- elsif @user.disabled? + %span.negative-hint= t('user_mailer.warning.explanation.disable') +- elsif @user.account.silenced? + %span.warning-hint= t('user_mailer.warning.explanation.silence') +- elsif !@user.confirmed? + %span.warning-hint= t('auth.status.confirming') +- elsif !@user.approved? + %span.warning-hint= t('auth.status.pending') +- else + %span.positive-hint= t('auth.status.functional') + +%hr.spacer/ diff --git a/app/views/auth/registrations/edit.html.haml b/app/views/auth/registrations/edit.html.haml index 694461fdf2..710ee5c689 100644 --- a/app/views/auth/registrations/edit.html.haml +++ b/app/views/auth/registrations/edit.html.haml @@ -1,25 +1,28 @@ - content_for :page_title do - = t('auth.security') + = t('settings.account_settings') + += render 'status' + +%h3= t('auth.security') = simple_form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put, class: 'auth_edit' }) do |f| = render 'shared/error_messages', object: resource - if !use_seamless_external_login? || resource.encrypted_password.present? - .fields-group - = f.input :email, wrapper: :with_label, input_html: { 'aria-label' => t('simple_form.labels.defaults.email') }, required: true, hint: false - - .fields-group - = f.input :current_password, wrapper: :with_label, input_html: { 'aria-label' => t('simple_form.labels.defaults.current_password'), :autocomplete => 'off' }, required: true - - .fields-group - = f.input :password, wrapper: :with_label, label: t('simple_form.labels.defaults.new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.new_password'), :autocomplete => 'off' }, hint: false - - .fields-group - = f.input :password_confirmation, wrapper: :with_label, label: t('simple_form.labels.defaults.confirm_new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_new_password'), :autocomplete => 'off' } + .fields-row + .fields-row__column.fields-group.fields-row__column-6 + = f.input :email, wrapper: :with_label, input_html: { 'aria-label' => t('simple_form.labels.defaults.email') }, required: true, disabled: current_account.suspended? + .fields-row__column.fields-group.fields-row__column-6 + = f.input :current_password, wrapper: :with_label, input_html: { 'aria-label' => t('simple_form.labels.defaults.current_password'), :autocomplete => 'off' }, required: true, disabled: current_account.suspended? + .fields-row + .fields-row__column.fields-group.fields-row__column-6 + = f.input :password, wrapper: :with_label, label: t('simple_form.labels.defaults.new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.new_password'), :autocomplete => 'off' }, hint: t('simple_form.hints.defaults.password'), disabled: current_account.suspended? + .fields-row__column.fields-group.fields-row__column-6 + = f.input :password_confirmation, wrapper: :with_label, label: t('simple_form.labels.defaults.confirm_new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_new_password'), :autocomplete => 'off' }, disabled: current_account.suspended? .actions - = f.button :button, t('generic.save_changes'), type: :submit + = f.button :button, t('generic.save_changes'), type: :submit, class: 'button', disabled: current_account.suspended? - else %p.hint= t('users.seamless_external_login') @@ -27,7 +30,7 @@ = render 'sessions' -- if open_deletion? +- if open_deletion? && !current_account.suspended? %hr.spacer/ - %h4= t('auth.delete_account') + %h3= t('auth.delete_account') %p.muted-hint= t('auth.delete_account_html', path: settings_delete_path) diff --git a/app/views/auth/setup/show.html.haml b/app/views/auth/setup/show.html.haml new file mode 100644 index 0000000000..8bb44ca7f7 --- /dev/null +++ b/app/views/auth/setup/show.html.haml @@ -0,0 +1,23 @@ +- content_for :page_title do + = t('auth.setup.title') + +- if missing_email? + = simple_form_for(@user, url: auth_setup_path) do |f| + = render 'shared/error_messages', object: @user + + .fields-group + %p.hint= t('auth.setup.email_below_hint_html') + + .fields-group + = f.input :email, required: true, hint: false, input_html: { 'aria-label' => t('simple_form.labels.defaults.email'), :autocomplete => 'off' } + + .actions + = f.submit t('admin.accounts.change_email.label'), class: 'button' +- else + .simple_form + %p.hint= t('auth.setup.email_settings_hint_html', email: content_tag(:strong, @user.email)) + +.form-footer + %ul.no-list + %li= link_to t('settings.account_settings'), edit_user_registration_path + %li= link_to t('auth.logout'), destroy_user_session_path, data: { method: :delete } diff --git a/app/views/oauth/authorized_applications/index.html.haml b/app/views/oauth/authorized_applications/index.html.haml index 19af5f55db..7203d758da 100644 --- a/app/views/oauth/authorized_applications/index.html.haml +++ b/app/views/oauth/authorized_applications/index.html.haml @@ -17,7 +17,7 @@ = application.name - else = link_to application.name, application.website, target: '_blank', rel: 'noopener' - %th!= application.scopes.map { |scope| t(scope, scope: [:doorkeeper, :scopes]) }.join('
') + %th!= application.scopes.map { |scope| t(scope, scope: [:doorkeeper, :scopes]) }.join(', ') %td= l application.created_at %td - unless application.superapp? diff --git a/config/locales/en.yml b/config/locales/en.yml index 89c52b84ae..9e1be87be1 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -524,7 +524,6 @@ en: apply_for_account: Request an invite change_password: Password checkbox_agreement_html: I agree to the server rules and terms of service - confirm_email: Confirm email delete_account: Delete account delete_account_html: If you wish to delete your account, you can proceed here. You will be asked for confirmation. didnt_get_confirmation: Didn't receive confirmation instructions? @@ -544,6 +543,14 @@ en: reset_password: Reset password security: Security set_new_password: Set new password + setup: + email_below_hint_html: If the below e-mail address is incorrect, you can change it here and receive a new confirmation e-mail. + email_settings_hint_html: The confirmation e-mail was sent to %{email}. If that e-mail address is not correct, you can change it in account settings. + title: Setup + status: + account_status: Account status + confirming: Waiting for e-mail confirmation to be completed. + pending: Your application is pending review by our staff. This may take some time. You will receive an e-mail if your application is approved. trouble_logging_in: Trouble logging in? authorize_follow: already_following: You are already following this account diff --git a/config/routes.rb b/config/routes.rb index 27b5366419..b6c215888a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -34,7 +34,10 @@ Rails.application.routes.draw do devise_scope :user do get '/invite/:invite_code', to: 'auth/registrations#new', as: :public_invite - match '/auth/finish_signup' => 'auth/confirmations#finish_signup', via: [:get, :patch], as: :finish_signup + + namespace :auth do + resource :setup, only: [:show, :update], controller: :setup + end end devise_for :users, path: 'auth', controllers: { diff --git a/db/seeds.rb b/db/seeds.rb index b112cf0738..0bfb5d0db5 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -1,4 +1,4 @@ -Doorkeeper::Application.create!(name: 'Web', superapp: true, redirect_uri: Doorkeeper.configuration.native_redirect_uri, scopes: 'read write follow') +Doorkeeper::Application.create!(name: 'Web', superapp: true, redirect_uri: Doorkeeper.configuration.native_redirect_uri, scopes: 'read write follow push') domain = ENV['LOCAL_DOMAIN'] || Rails.configuration.x.local_domain account = Account.find_or_initialize_by(id: -99, actor_type: 'Application', locked: true, username: domain) diff --git a/spec/controllers/api/base_controller_spec.rb b/spec/controllers/api/base_controller_spec.rb index 750ccc8cf6..05a42d1c19 100644 --- a/spec/controllers/api/base_controller_spec.rb +++ b/spec/controllers/api/base_controller_spec.rb @@ -15,7 +15,7 @@ describe Api::BaseController do end end - describe 'Forgery protection' do + describe 'forgery protection' do before do routes.draw { post 'success' => 'api/base#success' } end @@ -27,7 +27,45 @@ describe Api::BaseController do end end - describe 'Error handling' do + describe 'non-functional accounts handling' do + let(:user) { Fabricate(:user, account: Fabricate(:account, username: 'alice')) } + let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read') } + + controller do + before_action :require_user! + end + + before do + routes.draw { post 'success' => 'api/base#success' } + allow(controller).to receive(:doorkeeper_token) { token } + end + + it 'returns http forbidden for unconfirmed accounts' do + user.update(confirmed_at: nil) + post 'success' + expect(response).to have_http_status(403) + end + + it 'returns http forbidden for pending accounts' do + user.update(approved: false) + post 'success' + expect(response).to have_http_status(403) + end + + it 'returns http forbidden for disabled accounts' do + user.update(disabled: true) + post 'success' + expect(response).to have_http_status(403) + end + + it 'returns http forbidden for suspended accounts' do + user.account.suspend! + post 'success' + expect(response).to have_http_status(403) + end + end + + describe 'error handling' do ERRORS_WITH_CODES = { ActiveRecord::RecordInvalid => 422, Mastodon::ValidationError => 422, diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index 27946b60f9..1811500dfe 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -187,10 +187,10 @@ describe ApplicationController, type: :controller do expect(response).to have_http_status(200) end - it 'returns http 403 if user who signed in is suspended' do + it 'redirects to account status page' do sign_in(Fabricate(:user, account: Fabricate(:account, suspended: true))) get 'success' - expect(response).to have_http_status(403) + expect(response).to redirect_to(edit_user_registration_path) end end diff --git a/spec/controllers/auth/confirmations_controller_spec.rb b/spec/controllers/auth/confirmations_controller_spec.rb index e9a471fc5a..0b6b74ff90 100644 --- a/spec/controllers/auth/confirmations_controller_spec.rb +++ b/spec/controllers/auth/confirmations_controller_spec.rb @@ -50,45 +50,4 @@ describe Auth::ConfirmationsController, type: :controller do end end end - - describe 'GET #finish_signup' do - subject { get :finish_signup } - - let(:user) { Fabricate(:user) } - before do - sign_in user, scope: :user - @request.env['devise.mapping'] = Devise.mappings[:user] - end - - it 'renders finish_signup' do - is_expected.to render_template :finish_signup - expect(assigns(:user)).to have_attributes id: user.id - end - end - - describe 'PATCH #finish_signup' do - subject { patch :finish_signup, params: { user: { email: email } } } - - let(:user) { Fabricate(:user) } - before do - sign_in user, scope: :user - @request.env['devise.mapping'] = Devise.mappings[:user] - end - - context 'when email is valid' do - let(:email) { 'new_' + user.email } - - it 'redirects to root_path' do - is_expected.to redirect_to root_path - end - end - - context 'when email is invalid' do - let(:email) { '' } - - it 'renders finish_signup' do - is_expected.to render_template :finish_signup - end - end - end end diff --git a/spec/controllers/auth/registrations_controller_spec.rb b/spec/controllers/auth/registrations_controller_spec.rb index a4337039e1..3e11b34b53 100644 --- a/spec/controllers/auth/registrations_controller_spec.rb +++ b/spec/controllers/auth/registrations_controller_spec.rb @@ -46,6 +46,15 @@ RSpec.describe Auth::RegistrationsController, type: :controller do post :update expect(response).to have_http_status(200) end + + context 'when suspended' do + it 'returns http forbidden' do + request.env["devise.mapping"] = Devise.mappings[:user] + sign_in(Fabricate(:user, account_attributes: { username: 'test', suspended_at: Time.now.utc }), scope: :user) + post :update + expect(response).to have_http_status(403) + end + end end describe 'GET #new' do @@ -94,9 +103,9 @@ RSpec.describe Auth::RegistrationsController, type: :controller do post :create, params: { user: { account_attributes: { username: 'test' }, email: 'test@example.com', password: '12345678', password_confirmation: '12345678' } } end - it 'redirects to login page' do + it 'redirects to setup' do subject - expect(response).to redirect_to new_user_session_path + expect(response).to redirect_to auth_setup_path end it 'creates user' do @@ -120,9 +129,9 @@ RSpec.describe Auth::RegistrationsController, type: :controller do post :create, params: { user: { account_attributes: { username: 'test' }, email: 'test@example.com', password: '12345678', password_confirmation: '12345678' } } end - it 'redirects to login page' do + it 'redirects to setup' do subject - expect(response).to redirect_to new_user_session_path + expect(response).to redirect_to auth_setup_path end it 'creates user' do @@ -148,9 +157,9 @@ RSpec.describe Auth::RegistrationsController, type: :controller do post :create, params: { user: { account_attributes: { username: 'test' }, email: 'test@example.com', password: '12345678', password_confirmation: '12345678', 'invite_code': invite.code } } end - it 'redirects to login page' do + it 'redirects to setup' do subject - expect(response).to redirect_to new_user_session_path + expect(response).to redirect_to auth_setup_path end it 'creates user' do @@ -176,9 +185,9 @@ RSpec.describe Auth::RegistrationsController, type: :controller do post :create, params: { user: { account_attributes: { username: 'test' }, email: 'test@example.com', password: '12345678', password_confirmation: '12345678', 'invite_code': invite.code } } end - it 'redirects to login page' do + it 'redirects to setup' do subject - expect(response).to redirect_to new_user_session_path + expect(response).to redirect_to auth_setup_path end it 'creates user' do diff --git a/spec/controllers/auth/sessions_controller_spec.rb b/spec/controllers/auth/sessions_controller_spec.rb index 71fcc1a6e3..87ef4f2bb2 100644 --- a/spec/controllers/auth/sessions_controller_spec.rb +++ b/spec/controllers/auth/sessions_controller_spec.rb @@ -160,8 +160,8 @@ RSpec.describe Auth::SessionsController, type: :controller do let(:unconfirmed_user) { user.tap { |u| u.update!(confirmed_at: nil) } } let(:accept_language) { 'fr' } - it 'shows a translated login error' do - expect(flash[:alert]).to eq(I18n.t('devise.failure.unconfirmed', locale: accept_language)) + it 'redirects to home' do + expect(response).to redirect_to(root_path) end end diff --git a/spec/controllers/settings/deletes_controller_spec.rb b/spec/controllers/settings/deletes_controller_spec.rb index 35fd64e9b9..996872efd1 100644 --- a/spec/controllers/settings/deletes_controller_spec.rb +++ b/spec/controllers/settings/deletes_controller_spec.rb @@ -15,6 +15,15 @@ describe Settings::DeletesController do get :show expect(response).to have_http_status(200) end + + context 'when suspended' do + let(:user) { Fabricate(:user, account_attributes: { username: 'alice', suspended_at: Time.now.utc }) } + + it 'returns http forbidden' do + get :show + expect(response).to have_http_status(403) + end + end end context 'when not signed in' do @@ -49,6 +58,14 @@ describe Settings::DeletesController do it 'marks account as suspended' do expect(user.account.reload).to be_suspended end + + context 'when suspended' do + let(:user) { Fabricate(:user, account_attributes: { username: 'alice', suspended_at: Time.now.utc }) } + + it 'returns http forbidden' do + expect(response).to have_http_status(403) + end + end end context 'with incorrect password' do diff --git a/spec/features/log_in_spec.rb b/spec/features/log_in_spec.rb index 53a1f9b126..f6c26cd0f1 100644 --- a/spec/features/log_in_spec.rb +++ b/spec/features/log_in_spec.rb @@ -31,12 +31,12 @@ feature "Log in" do context do given(:confirmed_at) { nil } - scenario "A unconfirmed user is not able to log in" do + scenario "A unconfirmed user is able to log in" do fill_in "user_email", with: email fill_in "user_password", with: password click_on I18n.t('auth.login') - is_expected.to have_css(".flash-message", text: failure_message("unconfirmed")) + is_expected.to have_css("div.admin-wrapper") end end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 856254ce4b..d7c0b53593 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -506,7 +506,7 @@ RSpec.describe User, type: :model do context 'when user is not confirmed' do let(:confirmed_at) { nil } - it { is_expected.to be false } + it { is_expected.to be true } end end @@ -522,7 +522,7 @@ RSpec.describe User, type: :model do context 'when user is not confirmed' do let(:confirmed_at) { nil } - it { is_expected.to be false } + it { is_expected.to be true } end end end From b47e3b6cd89ee22c26babee9d73c3655b28d9a67 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2019 23:04:17 +0900 Subject: [PATCH 15/24] Bump capybara from 3.25.0 to 3.26.0 (#11385) Bumps [capybara](https://github.com/teamcapybara/capybara) from 3.25.0 to 3.26.0. - [Release notes](https://github.com/teamcapybara/capybara/releases) - [Changelog](https://github.com/teamcapybara/capybara/blob/master/History.md) - [Commits](https://github.com/teamcapybara/capybara/compare/3.25.0...3.26.0) Signed-off-by: dependabot-preview[bot] --- Gemfile | 2 +- Gemfile.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Gemfile b/Gemfile index d27e3560cb..78a33268c4 100644 --- a/Gemfile +++ b/Gemfile @@ -109,7 +109,7 @@ group :production, :test do end group :test do - gem 'capybara', '~> 3.25' + gem 'capybara', '~> 3.26' gem 'climate_control', '~> 0.2' gem 'faker', '~> 1.9' gem 'microformats', '~> 4.1' diff --git a/Gemfile.lock b/Gemfile.lock index 340537ac90..c003d650c2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -136,7 +136,7 @@ GEM sshkit (~> 1.3) capistrano-yarn (2.0.2) capistrano (~> 3.0) - capybara (3.25.0) + capybara (3.26.0) addressable mini_mime (>= 0.1.3) nokogiri (~> 1.8) @@ -352,7 +352,7 @@ GEM mime-types-data (~> 3.2015) mime-types-data (3.2018.0812) mimemagic (0.3.3) - mini_mime (1.0.1) + mini_mime (1.0.2) mini_portile2 (2.4.0) minitest (5.11.3) msgpack (1.2.10) @@ -502,7 +502,7 @@ GEM redis-store (>= 1.2, < 2) redis-store (1.5.0) redis (>= 2.2, < 5) - regexp_parser (1.5.1) + regexp_parser (1.6.0) request_store (1.4.1) rack (>= 1.4) responders (2.4.1) @@ -668,7 +668,7 @@ DEPENDENCIES capistrano-rails (~> 1.4) capistrano-rbenv (~> 2.1) capistrano-yarn (~> 2.0) - capybara (~> 3.25) + capybara (~> 3.26) charlock_holmes (~> 0.7.6) chewy (~> 5.0) cld3 (~> 3.2.4) From 362fa2dc8ae928fa044dc045f579c32374317dbe Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2019 23:05:34 +0900 Subject: [PATCH 16/24] Bump premailer-rails from 1.10.2 to 1.10.3 (#11386) Bumps [premailer-rails](https://github.com/fphilipe/premailer-rails) from 1.10.2 to 1.10.3. - [Release notes](https://github.com/fphilipe/premailer-rails/releases) - [Changelog](https://github.com/fphilipe/premailer-rails/blob/master/CHANGELOG.md) - [Commits](https://github.com/fphilipe/premailer-rails/compare/v1.10.2...v1.10.3) Signed-off-by: dependabot-preview[bot] --- Gemfile.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index c003d650c2..801be8ee50 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -163,7 +163,7 @@ GEM crack (0.4.3) safe_yaml (~> 1.0.0) crass (1.0.4) - css_parser (1.6.0) + css_parser (1.7.0) addressable debug_inspector (0.0.3) derailed_benchmarks (1.3.6) @@ -415,8 +415,8 @@ GEM addressable css_parser (>= 1.6.0) htmlentities (>= 4.0.0) - premailer-rails (1.10.2) - actionmailer (>= 3, < 6) + premailer-rails (1.10.3) + actionmailer (>= 3) premailer (~> 1.7, >= 1.7.9) private_address_check (0.5.0) pry (0.12.2) From e980e19a91dd085bbd591f7e24ec80a030cb3a91 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2019 23:06:34 +0900 Subject: [PATCH 17/24] Bump rubocop from 0.72.0 to 0.73.0 (#11384) Bumps [rubocop](https://github.com/rubocop-hq/rubocop) from 0.72.0 to 0.73.0. - [Release notes](https://github.com/rubocop-hq/rubocop/releases) - [Changelog](https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md) - [Commits](https://github.com/rubocop-hq/rubocop/compare/v0.72.0...v0.73.0) Signed-off-by: dependabot-preview[bot] --- Gemfile | 2 +- Gemfile.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index 78a33268c4..73d109ec3a 100644 --- a/Gemfile +++ b/Gemfile @@ -129,7 +129,7 @@ group :development do gem 'letter_opener', '~> 1.7' gem 'letter_opener_web', '~> 1.3' gem 'memory_profiler' - gem 'rubocop', '~> 0.72', require: false + gem 'rubocop', '~> 0.73', require: false gem 'rubocop-rails', '~> 2.2', require: false gem 'brakeman', '~> 4.5', require: false gem 'bundler-audit', '~> 0.6', require: false diff --git a/Gemfile.lock b/Gemfile.lock index 801be8ee50..0161d84668 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -532,7 +532,7 @@ GEM rspec-core (~> 3.0, >= 3.0.0) sidekiq (>= 2.4.0) rspec-support (3.8.0) - rubocop (0.72.0) + rubocop (0.73.0) jaro_winkler (~> 1.5.1) parallel (~> 1.10) parser (>= 2.6) @@ -747,7 +747,7 @@ DEPENDENCIES rqrcode (~> 0.10) rspec-rails (~> 3.8) rspec-sidekiq (~> 3.0) - rubocop (~> 0.72) + rubocop (~> 0.73) rubocop-rails (~> 2.2) sanitize (~> 5.0) sidekiq (~> 5.2) From 05b8468755acea97e96778e97fca5558243a34cf Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2019 23:07:42 +0900 Subject: [PATCH 18/24] Bump oj from 3.7.12 to 3.8.0 (#11387) Bumps [oj](https://github.com/ohler55/oj) from 3.7.12 to 3.8.0. - [Release notes](https://github.com/ohler55/oj/releases) - [Changelog](https://github.com/ohler55/oj/blob/develop/CHANGELOG.md) - [Commits](https://github.com/ohler55/oj/compare/v3.7.12...v3.8.0) Signed-off-by: dependabot-preview[bot] --- Gemfile | 2 +- Gemfile.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index 73d109ec3a..e969c4ae0c 100644 --- a/Gemfile +++ b/Gemfile @@ -61,7 +61,7 @@ gem 'mime-types', '~> 3.2', require: 'mime/types/columnar' gem 'nilsimsa', git: 'https://github.com/witgo/nilsimsa', ref: 'fd184883048b922b176939f851338d0a4971a532' gem 'nokogiri', '~> 1.10' gem 'nsa', '~> 0.2' -gem 'oj', '~> 3.7' +gem 'oj', '~> 3.8' gem 'ostatus2', '~> 2.0' gem 'ox', '~> 2.11' gem 'posix-spawn', git: 'https://github.com/rtomayko/posix-spawn', ref: '58465d2e213991f8afb13b984854a49fcdcc980c' diff --git a/Gemfile.lock b/Gemfile.lock index 0161d84668..eca010ec3f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -373,7 +373,7 @@ GEM concurrent-ruby (~> 1.0, >= 1.0.2) sidekiq (>= 3.5) statsd-ruby (~> 1.4, >= 1.4.0) - oj (3.7.12) + oj (3.8.0) omniauth (1.9.0) hashie (>= 3.4.6, < 3.7.0) rack (>= 1.6.2, < 3) @@ -715,7 +715,7 @@ DEPENDENCIES nilsimsa! nokogiri (~> 1.10) nsa (~> 0.2) - oj (~> 3.7) + oj (~> 3.8) omniauth (~> 1.9) omniauth-cas (~> 1.1) omniauth-saml (~> 1.10) From ab3126e7a23125b033ec198cfd83c2178338442c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2019 23:11:01 +0900 Subject: [PATCH 19/24] Bump browser from 2.5.3 to 2.6.1 (#11388) Bumps [browser](https://github.com/fnando/browser) from 2.5.3 to 2.6.1. - [Release notes](https://github.com/fnando/browser/releases) - [Changelog](https://github.com/fnando/browser/blob/master/CHANGELOG.md) - [Commits](https://github.com/fnando/browser/compare/v2.5.3...v2.6.1) Signed-off-by: dependabot-preview[bot] --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index eca010ec3f..1a5d54b444 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -111,7 +111,7 @@ GEM bootsnap (1.4.4) msgpack (~> 1.0) brakeman (4.5.1) - browser (2.5.3) + browser (2.6.1) builder (3.2.3) bullet (6.0.1) activesupport (>= 3.0.0) From 8a89a2b0f40c427fc48e449aa166f5f60d5a9575 Mon Sep 17 00:00:00 2001 From: Thibaut Girka Date: Tue, 23 Jul 2019 10:30:24 +0200 Subject: [PATCH 20/24] Revert some refactoring in order to make codebase closer to upstream --- app/controllers/settings/applications_controller.rb | 3 +++ app/controllers/settings/base_controller.rb | 5 ++--- app/controllers/settings/deletes_controller.rb | 4 +++- app/controllers/settings/exports_controller.rb | 4 ++++ app/controllers/settings/flavours_controller.rb | 6 ++++++ app/controllers/settings/imports_controller.rb | 3 +++ app/controllers/settings/migrations_controller.rb | 4 ++++ app/controllers/settings/preferences_controller.rb | 4 ++++ app/controllers/settings/profiles_controller.rb | 3 +++ .../two_factor_authentication/confirmations_controller.rb | 3 +++ .../two_factor_authentication/recovery_codes_controller.rb | 4 ++++ .../settings/two_factor_authentications_controller.rb | 3 +++ 12 files changed, 42 insertions(+), 4 deletions(-) diff --git a/app/controllers/settings/applications_controller.rb b/app/controllers/settings/applications_controller.rb index d3ac268d86..ed3f82a8e0 100644 --- a/app/controllers/settings/applications_controller.rb +++ b/app/controllers/settings/applications_controller.rb @@ -1,6 +1,9 @@ # frozen_string_literal: true class Settings::ApplicationsController < Settings::BaseController + layout 'admin' + + before_action :authenticate_user! before_action :set_application, only: [:show, :update, :destroy, :regenerate] before_action :prepare_scopes, only: [:create, :update] diff --git a/app/controllers/settings/base_controller.rb b/app/controllers/settings/base_controller.rb index 34ef165689..8c394a6d35 100644 --- a/app/controllers/settings/base_controller.rb +++ b/app/controllers/settings/base_controller.rb @@ -1,12 +1,11 @@ # frozen_string_literal: true class Settings::BaseController < ApplicationController - layout 'admin' - - before_action :authenticate_user! before_action :set_pack before_action :set_body_classes + private + def set_pack use_pack 'settings' end diff --git a/app/controllers/settings/deletes_controller.rb b/app/controllers/settings/deletes_controller.rb index 20dcc2dbd6..97fe4d3281 100644 --- a/app/controllers/settings/deletes_controller.rb +++ b/app/controllers/settings/deletes_controller.rb @@ -1,8 +1,10 @@ # frozen_string_literal: true class Settings::DeletesController < Settings::BaseController + layout 'admin' - prepend_before_action :check_enabled_deletion + before_action :check_enabled_deletion + before_action :authenticate_user! before_action :require_not_suspended! skip_before_action :require_functional! diff --git a/app/controllers/settings/exports_controller.rb b/app/controllers/settings/exports_controller.rb index 7f76668d56..3012fbf775 100644 --- a/app/controllers/settings/exports_controller.rb +++ b/app/controllers/settings/exports_controller.rb @@ -3,6 +3,10 @@ class Settings::ExportsController < Settings::BaseController include Authorization + layout 'admin' + + before_action :authenticate_user! + def show @export = Export.new(current_account) @backups = current_user.backups diff --git a/app/controllers/settings/flavours_controller.rb b/app/controllers/settings/flavours_controller.rb index 6343877153..62c52eee97 100644 --- a/app/controllers/settings/flavours_controller.rb +++ b/app/controllers/settings/flavours_controller.rb @@ -1,6 +1,12 @@ # frozen_string_literal: true class Settings::FlavoursController < Settings::BaseController + layout 'admin' + + before_action :authenticate_user! + + skip_before_action :require_functional! + def index redirect_to action: 'show', flavour: current_flavour end diff --git a/app/controllers/settings/imports_controller.rb b/app/controllers/settings/imports_controller.rb index dbd136ebe1..38f2e39c1d 100644 --- a/app/controllers/settings/imports_controller.rb +++ b/app/controllers/settings/imports_controller.rb @@ -1,6 +1,9 @@ # frozen_string_literal: true class Settings::ImportsController < Settings::BaseController + layout 'admin' + + before_action :authenticate_user! before_action :set_account def show diff --git a/app/controllers/settings/migrations_controller.rb b/app/controllers/settings/migrations_controller.rb index 89b3f7246c..59eb48779c 100644 --- a/app/controllers/settings/migrations_controller.rb +++ b/app/controllers/settings/migrations_controller.rb @@ -1,6 +1,10 @@ # frozen_string_literal: true class Settings::MigrationsController < Settings::BaseController + layout 'admin' + + before_action :authenticate_user! + def show @migration = Form::Migration.new(account: current_account.moved_to_account) end diff --git a/app/controllers/settings/preferences_controller.rb b/app/controllers/settings/preferences_controller.rb index 372f253cba..ab6b5c0b07 100644 --- a/app/controllers/settings/preferences_controller.rb +++ b/app/controllers/settings/preferences_controller.rb @@ -1,6 +1,10 @@ # frozen_string_literal: true class Settings::PreferencesController < Settings::BaseController + layout 'admin' + + before_action :authenticate_user! + def show; end def update diff --git a/app/controllers/settings/profiles_controller.rb b/app/controllers/settings/profiles_controller.rb index 76d599f08d..8b640cdca1 100644 --- a/app/controllers/settings/profiles_controller.rb +++ b/app/controllers/settings/profiles_controller.rb @@ -3,6 +3,9 @@ class Settings::ProfilesController < Settings::BaseController include ObfuscateFilename + layout 'admin' + + before_action :authenticate_user! before_action :set_account obfuscate_filename [:account, :avatar] diff --git a/app/controllers/settings/two_factor_authentication/confirmations_controller.rb b/app/controllers/settings/two_factor_authentication/confirmations_controller.rb index 1708d71d79..3145e092da 100644 --- a/app/controllers/settings/two_factor_authentication/confirmations_controller.rb +++ b/app/controllers/settings/two_factor_authentication/confirmations_controller.rb @@ -3,6 +3,9 @@ module Settings module TwoFactorAuthentication class ConfirmationsController < BaseController + layout 'admin' + + before_action :authenticate_user! before_action :ensure_otp_secret skip_before_action :require_functional! diff --git a/app/controllers/settings/two_factor_authentication/recovery_codes_controller.rb b/app/controllers/settings/two_factor_authentication/recovery_codes_controller.rb index 56763e10cc..09a759860e 100644 --- a/app/controllers/settings/two_factor_authentication/recovery_codes_controller.rb +++ b/app/controllers/settings/two_factor_authentication/recovery_codes_controller.rb @@ -3,6 +3,10 @@ module Settings module TwoFactorAuthentication class RecoveryCodesController < BaseController + layout 'admin' + + before_action :authenticate_user! + skip_before_action :require_functional! def create diff --git a/app/controllers/settings/two_factor_authentications_controller.rb b/app/controllers/settings/two_factor_authentications_controller.rb index e632d39cf8..6904076e42 100644 --- a/app/controllers/settings/two_factor_authentications_controller.rb +++ b/app/controllers/settings/two_factor_authentications_controller.rb @@ -2,6 +2,9 @@ module Settings class TwoFactorAuthenticationsController < BaseController + layout 'admin' + + before_action :authenticate_user! before_action :verify_otp_required, only: [:create] skip_before_action :require_functional! From cd8763b600d73a076c23a94d9c54ab04aac51314 Mon Sep 17 00:00:00 2001 From: Thibaut Girka Date: Tue, 23 Jul 2019 10:33:25 +0200 Subject: [PATCH 21/24] [Glitch] Display custom emoji in bio field names Port 4bd58b7f2da369a608eacb97f832728ddc139ce8 to glitch-soc --- app/javascript/flavours/glitch/actions/importer/normalizer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/flavours/glitch/actions/importer/normalizer.js b/app/javascript/flavours/glitch/actions/importer/normalizer.js index c19ca82659..52d85c059c 100644 --- a/app/javascript/flavours/glitch/actions/importer/normalizer.js +++ b/app/javascript/flavours/glitch/actions/importer/normalizer.js @@ -22,7 +22,7 @@ export function normalizeAccount(account) { if (account.fields) { account.fields = account.fields.map(pair => ({ ...pair, - name_emojified: emojify(escapeTextContentForBrowser(pair.name)), + name_emojified: emojify(escapeTextContentForBrowser(pair.name), emojiMap), value_emojified: emojify(pair.value, emojiMap), value_plain: unescapeHTML(pair.value), })); From 621590b4ab31dd12de97ded31f17a00ff2fc6dd6 Mon Sep 17 00:00:00 2001 From: Thibaut Girka Date: Tue, 23 Jul 2019 10:45:35 +0200 Subject: [PATCH 22/24] Refactor DisplayName component to make it closer to upstream --- .../glitch/components/display_name.js | 112 +++++++++--------- 1 file changed, 54 insertions(+), 58 deletions(-) diff --git a/app/javascript/flavours/glitch/components/display_name.js b/app/javascript/flavours/glitch/components/display_name.js index 7f6ef5a5da..7626fb25ca 100644 --- a/app/javascript/flavours/glitch/components/display_name.js +++ b/app/javascript/flavours/glitch/components/display_name.js @@ -1,73 +1,69 @@ -// Package imports. -import classNames from 'classnames'; -import PropTypes from 'prop-types'; import React from 'react'; import ImmutablePropTypes from 'react-immutable-proptypes'; +import PropTypes from 'prop-types'; +import classNames from 'classnames'; -// The component. -export default function DisplayName ({ - account, - className, - inline, - localDomain, - others, - onAccountClick, -}) { - const computedClass = classNames('display-name', { inline }, className); +export default class DisplayName extends React.PureComponent { - if (!account) return null; + static propTypes = { + account: ImmutablePropTypes.map, + className: PropTypes.string, + inline: PropTypes.bool, + localDomain: PropTypes.string, + others: ImmutablePropTypes.list, + handleClick: PropTypes.func, + }; - let displayName, suffix; + render() { + const { account, className, inline, localDomain, others, onAccountClick } = this.props; - let acct = account.get('acct'); + const computedClass = classNames('display-name', { inline }, className); - if (acct.indexOf('@') === -1 && localDomain) { - acct = `${acct}@${localDomain}`; - } + if (!account) return null; - if (others && others.size > 0) { - displayName = others.take(2).map(a => ( - onAccountClick(a.get('id'), e)} - title={`@${a.get('acct')}`} - > - - - - - )).reduce((prev, cur) => [prev, ', ', cur]); + let displayName, suffix; - if (others.size - 2 > 0) { - displayName.push(` +${others.size - 2}`); + let acct = account.get('acct'); + + if (acct.indexOf('@') === -1 && localDomain) { + acct = `${acct}@${localDomain}`; } - suffix = ( - onAccountClick(account.get('id'), e)}> - @{acct} - + if (others && others.size > 0) { + displayName = others.take(2).map(a => ( + onAccountClick(a.get('id'), e)} + title={`@${a.get('acct')}`} + > + + + + + )).reduce((prev, cur) => [prev, ', ', cur]); + + if (others.size - 2 > 0) { + displayName.push(` +${others.size - 2}`); + } + + suffix = ( + onAccountClick(account.get('id'), e)}> + @{acct} + + ); + } else { + displayName = ; + suffix = @{acct}; + } + + return ( + + {displayName} + {inline ? ' ' : null} + {suffix} + ); - } else { - displayName = ; - suffix = @{acct}; } - return ( - - {displayName} - {inline ? ' ' : null} - {suffix} - - ); } - -// Props. -DisplayName.propTypes = { - account: ImmutablePropTypes.map, - className: PropTypes.string, - inline: PropTypes.bool, - localDomain: PropTypes.string, - others: ImmutablePropTypes.list, - handleClick: PropTypes.func, -}; From c1231a846ac7b4a83b5b5b05384bca670d66ccde Mon Sep 17 00:00:00 2001 From: ThibG Date: Sun, 21 Jul 2019 18:10:40 +0200 Subject: [PATCH 23/24] [Glitch] Play animated custom emoji on hover Port 7de8c51873b51d8450f7a6597a43d454964d0407 to glitch-soc --- .../glitch/components/display_name.js | 44 ++++++++++++++++++- .../glitch/components/status_content.js | 32 ++++++++++++++ .../features/account/components/header.js | 43 +++++++++++++++++- .../flavours/glitch/packs/public.js | 10 +++++ .../flavours/glitch/util/emoji/index.js | 2 +- 5 files changed, 128 insertions(+), 3 deletions(-) diff --git a/app/javascript/flavours/glitch/components/display_name.js b/app/javascript/flavours/glitch/components/display_name.js index 7626fb25ca..9d8c4a775e 100644 --- a/app/javascript/flavours/glitch/components/display_name.js +++ b/app/javascript/flavours/glitch/components/display_name.js @@ -2,6 +2,7 @@ import React from 'react'; import ImmutablePropTypes from 'react-immutable-proptypes'; import PropTypes from 'prop-types'; import classNames from 'classnames'; +import { autoPlayGif } from 'flavours/glitch/util/initial_state'; export default class DisplayName extends React.PureComponent { @@ -14,6 +15,47 @@ export default class DisplayName extends React.PureComponent { handleClick: PropTypes.func, }; + _updateEmojis () { + const node = this.node; + + if (!node || autoPlayGif) { + return; + } + + const emojis = node.querySelectorAll('.custom-emoji'); + + for (var i = 0; i < emojis.length; i++) { + let emoji = emojis[i]; + if (emoji.classList.contains('status-emoji')) { + continue; + } + emoji.classList.add('status-emoji'); + + emoji.addEventListener('mouseenter', this.handleEmojiMouseEnter, false); + emoji.addEventListener('mouseleave', this.handleEmojiMouseLeave, false); + } + } + + componentDidMount () { + this._updateEmojis(); + } + + componentDidUpdate () { + this._updateEmojis(); + } + + handleEmojiMouseEnter = ({ target }) => { + target.src = target.getAttribute('data-original'); + } + + handleEmojiMouseLeave = ({ target }) => { + target.src = target.getAttribute('data-static'); + } + + setRef = (c) => { + this.node = c; + } + render() { const { account, className, inline, localDomain, others, onAccountClick } = this.props; @@ -58,7 +100,7 @@ export default class DisplayName extends React.PureComponent { } return ( - + {displayName} {inline ? ' ' : null} {suffix} diff --git a/app/javascript/flavours/glitch/components/status_content.js b/app/javascript/flavours/glitch/components/status_content.js index 07a0d1d5de..6e357fba0f 100644 --- a/app/javascript/flavours/glitch/components/status_content.js +++ b/app/javascript/flavours/glitch/components/status_content.js @@ -5,6 +5,7 @@ import { isRtl } from 'flavours/glitch/util/rtl'; import { FormattedMessage } from 'react-intl'; import Permalink from './permalink'; import classnames from 'classnames'; +import { autoPlayGif } from 'flavours/glitch/util/initial_state'; export default class StatusContent extends React.PureComponent { @@ -57,12 +58,35 @@ export default class StatusContent extends React.PureComponent { } } + _updateStatusEmojis () { + const node = this.node; + + if (!node || autoPlayGif) { + return; + } + + const emojis = node.querySelectorAll('.custom-emoji'); + + for (var i = 0; i < emojis.length; i++) { + let emoji = emojis[i]; + if (emoji.classList.contains('status-emoji')) { + continue; + } + emoji.classList.add('status-emoji'); + + emoji.addEventListener('mouseenter', this.handleEmojiMouseEnter, false); + emoji.addEventListener('mouseleave', this.handleEmojiMouseLeave, false); + } + } + componentDidMount () { this._updateStatusLinks(); + this._updateStatusEmojis(); } componentDidUpdate () { this._updateStatusLinks(); + this._updateStatusEmojis(); if (this.props.onUpdate) this.props.onUpdate(); } @@ -86,6 +110,14 @@ export default class StatusContent extends React.PureComponent { } } + handleEmojiMouseEnter = ({ target }) => { + target.src = target.getAttribute('data-original'); + } + + handleEmojiMouseLeave = ({ target }) => { + target.src = target.getAttribute('data-static'); + } + handleMouseDown = (e) => { this.startXY = [e.clientX, e.clientY]; } diff --git a/app/javascript/flavours/glitch/features/account/components/header.js b/app/javascript/flavours/glitch/features/account/components/header.js index 43c4f0d32a..e9437c0a96 100644 --- a/app/javascript/flavours/glitch/features/account/components/header.js +++ b/app/javascript/flavours/glitch/features/account/components/header.js @@ -71,6 +71,47 @@ class Header extends ImmutablePureComponent { window.open('/settings/profile', '_blank'); } + _updateEmojis () { + const node = this.node; + + if (!node || autoPlayGif) { + return; + } + + const emojis = node.querySelectorAll('.custom-emoji'); + + for (var i = 0; i < emojis.length; i++) { + let emoji = emojis[i]; + if (emoji.classList.contains('status-emoji')) { + continue; + } + emoji.classList.add('status-emoji'); + + emoji.addEventListener('mouseenter', this.handleEmojiMouseEnter, false); + emoji.addEventListener('mouseleave', this.handleEmojiMouseLeave, false); + } + } + + componentDidMount () { + this._updateEmojis(); + } + + componentDidUpdate () { + this._updateEmojis(); + } + + handleEmojiMouseEnter = ({ target }) => { + target.src = target.getAttribute('data-original'); + } + + handleEmojiMouseLeave = ({ target }) => { + target.src = target.getAttribute('data-static'); + } + + setRef = (c) => { + this.node = c; + } + render () { const { account, intl, domain, identity_proofs } = this.props; @@ -193,7 +234,7 @@ class Header extends ImmutablePureComponent { const acct = account.get('acct').indexOf('@') === -1 && domain ? `${account.get('acct')}@${domain}` : account.get('acct'); return ( -
+
{info} diff --git a/app/javascript/flavours/glitch/packs/public.js b/app/javascript/flavours/glitch/packs/public.js index da0b4c8e0e..9f88b0e04b 100644 --- a/app/javascript/flavours/glitch/packs/public.js +++ b/app/javascript/flavours/glitch/packs/public.js @@ -4,6 +4,7 @@ import ready from 'flavours/glitch/util/ready'; function main() { const IntlMessageFormat = require('intl-messageformat').default; const { timeAgoString } = require('flavours/glitch/components/relative_timestamp'); + const { delegate } = require('rails-ujs'); const emojify = require('flavours/glitch/util/emoji').default; const { getLocale } = require('locales'); const { messages } = getLocale(); @@ -23,6 +24,12 @@ function main() { } }; + const getEmojiAnimationHandler = (swapTo) => { + return ({ target }) => { + target.src = target.getAttribute(swapTo); + }; + }; + ready(() => { const locale = document.documentElement.lang; @@ -94,6 +101,9 @@ function main() { document.head.appendChild(scrollbarWidthStyle); scrollbarWidthStyle.sheet.insertRule(`body.with-modals--active { margin-right: ${scrollbarWidth}px; }`, 0); } + + delegate(document, '.custom-emoji', 'mouseover', getEmojiAnimationHandler('data-original')); + delegate(document, '.custom-emoji', 'mouseout', getEmojiAnimationHandler('data-static')); }); } diff --git a/app/javascript/flavours/glitch/util/emoji/index.js b/app/javascript/flavours/glitch/util/emoji/index.js index e6fcaf0dce..2f40f9b085 100644 --- a/app/javascript/flavours/glitch/util/emoji/index.js +++ b/app/javascript/flavours/glitch/util/emoji/index.js @@ -29,7 +29,7 @@ const emojify = (str, customEmojis = {}) => { // if you want additional emoji handler, add statements below which set replacement and return true. if (shortname in customEmojis) { const filename = autoPlayGif ? customEmojis[shortname].url : customEmojis[shortname].static_url; - replacement = `${shortname}`; + replacement = `${shortname}`; return true; } return false; From 48c68eafb73d2e697dab25ec55c235da4daf1cf8 Mon Sep 17 00:00:00 2001 From: Thibaut Girka Date: Tue, 23 Jul 2019 11:10:22 +0200 Subject: [PATCH 24/24] Handle animated emoji on mouse hover in CWs and poll options --- .../flavours/glitch/components/status_content.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/javascript/flavours/glitch/components/status_content.js b/app/javascript/flavours/glitch/components/status_content.js index 6e357fba0f..650b834def 100644 --- a/app/javascript/flavours/glitch/components/status_content.js +++ b/app/javascript/flavours/glitch/components/status_content.js @@ -226,7 +226,7 @@ export default class StatusContent extends React.PureComponent { } return ( -
+
-
+
{media}
);