diff --git a/.circleci/config.yml b/.circleci/config.yml
index ff8eb485986..83a2088d70f 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -3,7 +3,7 @@ version: 2
aliases:
- &defaults
docker:
- - image: circleci/ruby:2.6-stretch-node
+ - image: circleci/ruby:2.6-buster-node
environment: &ruby_environment
BUNDLE_APP_CONFIG: ./.bundle/
DB_HOST: localhost
@@ -39,7 +39,6 @@ aliases:
steps:
- checkout
- *attach_workspace
-
- restore_cache:
keys:
- v1-node-dependencies-{{ checksum "yarn.lock" }}
@@ -49,7 +48,6 @@ aliases:
key: v1-node-dependencies-{{ checksum "yarn.lock" }}
paths:
- ./node_modules/
-
- *persist_to_workspace
- &install_system_dependencies
@@ -58,13 +56,17 @@ aliases:
command: |
sudo apt-get update
sudo apt-get install -y libicu-dev libidn11-dev libprotobuf-dev protobuf-compiler
+
+ ## TODO: FIX THESE BUSTER DEPENDANCES
+ sudo wget http://ftp.au.debian.org/debian/pool/main/i/icu/libicu57_57.1-6+deb9u3_amd64.deb
+ sudo dpkg -i libicu57_57.1-6+deb9u3_amd64.deb
+ sudo wget http://ftp.au.debian.org/debian/pool/main/p/protobuf/libprotobuf10_3.0.0-9_amd64.deb
+ sudo dpkg -i libprotobuf10_3.0.0-9_amd64.deb
- &install_ruby_dependencies
steps:
- *attach_workspace
-
- *install_system_dependencies
-
- run: ruby -e 'puts RUBY_VERSION' | tee /tmp/.ruby-version
- *restore_ruby_dependencies
- run: bundle install --clean --jobs 16 --path ./vendor/bundle/ --retry 3 --with pam_authentication --without development production && bundle clean
@@ -82,10 +84,8 @@ aliases:
- &test_steps
steps:
- *attach_workspace
-
- *install_system_dependencies
- run: sudo apt-get install -y ffmpeg
-
- run:
name: Prepare Tests
command: ./bin/rails parallel:create parallel:load_schema parallel:prepare
@@ -105,14 +105,14 @@ jobs:
install-ruby2.5:
<<: *defaults
docker:
- - image: circleci/ruby:2.5-stretch-node
+ - image: circleci/ruby:2.5-buster-node
environment: *ruby_environment
<<: *install_ruby_dependencies
install-ruby2.4:
<<: *defaults
docker:
- - image: circleci/ruby:2.4-stretch-node
+ - image: circleci/ruby:2.4-buster-node
environment: *ruby_environment
<<: *install_ruby_dependencies
@@ -131,7 +131,7 @@ jobs:
test-ruby2.6:
<<: *defaults
docker:
- - image: circleci/ruby:2.6-stretch-node
+ - image: circleci/ruby:2.6-buster-node
environment: *ruby_environment
- image: circleci/postgres:10.6-alpine
environment:
@@ -142,7 +142,7 @@ jobs:
test-ruby2.5:
<<: *defaults
docker:
- - image: circleci/ruby:2.5-stretch-node
+ - image: circleci/ruby:2.5-buster-node
environment: *ruby_environment
- image: circleci/postgres:10.6-alpine
environment:
@@ -153,7 +153,7 @@ jobs:
test-ruby2.4:
<<: *defaults
docker:
- - image: circleci/ruby:2.4-stretch-node
+ - image: circleci/ruby:2.4-buster-node
environment: *ruby_environment
- image: circleci/postgres:10.6-alpine
environment:
@@ -164,7 +164,7 @@ jobs:
test-webui:
<<: *defaults
docker:
- - image: circleci/node:12.9-stretch
+ - image: circleci/node:12-buster
steps:
- *attach_workspace
- run: ./bin/retry yarn test:jest
diff --git a/Dockerfile b/Dockerfile
index 117727d08da..cc75bd6beee 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -4,7 +4,7 @@ FROM ubuntu:18.04 as build-dep
SHELL ["bash", "-c"]
# Install Node v12 (LTS)
-ENV NODE_VER="12.13.1"
+ENV NODE_VER="12.14.0"
RUN echo "Etc/UTC" > /etc/localtime && \
apt update && \
apt -y install wget python && \
diff --git a/Gemfile b/Gemfile
index bb4e65fdc2d..a0ad8e46482 100644
--- a/Gemfile
+++ b/Gemfile
@@ -7,11 +7,11 @@ gem 'pkg-config', '~> 1.4'
gem 'puma', '~> 4.3'
gem 'rails', '~> 5.2.4'
-gem 'sprockets', '~> 3.7'
+gem 'sprockets', '~> 3.7.2'
gem 'thor', '~> 0.20'
gem 'hamlit-rails', '~> 0.2'
-gem 'pg', '~> 1.1'
+gem 'pg', '~> 1.2'
gem 'makara', '~> 0.4'
gem 'pghero', '~> 2.4'
gem 'dotenv-rails', '~> 2.7'
@@ -31,7 +31,7 @@ gem 'browser'
gem 'charlock_holmes', '~> 0.7.7'
gem 'iso-639'
gem 'chewy', '~> 5.1'
-gem 'cld3', '~> 3.2.4'
+gem 'cld3', '~> 3.2.6'
gem 'devise', '~> 4.7'
gem 'devise-two-factor', '~> 3.1'
@@ -50,7 +50,7 @@ gem 'fast_blank', '~> 1.0'
gem 'fastimage'
gem 'goldfinger', '~> 2.1'
gem 'hiredis', '~> 0.6'
-gem 'redis-namespace', '~> 1.5'
+gem 'redis-namespace', '~> 1.7'
gem 'health_check', git: 'https://github.com/ianheggie/health_check', ref: '0b799ead604f900ed50685e9b2d469cd2befba5b'
gem 'html2text'
gem 'htmlentities', '~> 4.3'
@@ -61,7 +61,7 @@ gem 'httplog', '~> 1.3'
gem 'idn-ruby', require: 'idn'
gem 'kaminari', '~> 1.1'
gem 'link_header', '~> 0.0'
-gem 'mime-types', '~> 3.3', require: 'mime/types/columnar'
+gem 'mime-types', '~> 3.3.1', require: 'mime/types/columnar'
gem 'nilsimsa', git: 'https://github.com/witgo/nilsimsa', ref: 'fd184883048b922b176939f851338d0a4971a532'
gem 'nokogiri', '~> 1.10'
gem 'nsa', '~> 0.2'
@@ -120,8 +120,8 @@ end
group :test do
gem 'capybara', '~> 3.29'
gem 'climate_control', '~> 0.2'
- gem 'faker', '~> 2.9'
- gem 'microformats', '~> 4.1'
+ gem 'faker', '~> 2.10'
+ gem 'microformats', '~> 4.2'
gem 'rails-controller-testing', '~> 1.0'
gem 'rspec-sidekiq', '~> 3.0'
gem 'simplecov', '~> 0.17', require: false
diff --git a/Gemfile.lock b/Gemfile.lock
index cfa4201fde7..fec95d27f9d 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -185,10 +185,10 @@ GEM
crack (0.4.3)
safe_yaml (~> 1.0.0)
crass (1.0.5)
- css_parser (1.7.0)
+ css_parser (1.7.1)
addressable
debug_inspector (0.0.3)
- derailed_benchmarks (1.4.2)
+ derailed_benchmarks (1.4.3)
benchmark-ips (~> 2)
get_process_mem (~> 0)
heapy (~> 0)
@@ -240,9 +240,9 @@ GEM
tzinfo
excon (0.71.0)
fabrication (2.21.0)
- faker (2.9.0)
+ faker (2.10.0)
i18n (>= 1.6, < 1.8)
- faraday (0.15.4)
+ faraday (1.0.0)
multipart-post (>= 1.2, < 3)
fast_blank (1.0.0)
fastimage (2.1.7)
@@ -275,8 +275,8 @@ GEM
http (~> 3.0)
nokogiri (~> 1.8)
oj (~> 3.0)
- hamlit (2.9.3)
- temple (>= 0.8.0)
+ hamlit (2.11.0)
+ temple (>= 0.8.2)
thor
tilt
hamlit-rails (0.2.3)
@@ -374,9 +374,9 @@ GEM
microformats (4.1.0)
json (~> 2.1)
nokogiri (~> 1.8, >= 1.8.3)
- mime-types (3.3)
+ mime-types (3.3.1)
mime-types-data (~> 3.2015)
- mime-types-data (3.2019.0904)
+ mime-types-data (3.2019.1009)
mimemagic (0.3.3)
mini_mime (1.0.2)
mini_portile2 (2.4.0)
@@ -434,7 +434,7 @@ GEM
pastel (0.7.3)
equatable (~> 0.6)
tty-color (~> 0.5)
- pg (1.1.4)
+ pg (1.2.0)
pghero (2.4.1)
activerecord (>= 5)
pkg-config (1.4.0)
@@ -454,7 +454,7 @@ GEM
pry (~> 0.10)
pry-rails (0.3.9)
pry (>= 0.10.4)
- public_suffix (4.0.1)
+ public_suffix (4.0.2)
puma (4.3.1)
nio4r (~> 2.0)
pundit (2.1.0)
@@ -463,7 +463,7 @@ GEM
rack (2.0.8)
rack-attack (6.2.2)
rack (>= 1.0, < 3)
- rack-cors (1.1.0)
+ rack-cors (1.1.1)
rack (>= 2.0.0)
rack-protection (2.0.7)
rack
@@ -520,7 +520,7 @@ GEM
redis-activesupport (5.0.4)
activesupport (>= 3, < 6)
redis-store (>= 1.3, < 2)
- redis-namespace (1.6.0)
+ redis-namespace (1.7.0)
redis (>= 3.0.4)
redis-rack (2.0.4)
rack (>= 1.5, < 3)
@@ -532,7 +532,7 @@ GEM
redis-store (1.5.0)
redis (>= 2.2, < 5)
regexp_parser (1.6.0)
- request_store (1.4.1)
+ request_store (1.5.0)
rack (>= 1.4)
responders (3.0.0)
actionpack (>= 5.0)
@@ -618,21 +618,21 @@ GEM
sshkit (1.20.0)
net-scp (>= 1.1.2)
net-ssh (>= 2.8.0)
- stackprof (0.2.14)
+ stackprof (0.2.15)
statsd-ruby (1.4.0)
stoplight (2.2.0)
streamio-ffmpeg (3.0.2)
multi_json (~> 1.8)
strong_migrations (0.5.1)
activerecord (>= 5)
- temple (0.8.1)
+ temple (0.8.2)
terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1)
terrapin (0.6.0)
climate_control (>= 0.0.3, < 1.0)
thor (0.20.3)
thread_safe (0.3.6)
- tilt (2.0.9)
+ tilt (2.0.10)
tty-color (0.5.0)
tty-command (0.9.0)
pastel (~> 0.7.0)
@@ -654,7 +654,7 @@ GEM
tzinfo (>= 1.0.0)
unf (0.1.4)
unf_ext
- unf_ext (0.0.7.5)
+ unf_ext (0.0.7.6)
unicode-display_width (1.6.0)
uniform_notifier (1.12.1)
warden (1.2.8)
@@ -713,7 +713,7 @@ DEPENDENCIES
doorkeeper (~> 5.2)
dotenv-rails (~> 2.7)
fabrication (~> 2.21)
- faker (~> 2.9)
+ faker (~> 2.10)
fast_blank (~> 1.0)
fastimage
fog-core (<= 2.1.0)
@@ -759,7 +759,7 @@ DEPENDENCIES
parallel (~> 1.19)
parallel_tests (~> 2.30)
parslet
- pg (~> 1.1)
+ pg (~> 1.2)
pghero (~> 2.4)
pkg-config (~> 1.4)
posix-spawn!
@@ -778,7 +778,7 @@ DEPENDENCIES
rdf-normalize (~> 0.3)
redcarpet (~> 3.4)
redis (~> 4.1)
- redis-namespace (~> 1.5)
+ redis-namespace (~> 1.7)
redis-rails (~> 5.0)
rqrcode (~> 0.10)
rspec-rails (~> 3.9)
@@ -808,9 +808,3 @@ DEPENDENCIES
webmock (~> 3.7)
webpacker (~> 4.2)
webpush
-
-RUBY VERSION
- ruby 2.6.5p114
-
-BUNDLED WITH
- 1.17.3
diff --git a/app/controllers/admin/custom_emojis_controller.rb b/app/controllers/admin/custom_emojis_controller.rb
index 2af90f0513c..a446465c93f 100644
--- a/app/controllers/admin/custom_emojis_controller.rb
+++ b/app/controllers/admin/custom_emojis_controller.rb
@@ -2,10 +2,6 @@
module Admin
class CustomEmojisController < BaseController
- include ObfuscateFilename
-
- obfuscate_filename [:custom_emoji, :image]
-
def index
authorize :custom_emoji, :index?
diff --git a/app/controllers/api/v1/accounts/follower_accounts_controller.rb b/app/controllers/api/v1/accounts/follower_accounts_controller.rb
index 2dabb8398c1..e360b8a9296 100644
--- a/app/controllers/api/v1/accounts/follower_accounts_controller.rb
+++ b/app/controllers/api/v1/accounts/follower_accounts_controller.rb
@@ -21,11 +21,13 @@ class Api::V1::Accounts::FollowerAccountsController < Api::BaseController
def load_accounts
return [] if hide_results?
- default_accounts.merge(paginated_follows).to_a
+ scope = default_accounts
+ scope = scope.where.not(id: current_account.excluded_from_timeline_account_ids) unless current_account.nil?
+ scope.merge(paginated_follows).to_a
end
def hide_results?
- (@account.user_hides_network? && current_account.id != @account.id) || (current_account && @account.blocking?(current_account))
+ (@account.user_hides_network? && current_account&.id != @account.id) || (current_account && @account.blocking?(current_account))
end
def default_accounts
diff --git a/app/controllers/api/v1/accounts/following_accounts_controller.rb b/app/controllers/api/v1/accounts/following_accounts_controller.rb
index 44e89804b37..a405b365f2c 100644
--- a/app/controllers/api/v1/accounts/following_accounts_controller.rb
+++ b/app/controllers/api/v1/accounts/following_accounts_controller.rb
@@ -21,11 +21,13 @@ class Api::V1::Accounts::FollowingAccountsController < Api::BaseController
def load_accounts
return [] if hide_results?
- default_accounts.merge(paginated_follows).to_a
+ scope = default_accounts
+ scope = scope.where.not(id: current_account.excluded_from_timeline_account_ids) unless current_account.nil?
+ scope.merge(paginated_follows).to_a
end
def hide_results?
- (@account.user_hides_network? && current_account.id != @account.id) || (current_account && @account.blocking?(current_account))
+ (@account.user_hides_network? && current_account&.id != @account.id) || (current_account && @account.blocking?(current_account))
end
def default_accounts
diff --git a/app/controllers/api/v1/media_controller.rb b/app/controllers/api/v1/media_controller.rb
index aaa93b61583..81825db1552 100644
--- a/app/controllers/api/v1/media_controller.rb
+++ b/app/controllers/api/v1/media_controller.rb
@@ -4,9 +4,6 @@ class Api::V1::MediaController < Api::BaseController
before_action -> { doorkeeper_authorize! :write, :'write:media' }
before_action :require_user!
- include ObfuscateFilename
- obfuscate_filename :file
-
respond_to :json
def create
diff --git a/app/controllers/api/v1/statuses/favourited_by_accounts_controller.rb b/app/controllers/api/v1/statuses/favourited_by_accounts_controller.rb
index 657e5783198..99eff360ec8 100644
--- a/app/controllers/api/v1/statuses/favourited_by_accounts_controller.rb
+++ b/app/controllers/api/v1/statuses/favourited_by_accounts_controller.rb
@@ -17,7 +17,9 @@ class Api::V1::Statuses::FavouritedByAccountsController < Api::BaseController
private
def load_accounts
- default_accounts.merge(paginated_favourites).to_a
+ scope = default_accounts
+ scope = scope.where.not(id: current_account.excluded_from_timeline_account_ids) unless current_account.nil?
+ scope.merge(paginated_favourites).to_a
end
def default_accounts
diff --git a/app/controllers/api/v1/statuses/reblogged_by_accounts_controller.rb b/app/controllers/api/v1/statuses/reblogged_by_accounts_controller.rb
index 6851099f661..cc285ad2318 100644
--- a/app/controllers/api/v1/statuses/reblogged_by_accounts_controller.rb
+++ b/app/controllers/api/v1/statuses/reblogged_by_accounts_controller.rb
@@ -17,7 +17,9 @@ class Api::V1::Statuses::RebloggedByAccountsController < Api::BaseController
private
def load_accounts
- default_accounts.merge(paginated_statuses).to_a
+ scope = default_accounts
+ scope = scope.where.not(id: current_account.excluded_from_timeline_account_ids) unless current_account.nil?
+ scope.merge(paginated_statuses).to_a
end
def default_accounts
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index f1a4f0d0274..c882d40ab0f 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -25,6 +25,7 @@ class ApplicationController < ActionController::Base
rescue_from ActionController::InvalidAuthenticityToken, with: :unprocessable_entity
rescue_from ActionController::UnknownFormat, with: :not_acceptable
rescue_from ActionController::ParameterMissing, with: :bad_request
+ rescue_from Paperclip::AdapterRegistry::NoHandlerError, with: :bad_request
rescue_from ActiveRecord::RecordNotFound, with: :not_found
rescue_from Mastodon::NotPermittedError, with: :forbidden
rescue_from HTTP::Error, OpenSSL::SSL::SSLError, with: :internal_server_error
@@ -211,7 +212,12 @@ class ApplicationController < ActionController::Base
end
def respond_with_error(code)
- use_pack 'error'
- render "errors/#{code}", layout: 'error', status: code, formats: [:html]
+ respond_to do |format|
+ format.any do
+ use_pack 'error'
+ render "errors/#{code}", layout: 'error', status: code, formats: [:html]
+ end
+ format.json { render json: { error: Rack::Utils::HTTP_STATUS_CODES[code] }, status: code }
+ end
end
end
diff --git a/app/controllers/auth/registrations_controller.rb b/app/controllers/auth/registrations_controller.rb
index 068375843c2..a9d075a451e 100644
--- a/app/controllers/auth/registrations_controller.rb
+++ b/app/controllers/auth/registrations_controller.rb
@@ -11,6 +11,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController
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]
+ before_action :set_cache_headers, only: [:edit, :update]
skip_before_action :require_functional!, only: [:edit, :update]
@@ -114,4 +115,8 @@ class Auth::RegistrationsController < Devise::RegistrationsController
def require_not_suspended!
forbidden if current_account.suspended?
end
+
+ def set_cache_headers
+ response.headers['Cache-Control'] = 'no-cache, no-store, max-age=0, must-revalidate'
+ end
end
diff --git a/app/controllers/concerns/obfuscate_filename.rb b/app/controllers/concerns/obfuscate_filename.rb
deleted file mode 100644
index 22736ec3abf..00000000000
--- a/app/controllers/concerns/obfuscate_filename.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-# frozen_string_literal: true
-
-module ObfuscateFilename
- extend ActiveSupport::Concern
-
- class_methods do
- def obfuscate_filename(path)
- before_action do
- file = params.dig(*path)
- next if file.nil?
-
- file.original_filename = SecureRandom.hex(8) + File.extname(file.original_filename)
- end
- end
- end
-end
diff --git a/app/controllers/filters_controller.rb b/app/controllers/filters_controller.rb
index f1e110d87dd..76be03e5376 100644
--- a/app/controllers/filters_controller.rb
+++ b/app/controllers/filters_controller.rb
@@ -1,10 +1,9 @@
# frozen_string_literal: true
class FiltersController < ApplicationController
- include Authorization
-
layout 'admin'
+ before_action :authenticate_user!
before_action :set_filters, only: :index
before_action :set_filter, only: [:edit, :update, :destroy]
before_action :set_pack
diff --git a/app/controllers/follower_accounts_controller.rb b/app/controllers/follower_accounts_controller.rb
index df46f5f7209..a5dfffd6dd3 100644
--- a/app/controllers/follower_accounts_controller.rb
+++ b/app/controllers/follower_accounts_controller.rb
@@ -19,7 +19,6 @@ class FollowerAccountsController < ApplicationController
next if @account.user_hides_network?
follows
- @relationships = AccountRelationshipsPresenter.new(follows.map(&:account_id), current_user.account_id) if user_signed_in?
end
format.json do
@@ -38,7 +37,11 @@ class FollowerAccountsController < ApplicationController
private
def follows
- @follows ||= Follow.where(target_account: @account).recent.page(params[:page]).per(FOLLOW_PER_PAGE).preload(:account)
+ return @follows if defined?(@follows)
+
+ scope = Follow.where(target_account: @account)
+ scope = scope.where.not(account_id: current_account.excluded_from_timeline_account_ids) if user_signed_in?
+ @follows = scope.recent.page(params[:page]).per(FOLLOW_PER_PAGE).preload(:account)
end
def page_requested?
diff --git a/app/controllers/following_accounts_controller.rb b/app/controllers/following_accounts_controller.rb
index 8cab67ff55b..ff23d97f96e 100644
--- a/app/controllers/following_accounts_controller.rb
+++ b/app/controllers/following_accounts_controller.rb
@@ -19,7 +19,6 @@ class FollowingAccountsController < ApplicationController
next if @account.user_hides_network?
follows
- @relationships = AccountRelationshipsPresenter.new(follows.map(&:target_account_id), current_user.account_id) if user_signed_in?
end
format.json do
@@ -38,7 +37,11 @@ class FollowingAccountsController < ApplicationController
private
def follows
- @follows ||= Follow.where(account: @account).recent.page(params[:page]).per(FOLLOW_PER_PAGE).preload(:target_account)
+ return @follows if defined?(@follows)
+
+ scope = Follow.where(account: @account)
+ scope = scope.where.not(target_account_id: current_account.excluded_from_timeline_account_ids) if user_signed_in?
+ @follows = scope.recent.page(params[:page]).per(FOLLOW_PER_PAGE).preload(:target_account)
end
def page_requested?
diff --git a/app/controllers/oauth/authorizations_controller.rb b/app/controllers/oauth/authorizations_controller.rb
index f6f5d1eccb4..137346ed098 100644
--- a/app/controllers/oauth/authorizations_controller.rb
+++ b/app/controllers/oauth/authorizations_controller.rb
@@ -6,6 +6,7 @@ class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
before_action :store_current_location
before_action :authenticate_resource_owner!
before_action :set_pack
+ before_action :set_cache_headers
include Localized
@@ -32,4 +33,8 @@ class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
def truthy_param?(key)
ActiveModel::Type::Boolean.new.cast(params[key])
end
+
+ def set_cache_headers
+ response.headers['Cache-Control'] = 'no-cache, no-store, max-age=0, must-revalidate'
+ end
end
diff --git a/app/controllers/settings/base_controller.rb b/app/controllers/settings/base_controller.rb
index 8c394a6d35d..b97603af6f2 100644
--- a/app/controllers/settings/base_controller.rb
+++ b/app/controllers/settings/base_controller.rb
@@ -3,6 +3,7 @@
class Settings::BaseController < ApplicationController
before_action :set_pack
before_action :set_body_classes
+ before_action :set_cache_headers
private
@@ -13,4 +14,8 @@ class Settings::BaseController < ApplicationController
def set_body_classes
@body_classes = 'admin'
end
+
+ def set_cache_headers
+ response.headers['Cache-Control'] = 'no-cache, no-store, max-age=0, must-revalidate'
+ end
end
diff --git a/app/controllers/settings/profiles_controller.rb b/app/controllers/settings/profiles_controller.rb
index 8b640cdca1c..19a7ce157f0 100644
--- a/app/controllers/settings/profiles_controller.rb
+++ b/app/controllers/settings/profiles_controller.rb
@@ -1,16 +1,11 @@
# frozen_string_literal: true
class Settings::ProfilesController < Settings::BaseController
- include ObfuscateFilename
-
layout 'admin'
before_action :authenticate_user!
before_action :set_account
- obfuscate_filename [:account, :avatar]
- obfuscate_filename [:account, :header]
-
def show
@account.build_fields
end
diff --git a/app/controllers/well_known/host_meta_controller.rb b/app/controllers/well_known/host_meta_controller.rb
index 2e9298c4ae6..2fd6bc7cc91 100644
--- a/app/controllers/well_known/host_meta_controller.rb
+++ b/app/controllers/well_known/host_meta_controller.rb
@@ -8,12 +8,8 @@ module WellKnown
def show
@webfinger_template = "#{webfinger_url}?resource={uri}"
-
- respond_to do |format|
- format.xml { render content_type: 'application/xrd+xml' }
- end
-
expires_in 3.days, public: true
+ render content_type: 'application/xrd+xml', formats: [:xml]
end
end
end
diff --git a/app/helpers/accounts_helper.rb b/app/helpers/accounts_helper.rb
index 7fcc4e8162b..986fd1805c2 100644
--- a/app/helpers/accounts_helper.rb
+++ b/app/helpers/accounts_helper.rb
@@ -13,7 +13,7 @@ module AccountsHelper
if account.local?
"@#{account.acct}@#{Rails.configuration.x.local_domain}"
else
- "@#{account.acct}"
+ "@#{account.pretty_acct}"
end
end
diff --git a/app/javascript/flavours/glitch/styles/components/accounts.scss b/app/javascript/flavours/glitch/styles/components/accounts.scss
index 5be4da48a81..6305e2a4d0d 100644
--- a/app/javascript/flavours/glitch/styles/components/accounts.scss
+++ b/app/javascript/flavours/glitch/styles/components/accounts.scss
@@ -681,13 +681,13 @@
&__links {
font-size: 14px;
color: $darker-text-color;
+ padding: 10px 0;
a {
display: inline-block;
color: $darker-text-color;
text-decoration: none;
- padding: 10px;
- padding-top: 20px;
+ padding: 5px 10px;
font-weight: 500;
strong {
diff --git a/app/javascript/flavours/glitch/util/numbers.js b/app/javascript/flavours/glitch/util/numbers.js
index f7e4ceb9354..af18dcfddb2 100644
--- a/app/javascript/flavours/glitch/util/numbers.js
+++ b/app/javascript/flavours/glitch/util/numbers.js
@@ -4,9 +4,13 @@ import { FormattedNumber } from 'react-intl';
export const shortNumberFormat = number => {
if (number < 1000) {
return ;
- } else if (number < 1000000) {
+ } else if (number < 10000) {
return K;
- } else {
+ } else if (number < 1000000) {
+ return K;
+ } else if (number < 10000000) {
return M;
+ } else {
+ return M;
}
};
diff --git a/app/javascript/mastodon/actions/statuses.js b/app/javascript/mastodon/actions/statuses.js
index 06a19afc3ad..5640201c621 100644
--- a/app/javascript/mastodon/actions/statuses.js
+++ b/app/javascript/mastodon/actions/statuses.js
@@ -26,8 +26,9 @@ export const STATUS_UNMUTE_REQUEST = 'STATUS_UNMUTE_REQUEST';
export const STATUS_UNMUTE_SUCCESS = 'STATUS_UNMUTE_SUCCESS';
export const STATUS_UNMUTE_FAIL = 'STATUS_UNMUTE_FAIL';
-export const STATUS_REVEAL = 'STATUS_REVEAL';
-export const STATUS_HIDE = 'STATUS_HIDE';
+export const STATUS_REVEAL = 'STATUS_REVEAL';
+export const STATUS_HIDE = 'STATUS_HIDE';
+export const STATUS_COLLAPSE = 'STATUS_COLLAPSE';
export const REDRAFT = 'REDRAFT';
@@ -320,3 +321,11 @@ export function revealStatus(ids) {
ids,
};
};
+
+export function toggleStatusCollapse(id, isCollapsed) {
+ return {
+ type: STATUS_COLLAPSE,
+ id,
+ isCollapsed,
+ };
+}
diff --git a/app/javascript/mastodon/components/scrollable_list.js b/app/javascript/mastodon/components/scrollable_list.js
index 421756803c9..47a87b149a2 100644
--- a/app/javascript/mastodon/components/scrollable_list.js
+++ b/app/javascript/mastodon/components/scrollable_list.js
@@ -208,10 +208,13 @@ export default class ScrollableList extends PureComponent {
}
attachIntersectionObserver () {
- this.intersectionObserverWrapper.connect({
+ let nodeOptions = {
root: this.node,
rootMargin: '300% 0px',
- });
+ };
+
+ this.intersectionObserverWrapper
+ .connect(this.props.bindToDocument ? {} : nodeOptions);
}
detachIntersectionObserver () {
diff --git a/app/javascript/mastodon/components/status.js b/app/javascript/mastodon/components/status.js
index e120278a05b..0dc00cb98d5 100644
--- a/app/javascript/mastodon/components/status.js
+++ b/app/javascript/mastodon/components/status.js
@@ -76,6 +76,7 @@ class Status extends ImmutablePureComponent {
onEmbed: PropTypes.func,
onHeightChange: PropTypes.func,
onToggleHidden: PropTypes.func,
+ onToggleCollapsed: PropTypes.func,
muted: PropTypes.bool,
hidden: PropTypes.bool,
unread: PropTypes.bool,
@@ -102,19 +103,6 @@ class Status extends ImmutablePureComponent {
statusId: undefined,
};
- // Track height changes we know about to compensate scrolling
- componentDidMount () {
- this.didShowCard = !this.props.muted && !this.props.hidden && this.props.status && this.props.status.get('card');
- }
-
- getSnapshotBeforeUpdate () {
- if (this.props.getScrollPosition) {
- return this.props.getScrollPosition();
- } else {
- return null;
- }
- }
-
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.status && nextProps.status.get('id') !== prevState.statusId) {
return {
@@ -126,32 +114,6 @@ class Status extends ImmutablePureComponent {
}
}
- // Compensate height changes
- componentDidUpdate (prevProps, prevState, snapshot) {
- const doShowCard = !this.props.muted && !this.props.hidden && this.props.status && this.props.status.get('card');
-
- if (doShowCard && !this.didShowCard) {
- this.didShowCard = true;
-
- if (snapshot !== null && this.props.updateScrollBottom) {
- if (this.node && this.node.offsetTop < snapshot.top) {
- this.props.updateScrollBottom(snapshot.height - snapshot.top);
- }
- }
- }
- }
-
- componentWillUnmount() {
- if (this.node && this.props.getScrollPosition) {
- const position = this.props.getScrollPosition();
- if (position !== null && this.node.offsetTop < position.top) {
- requestAnimationFrame(() => {
- this.props.updateScrollBottom(position.height - position.top);
- });
- }
- }
- }
-
handleToggleMediaVisibility = () => {
this.setState({ showMedia: !this.state.showMedia });
}
@@ -196,7 +158,11 @@ class Status extends ImmutablePureComponent {
handleExpandedToggle = () => {
this.props.onToggleHidden(this._properStatus());
- };
+ }
+
+ handleCollapsedToggle = isCollapsed => {
+ this.props.onToggleCollapsed(this._properStatus(), isCollapsed);
+ }
renderLoadingMediaGallery () {
return
;
@@ -466,7 +432,7 @@ class Status extends ImmutablePureComponent {
-
+
{media}
diff --git a/app/javascript/mastodon/components/status_content.js b/app/javascript/mastodon/components/status_content.js
index d13091325be..5d921fd412f 100644
--- a/app/javascript/mastodon/components/status_content.js
+++ b/app/javascript/mastodon/components/status_content.js
@@ -23,11 +23,11 @@ export default class StatusContent extends React.PureComponent {
onExpandedToggle: PropTypes.func,
onClick: PropTypes.func,
collapsable: PropTypes.bool,
+ onCollapsedToggle: PropTypes.func,
};
state = {
hidden: true,
- collapsed: null, // `collapsed: null` indicates that an element doesn't need collapsing, while `true` or `false` indicates that it does (and is/isn't).
};
_updateStatusLinks () {
@@ -62,14 +62,16 @@ export default class StatusContent extends React.PureComponent {
link.setAttribute('rel', 'noopener noreferrer');
}
- if (
- this.props.collapsable
- && this.props.onClick
- && this.state.collapsed === null
- && node.clientHeight > MAX_HEIGHT
- && this.props.status.get('spoiler_text').length === 0
- ) {
- this.setState({ collapsed: true });
+ if (this.props.status.get('collapsed', null) === null) {
+ let collapsed =
+ this.props.collapsable
+ && this.props.onClick
+ && node.clientHeight > MAX_HEIGHT
+ && this.props.status.get('spoiler_text').length === 0;
+
+ if(this.props.onCollapsedToggle) this.props.onCollapsedToggle(collapsed);
+
+ this.props.status.set('collapsed', collapsed);
}
}
@@ -178,6 +180,7 @@ export default class StatusContent extends React.PureComponent {
}
const hidden = this.props.onExpandedToggle ? !this.props.expanded : this.state.hidden;
+ const renderReadMore = this.props.onClick && status.get('collapsed');
const content = { __html: status.get('contentHtml') };
const spoilerContent = { __html: status.get('spoilerHtml') };
@@ -185,7 +188,7 @@ export default class StatusContent extends React.PureComponent {
const classNames = classnames('status__content', {
'status__content--with-action': this.props.onClick && this.context.router,
'status__content--with-spoiler': status.get('spoiler_text').length > 0,
- 'status__content--collapsed': this.state.collapsed === true,
+ 'status__content--collapsed': renderReadMore,
});
if (isRtl(status.get('search_index'))) {
@@ -237,7 +240,7 @@ export default class StatusContent extends React.PureComponent {
,
];
- if (this.state.collapsed) {
+ if (renderReadMore) {
output.push(readMoreButton);
}
diff --git a/app/javascript/mastodon/containers/status_container.js b/app/javascript/mastodon/containers/status_container.js
index 35c16a20ca6..2ba3a3123da 100644
--- a/app/javascript/mastodon/containers/status_container.js
+++ b/app/javascript/mastodon/containers/status_container.js
@@ -23,6 +23,7 @@ import {
deleteStatus,
hideStatus,
revealStatus,
+ toggleStatusCollapse,
} from '../actions/statuses';
import {
unmuteAccount,
@@ -190,6 +191,10 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
}
},
+ onToggleCollapsed (status, isCollapsed) {
+ dispatch(toggleStatusCollapse(status.get('id'), isCollapsed));
+ },
+
onBlockDomain (domain) {
dispatch(openModal('CONFIRM', {
message: {domain} }} />,
diff --git a/app/javascript/mastodon/reducers/statuses.js b/app/javascript/mastodon/reducers/statuses.js
index 772f98bcbf5..398a48cff8f 100644
--- a/app/javascript/mastodon/reducers/statuses.js
+++ b/app/javascript/mastodon/reducers/statuses.js
@@ -12,6 +12,7 @@ import {
STATUS_UNMUTE_SUCCESS,
STATUS_REVEAL,
STATUS_HIDE,
+ STATUS_COLLAPSE,
} from '../actions/statuses';
import { TIMELINE_DELETE } from '../actions/timelines';
import { STATUS_IMPORT, STATUSES_IMPORT } from '../actions/importer';
@@ -73,6 +74,8 @@ export default function statuses(state = initialState, action) {
}
});
});
+ case STATUS_COLLAPSE:
+ return state.setIn([action.id, 'collapsed'], action.isCollapsed);
case TIMELINE_DELETE:
return deleteStatus(state, action.id, action.references);
default:
diff --git a/app/javascript/mastodon/utils/numbers.js b/app/javascript/mastodon/utils/numbers.js
index f7e4ceb9354..af18dcfddb2 100644
--- a/app/javascript/mastodon/utils/numbers.js
+++ b/app/javascript/mastodon/utils/numbers.js
@@ -4,9 +4,13 @@ import { FormattedNumber } from 'react-intl';
export const shortNumberFormat = number => {
if (number < 1000) {
return ;
- } else if (number < 1000000) {
+ } else if (number < 10000) {
return K;
- } else {
+ } else if (number < 1000000) {
+ return K;
+ } else if (number < 10000000) {
return M;
+ } else {
+ return M;
}
};
diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss
index 01a633c5fb8..1a5d9e3e31b 100644
--- a/app/javascript/styles/mastodon/components.scss
+++ b/app/javascript/styles/mastodon/components.scss
@@ -6399,13 +6399,13 @@ noscript {
&__links {
font-size: 14px;
color: $darker-text-color;
+ padding: 10px 0;
a {
display: inline-block;
color: $darker-text-color;
text-decoration: none;
- padding: 10px;
- padding-top: 20px;
+ padding: 5px 10px;
font-weight: 500;
strong {
diff --git a/app/lib/search_query_transformer.rb b/app/lib/search_query_transformer.rb
index 6a299f59d8a..e07ebfffed7 100644
--- a/app/lib/search_query_transformer.rb
+++ b/app/lib/search_query_transformer.rb
@@ -78,7 +78,7 @@ class SearchQueryTransformer < Parslet::Transform
elsif clause[:shortcode]
TermClause.new(prefix, operator, ":#{clause[:term]}:")
elsif clause[:phrase]
- PhraseClause.new(prefix, operator, clause[:phrase].map { |p| p[:term].to_s }.join(' '))
+ PhraseClause.new(prefix, operator, clause[:phrase].is_a?(Array) ? clause[:phrase].map { |p| p[:term].to_s }.join(' ') : clause[:phrase].to_s)
else
raise "Unexpected clause type: #{clause}"
end
diff --git a/app/middleware/handle_bad_encoding_middleware.rb b/app/middleware/handle_bad_encoding_middleware.rb
new file mode 100644
index 00000000000..6fce84b1522
--- /dev/null
+++ b/app/middleware/handle_bad_encoding_middleware.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+# See: https://jamescrisp.org/2018/05/28/fixing-invalid-query-parameters-invalid-encoding-in-a-rails-app/
+
+class HandleBadEncodingMiddleware
+ def initialize(app)
+ @app = app
+ end
+
+ def call(env)
+ begin
+ Rack::Utils.parse_nested_query(env['QUERY_STRING'].to_s)
+ rescue Rack::Utils::InvalidParameterError
+ env['QUERY_STRING'] = ''
+ end
+
+ @app.call(env)
+ end
+end
diff --git a/app/models/account.rb b/app/models/account.rb
index 25cde6d6c83..86f7295bceb 100644
--- a/app/models/account.rb
+++ b/app/models/account.rb
@@ -50,7 +50,7 @@
class Account < ApplicationRecord
USERNAME_RE = /[a-z0-9_]+([a-z0-9_\.-]+[a-z0-9_]+)?/i
- MENTION_RE = /(?<=^|[^\/[:word:]])@((#{USERNAME_RE})(?:@[a-z0-9\.\-]+[a-z0-9]+)?)/i
+ MENTION_RE = /(?<=^|[^\/[:word:]])@((#{USERNAME_RE})(?:@[[:word:]\.\-]+[a-z0-9]+)?)/i
include AccountAssociations
include AccountAvatar
@@ -168,6 +168,10 @@ class Account < ApplicationRecord
local? ? username : "#{username}@#{domain}"
end
+ def pretty_acct
+ local? ? username : "#{username}@#{Addressable::IDNA.to_unicode(domain)}"
+ end
+
def local_username_and_domain
"#{username}@#{Rails.configuration.x.local_domain}"
end
diff --git a/app/models/concerns/attachmentable.rb b/app/models/concerns/attachmentable.rb
index 3bbc6453c7f..1e8c4806f67 100644
--- a/app/models/concerns/attachmentable.rb
+++ b/app/models/concerns/attachmentable.rb
@@ -9,6 +9,7 @@ module Attachmentable
GIF_MATRIX_LIMIT = 921_600 # 1280x720px
included do
+ before_post_process :obfuscate_file_name
before_post_process :set_file_extensions
before_post_process :check_image_dimensions
before_post_process :set_file_content_type
@@ -68,4 +69,14 @@ module Attachmentable
rescue Terrapin::CommandLineError
''
end
+
+ def obfuscate_file_name
+ self.class.attachment_definitions.each_key do |attachment_name|
+ attachment = send(attachment_name)
+
+ next if attachment.blank?
+
+ attachment.instance_write :file_name, SecureRandom.hex(8) + File.extname(attachment.instance_read(:file_name))
+ end
+ end
end
diff --git a/app/models/media_attachment.rb b/app/models/media_attachment.rb
index 880599028b5..b87b1b9d33d 100644
--- a/app/models/media_attachment.rb
+++ b/app/models/media_attachment.rb
@@ -202,9 +202,12 @@ class MediaAttachment < ApplicationRecord
end
after_commit :reset_parent_cache, on: :update
+
before_create :prepare_description, unless: :local?
before_create :set_shortcode
+
before_post_process :set_type_and_extension
+
before_save :set_meta
class << self
diff --git a/app/serializers/rest/account_serializer.rb b/app/serializers/rest/account_serializer.rb
index 7bdb5d7fffe..657dd36f3b3 100644
--- a/app/serializers/rest/account_serializer.rb
+++ b/app/serializers/rest/account_serializer.rb
@@ -24,6 +24,10 @@ class REST::AccountSerializer < ActiveModel::Serializer
object.id.to_s
end
+ def acct
+ object.pretty_acct
+ end
+
def note
Formatter.instance.simplified_format(object)
end
diff --git a/app/services/backup_service.rb b/app/services/backup_service.rb
index 0b57b6d0cca..ab6d090a0ca 100644
--- a/app/services/backup_service.rb
+++ b/app/services/backup_service.rb
@@ -166,7 +166,7 @@ class BackupService < BaseService
io.write(buffer)
end
end
- rescue Errno::ENOENT
+ rescue Errno::ENOENT, Seahorse::Client::NetworkingError
Rails.logger.warn "Could not backup file #{filename}: file not found"
end
end
diff --git a/app/services/process_mentions_service.rb b/app/services/process_mentions_service.rb
index 19de377174a..3c257451c9b 100644
--- a/app/services/process_mentions_service.rb
+++ b/app/services/process_mentions_service.rb
@@ -14,7 +14,16 @@ class ProcessMentionsService < BaseService
mentions = []
status.text = status.text.gsub(Account::MENTION_RE) do |match|
- username, domain = Regexp.last_match(1).split('@')
+ username, domain = Regexp.last_match(1).split('@')
+
+ domain = begin
+ if TagManager.instance.local_domain?(domain)
+ nil
+ else
+ TagManager.instance.normalize_domain(domain)
+ end
+ end
+
mentioned_account = Account.find_remote(username, domain)
if mention_undeliverable?(mentioned_account)
diff --git a/app/services/resolve_url_service.rb b/app/services/resolve_url_service.rb
index 79b1bad0c2b..1a2b0d60cbc 100644
--- a/app/services/resolve_url_service.rb
+++ b/app/services/resolve_url_service.rb
@@ -12,6 +12,8 @@ class ResolveURLService < BaseService
process_local_url
elsif !fetched_resource.nil?
process_url
+ elsif @on_behalf_of.present?
+ process_url_from_db
end
end
@@ -24,15 +26,19 @@ class ResolveURLService < BaseService
status = FetchRemoteStatusService.new.call(resource_url, body)
authorize_with @on_behalf_of, status, :show? unless status.nil?
status
- elsif fetched_resource.nil? && @on_behalf_of.present?
- # It may happen that the resource is a private toot, and thus not fetchable,
- # but we can return the toot if we already know about it.
- status = Status.find_by(uri: @url) || Status.find_by(url: @url)
- authorize_with @on_behalf_of, status, :show? unless status.nil?
- status
end
end
+ def process_url_from_db
+ # It may happen that the resource is a private toot, and thus not fetchable,
+ # but we can return the toot if we already know about it.
+ status = Status.find_by(uri: @url) || Status.find_by(url: @url)
+ authorize_with @on_behalf_of, status, :show? unless status.nil?
+ status
+ rescue Mastodon::NotPermittedError
+ nil
+ end
+
def fetched_resource
@fetched_resource ||= FetchResourceService.new.call(@url)
end
diff --git a/app/views/admin/tags/_tag.html.haml b/app/views/admin/tags/_tag.html.haml
index 670f3bc0598..287d28e53b6 100644
--- a/app/views/admin/tags/_tag.html.haml
+++ b/app/views/admin/tags/_tag.html.haml
@@ -1,6 +1,7 @@
.batch-table__row
- %label.batch-table__row__select.batch-table__row__select--aligned.batch-checkbox
- = f.check_box :tag_ids, { multiple: true, include_hidden: false }, tag.id
+ - if batch_available
+ %label.batch-table__row__select.batch-table__row__select--aligned.batch-checkbox
+ = f.check_box :tag_ids, { multiple: true, include_hidden: false }, tag.id
.directory__tag
= link_to admin_tag_path(tag.id) do
diff --git a/app/views/admin/tags/index.html.haml b/app/views/admin/tags/index.html.haml
index 298ac59e9a2..aa4f4c29787 100644
--- a/app/views/admin/tags/index.html.haml
+++ b/app/views/admin/tags/index.html.haml
@@ -47,25 +47,26 @@
.batch-table.optional
.batch-table__toolbar
- %label.batch-table__toolbar__select.batch-checkbox-all
- = check_box_tag :batch_checkbox_all, nil, false
- .batch-table__toolbar__actions
- - if params[:pending_review] == '1'
+ - if params[:pending_review] == '1' || params[:unreviewed] == '1'
+ %label.batch-table__toolbar__select.batch-checkbox-all
+ = check_box_tag :batch_checkbox_all, nil, false
+ .batch-table__toolbar__actions
= f.button safe_join([fa_icon('check'), t('admin.accounts.approve')]), name: :approve, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') }
= f.button safe_join([fa_icon('times'), t('admin.accounts.reject')]), name: :reject, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') }
- - else
+ - else
+ .batch-table__toolbar__actions
%span.neutral-hint= t('generic.no_batch_actions_available')
.batch-table__body
- if @tags.empty?
= nothing_here 'nothing-here--under-tabs'
- else
- = render partial: 'tag', collection: @tags, locals: { f: f }
+ = render partial: 'tag', collection: @tags, locals: { f: f, batch_available: params[:pending_review] == '1' || params[:unreviewed] == '1' }
= paginate @tags
-- if params[:pending_review] == '1'
+- if params[:pending_review] == '1' || params[:unreviewed] == '1'
%hr.spacer/
%div{ style: 'overflow: hidden' }
diff --git a/app/views/settings/preferences/appearance/show.html.haml b/app/views/settings/preferences/appearance/show.html.haml
index f1e3d2e97ef..f460cebba38 100644
--- a/app/views/settings/preferences/appearance/show.html.haml
+++ b/app/views/settings/preferences/appearance/show.html.haml
@@ -5,6 +5,10 @@
.fields-group
= f.input :locale, collection: I18n.available_locales, wrapper: :with_label, include_blank: false, label_method: lambda { |locale| human_locale(locale) }, selected: I18n.locale, hint: false
+ - unless I18n.locale == :en
+ .flash-message{ style: "text-align: unset; color: unset" }
+ #{t 'appearance.localization.body'} #{content_tag(:a, t('appearance.localization.guide_link_text'), href: t('appearance.localization.guide_link'), target: "_blank", rel: "noopener", style: "text-decoration: underline")}
+
%h4= t 'appearance.advanced_web_interface'
%p.hint= t 'appearance.advanced_web_interface_hint'
diff --git a/app/views/statuses/embed.html.haml b/app/views/statuses/embed.html.haml
index 6f2ec646fe6..2f111f53fcc 100644
--- a/app/views/statuses/embed.html.haml
+++ b/app/views/statuses/embed.html.haml
@@ -1,3 +1,2 @@
-- cache @status do
- .activity-stream.activity-stream--headless
- = render 'status', status: @status, centered: true, autoplay: @autoplay
+.activity-stream.activity-stream--headless
+ = render 'status', status: @status, centered: true, autoplay: @autoplay
diff --git a/app/workers/refollow_worker.rb b/app/workers/refollow_worker.rb
index 12f2bf671b8..9b07ce1b56a 100644
--- a/app/workers/refollow_worker.rb
+++ b/app/workers/refollow_worker.rb
@@ -7,15 +7,18 @@ class RefollowWorker
def perform(target_account_id)
target_account = Account.find(target_account_id)
- return unless target_account.protocol == :activitypub
+ return unless target_account.activitypub?
+
+ target_account.passive_relationships.where(account: Account.where(domain: nil)).includes(:account).reorder(nil).find_each do |follow|
+ reblogs = follow.show_reblogs?
- target_account.followers.where(domain: nil).reorder(nil).find_each do |follower|
# Locally unfollow remote account
+ follower = follow.account
follower.unfollow!(target_account)
# Schedule re-follow
begin
- FollowService.new.call(follower, target_account)
+ FollowService.new.call(follower, target_account, reblogs: reblogs)
rescue Mastodon::NotPermittedError, ActiveRecord::RecordNotFound, Mastodon::UnexpectedResponseError, HTTP::Error, OpenSSL::SSL::SSLError
next
end
diff --git a/config/application.rb b/config/application.rb
index e1f7ae707da..58e59fd51b0 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -7,6 +7,7 @@ require 'rails/all'
Bundler.require(*Rails.groups)
require_relative '../app/lib/exceptions'
+require_relative '../app/middleware/handle_bad_encoding_middleware'
require_relative '../lib/paperclip/lazy_thumbnail'
require_relative '../lib/paperclip/gif_transcoder'
require_relative '../lib/paperclip/video_transcoder'
@@ -118,6 +119,7 @@ module Mastodon
config.active_job.queue_adapter = :sidekiq
+ config.middleware.insert_before Rack::Runtime, HandleBadEncodingMiddleware
config.middleware.use Rack::Attack
config.middleware.use Rack::Deflater
diff --git a/config/initializers/doorkeeper.rb b/config/initializers/doorkeeper.rb
index 7784bec6296..e03380cec11 100644
--- a/config/initializers/doorkeeper.rb
+++ b/config/initializers/doorkeeper.rb
@@ -8,20 +8,15 @@ Doorkeeper.configure do
end
resource_owner_from_credentials do |_routes|
- if Devise.ldap_authentication
- user = User.authenticate_with_ldap({ :email => request.params[:username], :password => request.params[:password] })
- end
-
- if Devise.pam_authentication
- user ||= User.authenticate_with_ldap({ :email => request.params[:username], :password => request.params[:password] })
- end
+ user = User.authenticate_with_ldap(email: request.params[:username], password: request.params[:password]) if Devise.ldap_authentication
+ user ||= User.authenticate_with_pam(email: request.params[:username], password: request.params[:password]) if Devise.pam_authentication
if user.nil?
user = User.find_by(email: request.params[:username])
- user = nil unless user.valid_password?(request.params[:password])
+ user = nil unless user&.valid_password?(request.params[:password])
end
- user if !user&.otp_required_for_login?
+ user unless user&.otp_required_for_login?
end
# If you want to restrict access to the web interface for adding oauth authorized applications, you need to declare the block below.
diff --git a/config/initializers/paperclip.rb b/config/initializers/paperclip.rb
index 5109baff701..8909678d653 100644
--- a/config/initializers/paperclip.rb
+++ b/config/initializers/paperclip.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+Paperclip::DataUriAdapter.register
+
Paperclip.interpolates :filename do |attachment, style|
if style == :original
attachment.original_filename
diff --git a/config/initializers/rack_attack.rb b/config/initializers/rack_attack.rb
index 273cac9ca3d..3cd7ea3a6b1 100644
--- a/config/initializers/rack_attack.rb
+++ b/config/initializers/rack_attack.rb
@@ -46,10 +46,7 @@ class Rack::Attack
PROTECTED_PATHS_REGEX = Regexp.union(PROTECTED_PATHS.map { |path| /\A#{Regexp.escape(path)}/ })
- # Always allow requests from localhost
- # (blocklist & throttles are skipped)
Rack::Attack.safelist('allow from localhost') do |req|
- # Requests are allowed if the return value is truthy
req.remote_ip == '127.0.0.1' || req.remote_ip == '::1'
end
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 43c24fc4e1a..74f397a3f82 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -594,6 +594,10 @@ en:
animations_and_accessibility: Animations and accessibility
confirmation_dialogs: Confirmation dialogs
discovery: Discovery
+ localization:
+ body: Mastodon is translated by volunteers.
+ guide_link: https://crowdin.com/project/mastodon
+ guide_link_text: Everyone can contribute.
sensitive_content: Sensitive content
toot_layout: Toot layout
application_mailer:
diff --git a/db/migrate/20170711225116_fix_null_booleans.rb b/db/migrate/20170711225116_fix_null_booleans.rb
index 5b319471d70..aabb81f217b 100644
--- a/db/migrate/20170711225116_fix_null_booleans.rb
+++ b/db/migrate/20170711225116_fix_null_booleans.rb
@@ -1,17 +1,19 @@
class FixNullBooleans < ActiveRecord::Migration[5.1]
def change
- change_column_default :domain_blocks, :reject_media, false
- change_column_null :domain_blocks, :reject_media, false, false
+ safety_assured do
+ change_column_default :domain_blocks, :reject_media, false
+ change_column_null :domain_blocks, :reject_media, false, false
- change_column_default :imports, :approved, false
- change_column_null :imports, :approved, false, false
+ change_column_default :imports, :approved, false
+ change_column_null :imports, :approved, false, false
- change_column_null :statuses, :sensitive, false, false
- change_column_null :statuses, :reply, false, false
+ change_column_null :statuses, :sensitive, false, false
+ change_column_null :statuses, :reply, false, false
- change_column_null :users, :admin, false, false
+ change_column_null :users, :admin, false, false
- change_column_default :users, :otp_required_for_login, false
- change_column_null :users, :otp_required_for_login, false, false
+ change_column_default :users, :otp_required_for_login, false
+ change_column_null :users, :otp_required_for_login, false, false
+ end
end
end
diff --git a/db/migrate/20171010025614_change_accounts_nonnullable_in_account_moderation_notes.rb b/db/migrate/20171010025614_change_accounts_nonnullable_in_account_moderation_notes.rb
index 747e5a82691..1d7a0086c24 100644
--- a/db/migrate/20171010025614_change_accounts_nonnullable_in_account_moderation_notes.rb
+++ b/db/migrate/20171010025614_change_accounts_nonnullable_in_account_moderation_notes.rb
@@ -1,6 +1,8 @@
class ChangeAccountsNonnullableInAccountModerationNotes < ActiveRecord::Migration[5.1]
def change
- change_column_null :account_moderation_notes, :account_id, false
- change_column_null :account_moderation_notes, :target_account_id, false
+ safety_assured do
+ change_column_null :account_moderation_notes, :account_id, false
+ change_column_null :account_moderation_notes, :target_account_id, false
+ end
end
end
diff --git a/db/migrate/20171201000000_change_account_id_nonnullable_in_lists.rb b/db/migrate/20171201000000_change_account_id_nonnullable_in_lists.rb
index 3369e3b9e97..ac86c9e777d 100644
--- a/db/migrate/20171201000000_change_account_id_nonnullable_in_lists.rb
+++ b/db/migrate/20171201000000_change_account_id_nonnullable_in_lists.rb
@@ -1,5 +1,7 @@
class ChangeAccountIdNonnullableInLists < ActiveRecord::Migration[5.1]
def change
- change_column_null :lists, :account_id, false
+ safety_assured do
+ change_column_null :lists, :account_id, false
+ end
end
end
diff --git a/db/migrate/20180310000000_change_columns_in_notifications_nonnullable.rb b/db/migrate/20180310000000_change_columns_in_notifications_nonnullable.rb
index 05ffd050104..dba789207da 100644
--- a/db/migrate/20180310000000_change_columns_in_notifications_nonnullable.rb
+++ b/db/migrate/20180310000000_change_columns_in_notifications_nonnullable.rb
@@ -1,8 +1,10 @@
class ChangeColumnsInNotificationsNonnullable < ActiveRecord::Migration[5.1]
def change
- change_column_null :notifications, :activity_id, false
- change_column_null :notifications, :activity_type, false
- change_column_null :notifications, :account_id, false
- change_column_null :notifications, :from_account_id, false
+ safety_assured do
+ change_column_null :notifications, :activity_id, false
+ change_column_null :notifications, :activity_type, false
+ change_column_null :notifications, :account_id, false
+ change_column_null :notifications, :from_account_id, false
+ end
end
end
diff --git a/package.json b/package.json
index 9e0c4455066..71cab74ab0b 100644
--- a/package.json
+++ b/package.json
@@ -60,7 +60,7 @@
},
"private": true,
"dependencies": {
- "@babel/core": "^7.7.5",
+ "@babel/core": "^7.7.7",
"@babel/plugin-proposal-class-properties": "^7.7.4",
"@babel/plugin-proposal-decorators": "^7.7.4",
"@babel/plugin-transform-react-inline-elements": "^7.7.4",
@@ -69,7 +69,7 @@
"@babel/preset-react": "^7.7.4",
"@babel/runtime": "^7.7.7",
"@gamestdio/websocket": "^0.3.2",
- "@clusterws/cws": "^0.16.0",
+ "@clusterws/cws": "^0.16.1",
"array-includes": "^3.1.1",
"atrament": "^0.2.3",
"arrow-key-navigation": "^1.1.0",
@@ -107,7 +107,7 @@
"intl": "^1.2.5",
"intl-messageformat": "^2.2.0",
"intl-relativeformat": "^6.4.3",
- "is-nan": "^1.2.1",
+ "is-nan": "^1.3.0",
"js-yaml": "^3.13.1",
"lodash": "^4.17.14",
"mark-loader": "^0.1.6",
@@ -146,7 +146,7 @@
"react-textarea-autosize": "^7.1.2",
"react-toggle": "^4.1.1",
"redis": "^2.7.1",
- "redux": "^4.0.4",
+ "redux": "^4.0.5",
"redux-immutable": "^4.0.0",
"redux-thunk": "^2.2.0",
"rellax": "^1.10.0",
@@ -157,13 +157,13 @@
"sass-loader": "^8.0.0",
"stringz": "^2.0.0",
"substring-trie": "^1.0.2",
- "terser-webpack-plugin": "^2.2.2",
+ "terser-webpack-plugin": "^2.3.1",
"tesseract.js": "^2.0.0-alpha.16",
"throng": "^4.0.0",
"tiny-queue": "^0.2.1",
"uuid": "^3.3.3",
"wavesurfer.js": "^3.2.0",
- "webpack": "^4.41.2",
+ "webpack": "^4.41.5",
"webpack-assets-manifest": "^3.1.1",
"webpack-bundle-analyzer": "^3.6.0",
"webpack-cli": "^3.3.10",
@@ -175,7 +175,7 @@
"babel-jest": "^24.9.0",
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.1",
- "eslint": "^6.7.2",
+ "eslint": "^6.8.0",
"eslint-plugin-import": "~2.19.1",
"eslint-plugin-jsx-a11y": "~6.2.3",
"eslint-plugin-promise": "~4.2.1",
@@ -183,7 +183,7 @@
"jest": "^24.9.0",
"raf": "^3.4.1",
"react-intl-translations-manager": "^5.0.3",
- "react-test-renderer": "^16.11.0",
+ "react-test-renderer": "^16.12.0",
"sass-lint": "^1.13.1",
"webpack-dev-server": "^3.9.0",
"yargs": "^15.0.2"
diff --git a/spec/controllers/api/proofs_controller_spec.rb b/spec/controllers/api/proofs_controller_spec.rb
index dbde4927f14..2fe6150052c 100644
--- a/spec/controllers/api/proofs_controller_spec.rb
+++ b/spec/controllers/api/proofs_controller_spec.rb
@@ -85,10 +85,7 @@ describe Api::ProofsController do
end
it 'has the correct avatar url' do
- first_part = 'https://cb6e6126.ngrok.io/system/accounts/avatars/'
- last_part = 'original/avatar.gif'
-
- expect(body_as_json[:avatar]).to match /#{Regexp.quote(first_part)}(?:\d{3,5}\/){3}#{Regexp.quote(last_part)}/
+ expect(body_as_json[:avatar]).to match "https://cb6e6126.ngrok.io#{alice.avatar.url}"
end
end
end
diff --git a/spec/controllers/api/v1/accounts/follower_accounts_controller_spec.rb b/spec/controllers/api/v1/accounts/follower_accounts_controller_spec.rb
index 75e0570e927..54587187ff9 100644
--- a/spec/controllers/api/v1/accounts/follower_accounts_controller_spec.rb
+++ b/spec/controllers/api/v1/accounts/follower_accounts_controller_spec.rb
@@ -3,19 +3,38 @@ require 'rails_helper'
describe Api::V1::Accounts::FollowerAccountsController do
render_views
- let(:user) { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
- let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:accounts') }
+ let(:user) { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
+ let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:accounts') }
+ let(:account) { Fabricate(:account) }
+ let(:alice) { Fabricate(:account) }
+ let(:bob) { Fabricate(:account) }
before do
- Fabricate(:follow, target_account: user.account)
+ alice.follow!(account)
+ bob.follow!(account)
allow(controller).to receive(:doorkeeper_token) { token }
end
describe 'GET #index' do
it 'returns http success' do
- get :index, params: { account_id: user.account.id, limit: 1 }
+ get :index, params: { account_id: account.id, limit: 2 }
expect(response).to have_http_status(200)
end
+
+ it 'returns accounts following the given account' do
+ get :index, params: { account_id: account.id, limit: 2 }
+
+ expect(body_as_json.size).to eq 2
+ expect([body_as_json[0][:id], body_as_json[1][:id]]).to match_array([alice.id.to_s, bob.id.to_s])
+ end
+
+ it 'does not return blocked users' do
+ user.account.block!(bob)
+ get :index, params: { account_id: account.id, limit: 2 }
+
+ expect(body_as_json.size).to eq 1
+ expect(body_as_json[0][:id]).to eq alice.id.to_s
+ end
end
end
diff --git a/spec/controllers/api/v1/accounts/following_accounts_controller_spec.rb b/spec/controllers/api/v1/accounts/following_accounts_controller_spec.rb
index 7f7105ad3aa..a580a736844 100644
--- a/spec/controllers/api/v1/accounts/following_accounts_controller_spec.rb
+++ b/spec/controllers/api/v1/accounts/following_accounts_controller_spec.rb
@@ -3,19 +3,38 @@ require 'rails_helper'
describe Api::V1::Accounts::FollowingAccountsController do
render_views
- let(:user) { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
- let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:accounts') }
+ let(:user) { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
+ let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:accounts') }
+ let(:account) { Fabricate(:account) }
+ let(:alice) { Fabricate(:account) }
+ let(:bob) { Fabricate(:account) }
before do
- Fabricate(:follow, account: user.account)
+ account.follow!(alice)
+ account.follow!(bob)
allow(controller).to receive(:doorkeeper_token) { token }
end
describe 'GET #index' do
it 'returns http success' do
- get :index, params: { account_id: user.account.id, limit: 1 }
+ get :index, params: { account_id: account.id, limit: 2 }
expect(response).to have_http_status(200)
end
+
+ it 'returns accounts followed by the given account' do
+ get :index, params: { account_id: account.id, limit: 2 }
+
+ expect(body_as_json.size).to eq 2
+ expect([body_as_json[0][:id], body_as_json[1][:id]]).to match_array([alice.id.to_s, bob.id.to_s])
+ end
+
+ it 'does not return blocked users' do
+ user.account.block!(bob)
+ get :index, params: { account_id: account.id, limit: 2 }
+
+ expect(body_as_json.size).to eq 1
+ expect(body_as_json[0][:id]).to eq alice.id.to_s
+ end
end
end
diff --git a/spec/controllers/api/v1/statuses/favourited_by_accounts_controller_spec.rb b/spec/controllers/api/v1/statuses/favourited_by_accounts_controller_spec.rb
index 40f75c700f5..f053ae57385 100644
--- a/spec/controllers/api/v1/statuses/favourited_by_accounts_controller_spec.rb
+++ b/spec/controllers/api/v1/statuses/favourited_by_accounts_controller_spec.rb
@@ -6,6 +6,8 @@ RSpec.describe Api::V1::Statuses::FavouritedByAccountsController, type: :control
let(:user) { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
let(:app) { Fabricate(:application, name: 'Test app', website: 'http://testapp.com') }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, application: app, scopes: 'read:accounts') }
+ let(:alice) { Fabricate(:account) }
+ let(:bob) { Fabricate(:account) }
context 'with an oauth token' do
before do
@@ -16,14 +18,28 @@ RSpec.describe Api::V1::Statuses::FavouritedByAccountsController, type: :control
let(:status) { Fabricate(:status, account: user.account) }
before do
- Fabricate(:favourite, status: status)
+ Favourite.create!(account: alice, status: status)
+ Favourite.create!(account: bob, status: status)
end
it 'returns http success' do
- get :index, params: { status_id: status.id, limit: 1 }
+ get :index, params: { status_id: status.id, limit: 2 }
expect(response).to have_http_status(200)
expect(response.headers['Link'].links.size).to eq(2)
end
+
+ it 'returns accounts who favorited the status' do
+ get :index, params: { status_id: status.id, limit: 2 }
+ expect(body_as_json.size).to eq 2
+ expect([body_as_json[0][:id], body_as_json[1][:id]]).to match_array([alice.id.to_s, bob.id.to_s])
+ end
+
+ it 'does not return blocked users' do
+ user.account.block!(bob)
+ get :index, params: { status_id: status.id, limit: 2 }
+ expect(body_as_json.size).to eq 1
+ expect(body_as_json[0][:id]).to eq alice.id.to_s
+ end
end
end
diff --git a/spec/controllers/api/v1/statuses/reblogged_by_accounts_controller_spec.rb b/spec/controllers/api/v1/statuses/reblogged_by_accounts_controller_spec.rb
index d758786dc99..60908b7b380 100644
--- a/spec/controllers/api/v1/statuses/reblogged_by_accounts_controller_spec.rb
+++ b/spec/controllers/api/v1/statuses/reblogged_by_accounts_controller_spec.rb
@@ -6,6 +6,8 @@ RSpec.describe Api::V1::Statuses::RebloggedByAccountsController, type: :controll
let(:user) { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
let(:app) { Fabricate(:application, name: 'Test app', website: 'http://testapp.com') }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, application: app, scopes: 'read:accounts') }
+ let(:alice) { Fabricate(:account) }
+ let(:bob) { Fabricate(:account) }
context 'with an oauth token' do
before do
@@ -16,14 +18,28 @@ RSpec.describe Api::V1::Statuses::RebloggedByAccountsController, type: :controll
let(:status) { Fabricate(:status, account: user.account) }
before do
- Fabricate(:status, reblog_of_id: status.id)
+ Fabricate(:status, account: alice, reblog_of_id: status.id)
+ Fabricate(:status, account: bob, reblog_of_id: status.id)
end
it 'returns http success' do
- get :index, params: { status_id: status.id, limit: 1 }
+ get :index, params: { status_id: status.id, limit: 2 }
expect(response).to have_http_status(200)
expect(response.headers['Link'].links.size).to eq(2)
end
+
+ it 'returns accounts who reblogged the status' do
+ get :index, params: { status_id: status.id, limit: 2 }
+ expect(body_as_json.size).to eq 2
+ expect([body_as_json[0][:id], body_as_json[1][:id]]).to match_array([alice.id.to_s, bob.id.to_s])
+ end
+
+ it 'does not return blocked users' do
+ user.account.block!(bob)
+ get :index, params: { status_id: status.id, limit: 2 }
+ expect(body_as_json.size).to eq 1
+ expect(body_as_json[0][:id]).to eq alice.id.to_s
+ end
end
end
diff --git a/spec/controllers/concerns/obfuscate_filename_spec.rb b/spec/controllers/concerns/obfuscate_filename_spec.rb
deleted file mode 100644
index e06d53c0385..00000000000
--- a/spec/controllers/concerns/obfuscate_filename_spec.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-# frozen_string_literal: true
-
-require 'rails_helper'
-
-describe ApplicationController, type: :controller do
- controller do
- include ObfuscateFilename
-
- obfuscate_filename :file
-
- def file
- render plain: params[:file]&.original_filename
- end
- end
-
- before do
- routes.draw { get 'file' => 'anonymous#file' }
- end
-
- it 'obfusticates filename if the given parameter is specified' do
- file = fixture_file_upload('files/imports.txt', 'text/plain')
- post 'file', params: { file: file }
- expect(response.body).to end_with '.txt'
- expect(response.body).not_to include 'imports'
- end
-
- it 'does nothing if the given parameter is not specified' do
- post 'file'
- end
-end
diff --git a/spec/controllers/follower_accounts_controller_spec.rb b/spec/controllers/follower_accounts_controller_spec.rb
index 83032ab6464..34a0cf3f482 100644
--- a/spec/controllers/follower_accounts_controller_spec.rb
+++ b/spec/controllers/follower_accounts_controller_spec.rb
@@ -22,6 +22,18 @@ describe FollowerAccountsController do
expect(assigned[0]).to eq follow1
expect(assigned[1]).to eq follow0
end
+
+ it 'does not assign blocked users' do
+ user = Fabricate(:user)
+ user.account.block!(follower0)
+ sign_in(user)
+
+ expect(response).to have_http_status(200)
+
+ assigned = assigns(:follows).to_a
+ expect(assigned.size).to eq 1
+ expect(assigned[0]).to eq follow1
+ end
end
context 'when format is json' do
diff --git a/spec/controllers/following_accounts_controller_spec.rb b/spec/controllers/following_accounts_controller_spec.rb
index d5e4ee587f4..e9a1f597df6 100644
--- a/spec/controllers/following_accounts_controller_spec.rb
+++ b/spec/controllers/following_accounts_controller_spec.rb
@@ -22,6 +22,18 @@ describe FollowingAccountsController do
expect(assigned[0]).to eq follow1
expect(assigned[1]).to eq follow0
end
+
+ it 'does not assign blocked users' do
+ user = Fabricate(:user)
+ user.account.block!(followee0)
+ sign_in(user)
+
+ expect(response).to have_http_status(200)
+
+ assigned = assigns(:follows).to_a
+ expect(assigned.size).to eq 1
+ expect(assigned[0]).to eq follow1
+ end
end
context 'when format is json' do
diff --git a/spec/features/log_in_spec.rb b/spec/features/log_in_spec.rb
index b874c255b39..de1a6de0315 100644
--- a/spec/features/log_in_spec.rb
+++ b/spec/features/log_in_spec.rb
@@ -1,47 +1,51 @@
-require "rails_helper"
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+feature 'Log in' do
+ include ProfileStories
-feature "Log in" do
given(:email) { "test@example.com" }
given(:password) { "password" }
given(:confirmed_at) { Time.zone.now }
background do
- Fabricate(:user, email: email, password: password, confirmed_at: confirmed_at)
+ as_a_registered_user
visit new_user_session_path
end
subject { page }
- scenario "A valid email and password user is able to log in" do
- fill_in "user_email", with: email
- fill_in "user_password", with: password
+ scenario 'A valid email and password 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("div.app-holder")
+ is_expected.to have_css('div.app-holder')
end
- scenario "A invalid email and password user is not able to log in" do
- fill_in "user_email", with: "invalid_email"
- fill_in "user_password", with: "invalid_password"
+ scenario 'A invalid email and password user is not able to log in' do
+ fill_in 'user_email', with: 'invalid_email'
+ fill_in 'user_password', with: 'invalid_password'
click_on I18n.t('auth.login')
- is_expected.to have_css(".flash-message", text: failure_message("invalid"))
+ is_expected.to have_css('.flash-message', text: failure_message('invalid'))
end
context do
given(:confirmed_at) { nil }
- scenario "A unconfirmed user is able to log in" do
- fill_in "user_email", with: email
- fill_in "user_password", with: password
+ 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("div.admin-wrapper")
+ is_expected.to have_css('div.admin-wrapper')
end
end
def failure_message(message)
keys = User.authentication_keys.map { |key| User.human_attribute_name(key) }
- I18n.t("devise.failure.#{message}", authentication_keys: keys.join("support.array.words_connector"))
+ I18n.t("devise.failure.#{message}", authentication_keys: keys.join('support.array.words_connector'))
end
end
diff --git a/spec/features/profile_spec.rb b/spec/features/profile_spec.rb
new file mode 100644
index 00000000000..3202167cab5
--- /dev/null
+++ b/spec/features/profile_spec.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+feature 'Profile' do
+ include ProfileStories
+
+ given(:local_domain) { ENV['LOCAL_DOMAIN'] }
+
+ background do
+ as_a_logged_in_user
+ with_alice_as_local_user
+ end
+
+ subject { page }
+
+ scenario 'I can view Annes public account' do
+ visit account_path('alice')
+
+ is_expected.to have_title("alice (@alice@#{local_domain})")
+
+ within('.public-account-header h1') do
+ is_expected.to have_content("alice @alice@#{local_domain}")
+ end
+
+ bio_elem = first('.public-account-bio')
+ expect(bio_elem).to have_content(alice_bio)
+ # The bio has hashtags made clickable
+ expect(bio_elem).to have_link('cryptology')
+ expect(bio_elem).to have_link('science')
+ # Nicknames are make clickable
+ expect(bio_elem).to have_link('@alice')
+ expect(bio_elem).to have_link('@bob')
+ # Nicknames not on server are not clickable
+ expect(bio_elem).not_to have_link('@pepe')
+ end
+
+ scenario 'I can change my account' do
+ visit settings_profile_path
+ fill_in 'Display name', with: 'Bob'
+ fill_in 'Bio', with: 'Bob is silent'
+ click_on 'Save changes'
+ is_expected.to have_content 'Changes successfully saved!'
+
+ # View my own public profile and see the changes
+ click_link "Bob @bob@#{local_domain}"
+
+ within('.public-account-header h1') do
+ is_expected.to have_content("Bob @bob@#{local_domain}")
+ end
+ expect(first('.public-account-bio')).to have_content('Bob is silent')
+ end
+end
diff --git a/spec/middleware/handle_bad_encoding_middleware_spec.rb b/spec/middleware/handle_bad_encoding_middleware_spec.rb
new file mode 100644
index 00000000000..8c0d24f1821
--- /dev/null
+++ b/spec/middleware/handle_bad_encoding_middleware_spec.rb
@@ -0,0 +1,21 @@
+require 'rails_helper'
+
+RSpec.describe HandleBadEncodingMiddleware do
+ let(:app) { double() }
+ let(:middleware) { HandleBadEncodingMiddleware.new(app) }
+
+ it "request with query string is unchanged" do
+ expect(app).to receive(:call).with("PATH" => "/some/path", "QUERY_STRING" => "name=fred")
+ middleware.call("PATH" => "/some/path", "QUERY_STRING" => "name=fred")
+ end
+
+ it "request with no query string is unchanged" do
+ expect(app).to receive(:call).with("PATH" => "/some/path")
+ middleware.call("PATH" => "/some/path")
+ end
+
+ it "request with invalid encoding in query string drops query string" do
+ expect(app).to receive(:call).with("QUERY_STRING" => "", "PATH" => "/some/path")
+ middleware.call("QUERY_STRING" => "q=%2Fsearch%2Fall%Forder%3Ddescending%26page%3D5%26sort%3Dcreated_at", "PATH" => "/some/path")
+ end
+end
diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb
index b2f6234cb9f..3cca9b3439a 100644
--- a/spec/models/account_spec.rb
+++ b/spec/models/account_spec.rb
@@ -823,4 +823,5 @@ RSpec.describe Account, type: :model do
end
include_examples 'AccountAvatar', :account
+ include_examples 'AccountHeader', :account
end
diff --git a/spec/models/media_attachment_spec.rb b/spec/models/media_attachment_spec.rb
index 7ddfba7ed9b..a275621a134 100644
--- a/spec/models/media_attachment_spec.rb
+++ b/spec/models/media_attachment_spec.rb
@@ -133,6 +133,24 @@ RSpec.describe MediaAttachment, type: :model do
expect(media.file.meta["small"]["height"]).to eq 327
expect(media.file.meta["small"]["aspect"]).to eq 490.0 / 327
end
+
+ it 'gives the file a random name' do
+ expect(media.file_file_name).to_not eq 'attachment.jpg'
+ end
+ end
+
+ describe 'base64-encoded jpeg' do
+ let(:base64_attachment) { "data:image/jpeg;base64,#{Base64.encode64(attachment_fixture('attachment.jpg').read)}" }
+ let(:media) { MediaAttachment.create(account: Fabricate(:account), file: base64_attachment) }
+
+ it 'saves media attachment' do
+ expect(media.persisted?).to be true
+ expect(media.file).to_not be_nil
+ end
+
+ it 'gives the file a file name' do
+ expect(media.file_file_name).to_not be_blank
+ end
end
describe 'descriptions for remote attachments' do
diff --git a/spec/services/process_mentions_service_spec.rb b/spec/services/process_mentions_service_spec.rb
index b1abd79b0f2..c30de8eeb9c 100644
--- a/spec/services/process_mentions_service_spec.rb
+++ b/spec/services/process_mentions_service_spec.rb
@@ -5,11 +5,11 @@ RSpec.describe ProcessMentionsService, type: :service do
let(:visibility) { :public }
let(:status) { Fabricate(:status, account: account, text: "Hello @#{remote_user.acct}", visibility: visibility) }
+ subject { ProcessMentionsService.new }
+
context 'OStatus with public toot' do
let(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :ostatus, domain: 'example.com', salmon_url: 'http://salmon.example.com') }
- subject { ProcessMentionsService.new }
-
before do
stub_request(:post, remote_user.salmon_url)
subject.call(status)
@@ -24,8 +24,6 @@ RSpec.describe ProcessMentionsService, type: :service do
let(:visibility) { :private }
let(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :ostatus, domain: 'example.com', salmon_url: 'http://salmon.example.com') }
- subject { ProcessMentionsService.new }
-
before do
stub_request(:post, remote_user.salmon_url)
subject.call(status)
@@ -41,29 +39,45 @@ RSpec.describe ProcessMentionsService, type: :service do
end
context 'ActivityPub' do
- let(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox') }
+ context do
+ let(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox') }
- subject { ProcessMentionsService.new }
+ before do
+ stub_request(:post, remote_user.inbox_url)
+ subject.call(status)
+ end
- before do
- stub_request(:post, remote_user.inbox_url)
- subject.call(status)
+ it 'creates a mention' do
+ expect(remote_user.mentions.where(status: status).count).to eq 1
+ end
+
+ it 'sends activity to the inbox' do
+ expect(a_request(:post, remote_user.inbox_url)).to have_been_made.once
+ end
end
- it 'creates a mention' do
- expect(remote_user.mentions.where(status: status).count).to eq 1
- end
+ context 'with an IDN domain' do
+ let(:remote_user) { Fabricate(:account, username: 'sneak', protocol: :activitypub, domain: 'xn--hresiar-mxa.ch', inbox_url: 'http://example.com/inbox') }
+ let(:status) { Fabricate(:status, account: account, text: "Hello @sneak@hæresiar.ch") }
- it 'sends activity to the inbox' do
- expect(a_request(:post, remote_user.inbox_url)).to have_been_made.once
+ before do
+ stub_request(:post, remote_user.inbox_url)
+ subject.call(status)
+ end
+
+ it 'creates a mention' do
+ expect(remote_user.mentions.where(status: status).count).to eq 1
+ end
+
+ it 'sends activity to the inbox' do
+ expect(a_request(:post, remote_user.inbox_url)).to have_been_made.once
+ end
end
end
context 'Temporarily-unreachable ActivityPub user' do
let(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox', last_webfingered_at: nil) }
- subject { ProcessMentionsService.new }
-
before do
stub_request(:get, "https://example.com/.well-known/host-meta").to_return(status: 404)
stub_request(:get, "https://example.com/.well-known/webfinger?resource=acct:remote_user@example.com").to_return(status: 500)
diff --git a/spec/support/examples/models/concerns/account_avatar.rb b/spec/support/examples/models/concerns/account_avatar.rb
index f2a8a2459c5..2180f52739f 100644
--- a/spec/support/examples/models/concerns/account_avatar.rb
+++ b/spec/support/examples/models/concerns/account_avatar.rb
@@ -16,4 +16,24 @@ shared_examples 'AccountAvatar' do |fabricator|
end
end
end
+
+ describe 'base64-encoded files' do
+ let(:base64_attachment) { "data:image/jpeg;base64,#{Base64.encode64(attachment_fixture('attachment.jpg').read)}" }
+ let(:account) { Fabricate(fabricator, avatar: base64_attachment) }
+
+ it 'saves avatar' do
+ expect(account.persisted?).to be true
+ expect(account.avatar).to_not be_nil
+ end
+
+ it 'gives the avatar a file name' do
+ expect(account.avatar_file_name).to_not be_blank
+ end
+
+ it 'saves a new avatar under a different file name' do
+ previous_file_name = account.avatar_file_name
+ account.update(avatar: base64_attachment)
+ expect(account.avatar_file_name).to_not eq previous_file_name
+ end
+ end
end
diff --git a/spec/support/examples/models/concerns/account_header.rb b/spec/support/examples/models/concerns/account_header.rb
new file mode 100644
index 00000000000..77ee0e62901
--- /dev/null
+++ b/spec/support/examples/models/concerns/account_header.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+shared_examples 'AccountHeader' do |fabricator|
+ describe 'base64-encoded files' do
+ let(:base64_attachment) { "data:image/jpeg;base64,#{Base64.encode64(attachment_fixture('attachment.jpg').read)}" }
+ let(:account) { Fabricate(fabricator, header: base64_attachment) }
+
+ it 'saves header' do
+ expect(account.persisted?).to be true
+ expect(account.header).to_not be_nil
+ end
+
+ it 'gives the header a file name' do
+ expect(account.header_file_name).to_not be_blank
+ end
+
+ it 'saves a new header under a different file name' do
+ previous_file_name = account.header_file_name
+ account.update(header: base64_attachment)
+ expect(account.header_file_name).to_not eq previous_file_name
+ end
+ end
+end
diff --git a/spec/support/stories/profile_stories.rb b/spec/support/stories/profile_stories.rb
new file mode 100644
index 00000000000..75b413330fc
--- /dev/null
+++ b/spec/support/stories/profile_stories.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+module ProfileStories
+ attr_reader :bob, :alice, :alice_bio
+
+ def as_a_registered_user
+ @bob = Fabricate(
+ :user,
+ email: email, password: password, confirmed_at: confirmed_at,
+ account: Fabricate(:account, username: 'bob')
+ )
+ end
+
+ def as_a_logged_in_user
+ as_a_registered_user
+ visit new_user_session_path
+ fill_in 'user_email', with: email
+ fill_in 'user_password', with: password
+ click_on I18n.t('auth.login')
+ end
+
+ def with_alice_as_local_user
+ @alice_bio = '@alice and @bob are fictional characters commonly used as'\
+ 'placeholder names in #cryptology, as well as #science and'\
+ 'engineering 📖 literature. Not affilated with @pepe.'
+
+ @alice = Fabricate(
+ :user,
+ email: 'alice@example.com', password: password, confirmed_at: confirmed_at,
+ account: Fabricate(:account, username: 'alice', note: @alice_bio)
+ )
+ end
+
+ def confirmed_at
+ @confirmed_at ||= Time.zone.now
+ end
+
+ def email
+ @email ||= 'test@example.com'
+ end
+
+ def password
+ @password ||= 'password'
+ end
+end
diff --git a/spec/workers/refollow_worker_spec.rb b/spec/workers/refollow_worker_spec.rb
new file mode 100644
index 00000000000..29771aa59b7
--- /dev/null
+++ b/spec/workers/refollow_worker_spec.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe RefollowWorker do
+ subject { described_class.new }
+ let(:account) { Fabricate(:account, domain: 'example.org', protocol: :activitypub) }
+ let(:alice) { Fabricate(:account, domain: nil, username: 'alice') }
+ let(:bob) { Fabricate(:account, domain: nil, username: 'bob') }
+
+ describe 'perform' do
+ let(:service) { double }
+
+ before do
+ allow(FollowService).to receive(:new).and_return(service)
+ allow(service).to receive(:call)
+
+ alice.follow!(account, reblogs: true)
+ bob.follow!(account, reblogs: false)
+ end
+
+ it 'calls FollowService for local followers' do
+ result = subject.perform(account.id)
+
+ expect(result).to be_nil
+ expect(service).to have_received(:call).with(alice, account, reblogs: true)
+ expect(service).to have_received(:call).with(bob, account, reblogs: false)
+ end
+ end
+end
diff --git a/yarn.lock b/yarn.lock
index f7834e46915..258567ddb67 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -9,15 +9,15 @@
dependencies:
"@babel/highlight" "^7.0.0"
-"@babel/core@^7.1.0", "@babel/core@^7.4.5", "@babel/core@^7.7.5":
- version "7.7.5"
- resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.7.5.tgz#ae1323cd035b5160293307f50647e83f8ba62f7e"
- integrity sha512-M42+ScN4+1S9iB6f+TL7QBpoQETxbclx+KNoKJABghnKYE+fMzSGqst0BZJc8CpI625bwPwYgUyRvxZ+0mZzpw==
+"@babel/core@^7.1.0", "@babel/core@^7.4.5", "@babel/core@^7.7.7":
+ version "7.7.7"
+ resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.7.7.tgz#ee155d2e12300bcc0cff6a8ad46f2af5063803e9"
+ integrity sha512-jlSjuj/7z138NLZALxVgrx13AOtqip42ATZP7+kYl53GvDV6+4dCek1mVUo8z8c8Xnw/mx2q3d9HWh3griuesQ==
dependencies:
"@babel/code-frame" "^7.5.5"
- "@babel/generator" "^7.7.4"
+ "@babel/generator" "^7.7.7"
"@babel/helpers" "^7.7.4"
- "@babel/parser" "^7.7.5"
+ "@babel/parser" "^7.7.7"
"@babel/template" "^7.7.4"
"@babel/traverse" "^7.7.4"
"@babel/types" "^7.7.4"
@@ -29,10 +29,10 @@
semver "^5.4.1"
source-map "^0.5.0"
-"@babel/generator@^7.0.0", "@babel/generator@^7.7.4":
- version "7.7.4"
- resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.7.4.tgz#db651e2840ca9aa66f327dcec1dc5f5fa9611369"
- integrity sha512-m5qo2WgdOJeyYngKImbkyQrnUN1mPceaG5BV+G0E3gWsa4l/jCSryWJdM2x8OuGAOyh+3d5pVYfZWCiNFtynxg==
+"@babel/generator@^7.0.0", "@babel/generator@^7.7.4", "@babel/generator@^7.7.7":
+ version "7.7.7"
+ resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.7.7.tgz#859ac733c44c74148e1a72980a64ec84b85f4f45"
+ integrity sha512-/AOIBpHh/JU1l0ZFS4kiRCBnLi6OTHzh0RPk3h9isBxkkqELtQNFi1Vr/tiG9p1yfoUdKVwISuXWQR+hwwM4VQ==
dependencies:
"@babel/types" "^7.7.4"
jsesc "^2.5.1"
@@ -252,10 +252,10 @@
esutils "^2.0.2"
js-tokens "^4.0.0"
-"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.7.4", "@babel/parser@^7.7.5":
- version "7.7.5"
- resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.7.5.tgz#cbf45321619ac12d83363fcf9c94bb67fa646d71"
- integrity sha512-KNlOe9+/nk4i29g0VXgl8PEXIRms5xKLJeuZ6UptN0fHv+jDiriG+y94X6qAgWTR0h3KaoM1wK5G5h7MHFRSig==
+"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.7.4", "@babel/parser@^7.7.7":
+ version "7.7.7"
+ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.7.7.tgz#1b886595419cf92d811316d5b715a53ff38b4937"
+ integrity sha512-WtTZMZAZLbeymhkd/sEaPD8IQyGAhmuTuvTzLiCFM7iXiVdY0gc0IaI+cW0fh1BnSMbJSzXX6/fHllgHKwHhXw==
"@babel/plugin-proposal-async-generator-functions@^7.7.4":
version "7.7.4"
@@ -795,10 +795,10 @@
lodash "^4.17.13"
to-fast-properties "^2.0.0"
-"@clusterws/cws@^0.16.0":
- version "0.16.0"
- resolved "https://registry.yarnpkg.com/@clusterws/cws/-/cws-0.16.0.tgz#f6116cbf3a8b7ad0657916616ce5f8248746b797"
- integrity sha512-YeGpAPIdkBsOnAkmFKVMWEjCKDH900U2if0B+nc1imfv+64AIb2JX2xiTA6BLDLppEgWV5c6bpWESjbHCNblHw==
+"@clusterws/cws@^0.16.1":
+ version "0.16.1"
+ resolved "https://registry.yarnpkg.com/@clusterws/cws/-/cws-0.16.1.tgz#443c47909601e87b881ffb2c4c83cbf27a228487"
+ integrity sha512-OamMsXwVppfmwX14Ed1msJJN0KNi+VBQh5AkAqUvIGTcJyHcjpsKjU15wS8QumvEewxx9gvGp7aSDop0/R5Gqg==
"@cnakazawa/watch@^1.0.3":
version "1.0.3"
@@ -3247,7 +3247,7 @@ default-gateway@^4.2.0:
execa "^1.0.0"
ip-regex "^2.1.0"
-define-properties@^1.1.1, define-properties@^1.1.2, define-properties@^1.1.3:
+define-properties@^1.1.2, define-properties@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==
@@ -3942,10 +3942,10 @@ eslint@^2.7.0:
text-table "~0.2.0"
user-home "^2.0.0"
-eslint@^6.7.2:
- version "6.7.2"
- resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.7.2.tgz#c17707ca4ad7b2d8af986a33feba71e18a9fecd1"
- integrity sha512-qMlSWJaCSxDFr8fBPvJM9kJwbazrhNcBU3+DszDW1OlEwKBBRWsJc7NJFelvwQpanHCR14cOLD41x8Eqvo3Nng==
+eslint@^6.8.0:
+ version "6.8.0"
+ resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.8.0.tgz#62262d6729739f9275723824302fb227c8c93ffb"
+ integrity sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==
dependencies:
"@babel/code-frame" "^7.0.0"
ajv "^6.10.0"
@@ -4031,12 +4031,7 @@ estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0:
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
-esutils@^2.0.0:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b"
- integrity sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=
-
-esutils@^2.0.2:
+esutils@^2.0.0, esutils@^2.0.2:
version "2.0.3"
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
@@ -4385,10 +4380,10 @@ find-cache-dir@^2.0.0, find-cache-dir@^2.1.0:
make-dir "^2.0.0"
pkg-dir "^3.0.0"
-find-cache-dir@^3.0.0, find-cache-dir@^3.1.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.1.0.tgz#9935894999debef4cf9f677fdf646d002c4cdecb"
- integrity sha512-zw+EFiNBNPgI2NTrKkDd1xd7q0cs6wr/iWnr/oUkI0yF9K9GqQ+riIt4aiyFaaqpaWbxPrJXHI+QvmNUQbX+0Q==
+find-cache-dir@^3.0.0, find-cache-dir@^3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.2.0.tgz#e7fe44c1abc1299f516146e563108fd1006c1874"
+ integrity sha512-1JKclkYYsf1q9WIJKLZa9S9muC+08RIjzAlLrK4QcYLJMS6mk9yombQ9qf+zJ7H9LS800k0s44L4sDq9VYzqyg==
dependencies:
commondir "^1.0.1"
make-dir "^3.0.0"
@@ -5620,12 +5615,12 @@ is-my-json-valid@^2.10.0:
jsonpointer "^4.0.0"
xtend "^4.0.0"
-is-nan@^1.2.1:
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.2.1.tgz#9faf65b6fb6db24b7f5c0628475ea71f988401e2"
- integrity sha1-n69ltvttskt/XAYoR16nH5iEAeI=
+is-nan@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.3.0.tgz#85d1f5482f7051c2019f5673ccebdb06f3b0db03"
+ integrity sha512-z7bbREymOqt2CCaZVly8aC4ML3Xhfi0ekuOnjO2L8vKdl+CttdVoGZQhd4adMFAsxQ5VeRVwORs4tU8RH+HFtQ==
dependencies:
- define-properties "^1.1.1"
+ define-properties "^1.1.3"
is-number-object@^1.0.4:
version "1.0.4"
@@ -8785,15 +8780,15 @@ react-swipeable-views@^0.13.3:
react-swipeable-views-utils "^0.13.3"
warning "^4.0.1"
-react-test-renderer@^16.0.0-0, react-test-renderer@^16.11.0:
- version "16.11.0"
- resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.11.0.tgz#72574566496462c808ac449b0287a4c0a1a7d8f8"
- integrity sha512-nh9gDl8R4ut+ZNNb2EeKO5VMvTKxwzurbSMuGBoKtjpjbg8JK/u3eVPVNi1h1Ue+eYK9oSzJjb+K3lzLxyA4ag==
+react-test-renderer@^16.0.0-0, react-test-renderer@^16.12.0:
+ version "16.12.0"
+ resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.12.0.tgz#11417ffda579306d4e841a794d32140f3da1b43f"
+ integrity sha512-Vj/teSqt2oayaWxkbhQ6gKis+t5JrknXfPVo+aIJ8QwYAqMPH77uptOdrlphyxl8eQI/rtkOYg86i/UWkpFu0w==
dependencies:
object-assign "^4.1.1"
prop-types "^15.6.2"
react-is "^16.8.6"
- scheduler "^0.17.0"
+ scheduler "^0.18.0"
react-textarea-autosize@^7.1.2:
version "7.1.2"
@@ -8946,10 +8941,10 @@ redux-thunk@^2.2.0:
resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622"
integrity sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw==
-redux@^4.0.4:
- version "4.0.4"
- resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.4.tgz#4ee1aeb164b63d6a1bcc57ae4aa0b6e6fa7a3796"
- integrity sha512-vKv4WdiJxOWKxK0yRoaK3Y4pxxB0ilzVx6dszU2W8wLxlb2yikRph4iV/ymtdJ6ZxpBLFbyrxklnT5yBbQSl3Q==
+redux@^4.0.5:
+ version "4.0.5"
+ resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f"
+ integrity sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==
dependencies:
loose-envify "^1.4.0"
symbol-observable "^1.2.0"
@@ -9395,14 +9390,6 @@ sax@^1.2.4, sax@~1.2.4:
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
-scheduler@^0.17.0:
- version "0.17.0"
- resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.17.0.tgz#7c9c673e4ec781fac853927916d1c426b6f3ddfe"
- integrity sha512-7rro8Io3tnCPuY4la/NuI5F2yfESpnfZyT6TtkXnSWVkcu0BCDJ+8gk5ozUaFaxpIyNuWAPXrH0yFcSi28fnDA==
- dependencies:
- loose-envify "^1.1.0"
- object-assign "^4.1.1"
-
scheduler@^0.18.0:
version "0.18.0"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.18.0.tgz#5901ad6659bc1d8f3fdaf36eb7a67b0d6746b1c4"
@@ -9487,12 +9474,7 @@ send@0.17.1:
range-parser "~1.2.1"
statuses "~1.5.0"
-serialize-javascript@^1.7.0:
- version "1.9.1"
- resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.9.1.tgz#cfc200aef77b600c47da9bb8149c943e798c2fdb"
- integrity sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A==
-
-serialize-javascript@^2.1.1, serialize-javascript@^2.1.2:
+serialize-javascript@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-2.1.2.tgz#ecec53b0e0317bdc95ef76ab7074b7384785fa61"
integrity sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==
@@ -10164,39 +10146,39 @@ tcomb@^2.5.0:
resolved "https://registry.yarnpkg.com/tcomb/-/tcomb-2.7.0.tgz#10d62958041669a5d53567b9a4ee8cde22b1c2b0"
integrity sha1-ENYpWAQWaaXVNWe5pO6M3iKxwrA=
-terser-webpack-plugin@^1.4.1:
- version "1.4.1"
- resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.1.tgz#61b18e40eaee5be97e771cdbb10ed1280888c2b4"
- integrity sha512-ZXmmfiwtCLfz8WKZyYUuuHf3dMYEjg8NrjHMb0JqHVHVOSkzp3cW2/XG1fP3tRhqEqSzMwzzRQGtAPbs4Cncxg==
+terser-webpack-plugin@^1.4.3:
+ version "1.4.3"
+ resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz#5ecaf2dbdc5fb99745fd06791f46fc9ddb1c9a7c"
+ integrity sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA==
dependencies:
cacache "^12.0.2"
find-cache-dir "^2.1.0"
is-wsl "^1.1.0"
schema-utils "^1.0.0"
- serialize-javascript "^1.7.0"
+ serialize-javascript "^2.1.2"
source-map "^0.6.1"
terser "^4.1.2"
webpack-sources "^1.4.0"
worker-farm "^1.7.0"
-terser-webpack-plugin@^2.2.2:
- version "2.2.2"
- resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-2.2.2.tgz#2a6e00237125564a455ad69b22e08ee59420473a"
- integrity sha512-/CHMNswPMAwuD2kd++qys8UmBRmsshPSzHw4BlDwurPtK9YjeK93OV89YWkJulHk972cs07K/7Z92V6PNjWF8A==
+terser-webpack-plugin@^2.3.1:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-2.3.1.tgz#6a63c27debc15b25ffd2588562ee2eeabdcab923"
+ integrity sha512-dNxivOXmDgZqrGxOttBH6B4xaxT4zNC+Xd+2K8jwGDMK5q2CZI+KZMA1AAnSRT+BTRvuzKsDx+fpxzPAmAMVcA==
dependencies:
cacache "^13.0.1"
- find-cache-dir "^3.1.0"
+ find-cache-dir "^3.2.0"
jest-worker "^24.9.0"
schema-utils "^2.6.1"
- serialize-javascript "^2.1.1"
+ serialize-javascript "^2.1.2"
source-map "^0.6.1"
- terser "^4.4.2"
+ terser "^4.4.3"
webpack-sources "^1.4.3"
-terser@^4.1.2, terser@^4.4.2:
- version "4.4.2"
- resolved "https://registry.yarnpkg.com/terser/-/terser-4.4.2.tgz#448fffad0245f4c8a277ce89788b458bfd7706e8"
- integrity sha512-Uufrsvhj9O1ikwgITGsZ5EZS6qPokUOkCegS7fYOdGTv+OA90vndUbU6PEjr5ePqHfNUbGyMO7xyIZv2MhsALQ==
+terser@^4.1.2, terser@^4.4.3:
+ version "4.4.3"
+ resolved "https://registry.yarnpkg.com/terser/-/terser-4.4.3.tgz#401abc52b88869cf904412503b1eb7da093ae2f0"
+ integrity sha512-0ikKraVtRDKGzHrzkCv5rUNDzqlhmhowOBqC0XqUHFpW+vJ45+20/IFBcebwKfiS2Z9fJin6Eo+F1zLZsxi8RA==
dependencies:
commander "^2.20.0"
source-map "~0.6.1"
@@ -10858,10 +10840,10 @@ webpack-sources@^1.0.0, webpack-sources@^1.0.1, webpack-sources@^1.1.0, webpack-
source-list-map "^2.0.0"
source-map "~0.6.1"
-webpack@^4.41.2:
- version "4.41.2"
- resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.41.2.tgz#c34ec76daa3a8468c9b61a50336d8e3303dce74e"
- integrity sha512-Zhw69edTGfbz9/8JJoyRQ/pq8FYUoY0diOXqW0T6yhgdhCv6wr0hra5DwwWexNRns2Z2+gsnrNcbe9hbGBgk/A==
+webpack@^4.41.5:
+ version "4.41.5"
+ resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.41.5.tgz#3210f1886bce5310e62bb97204d18c263341b77c"
+ integrity sha512-wp0Co4vpyumnp3KlkmpM5LWuzvZYayDwM2n17EHFr4qxBBbRokC7DJawPJC7TfSFZ9HZ6GsdH40EBj4UV0nmpw==
dependencies:
"@webassemblyjs/ast" "1.8.5"
"@webassemblyjs/helper-module-context" "1.8.5"
@@ -10883,7 +10865,7 @@ webpack@^4.41.2:
node-libs-browser "^2.2.1"
schema-utils "^1.0.0"
tapable "^1.1.3"
- terser-webpack-plugin "^1.4.1"
+ terser-webpack-plugin "^1.4.3"
watchpack "^1.6.0"
webpack-sources "^1.4.1"