Merge pull request #1324 from ThibG/glitch-soc/merge-upstream

Merge upstream changes
th-downstream
ThibG 5 years ago committed by GitHub
commit 418c1806c6

@ -5,12 +5,13 @@ aliases:
docker: docker:
- image: circleci/ruby:2.7-buster-node - image: circleci/ruby:2.7-buster-node
environment: &ruby_environment environment: &ruby_environment
BUNDLE_JOBS: 3
BUNDLE_RETRY: 3
BUNDLE_APP_CONFIG: ./.bundle/ BUNDLE_APP_CONFIG: ./.bundle/
BUNDLE_PATH: ./vendor/bundle/ BUNDLE_PATH: ./vendor/bundle/
DB_HOST: localhost DB_HOST: localhost
DB_USER: root DB_USER: root
RAILS_ENV: test RAILS_ENV: test
PARALLEL_TEST_PROCESSORS: 4
ALLOW_NOPAM: true ALLOW_NOPAM: true
CONTINUOUS_INTEGRATION: true CONTINUOUS_INTEGRATION: true
DISABLE_SIMPLECOV: true DISABLE_SIMPLECOV: true
@ -32,9 +33,9 @@ aliases:
- &restore_ruby_dependencies - &restore_ruby_dependencies
restore_cache: restore_cache:
keys: keys:
- v2-ruby-dependencies-{{ checksum "/tmp/.ruby-version" }}-{{ checksum "Gemfile.lock" }} - v3-ruby-dependencies-{{ checksum "/tmp/.ruby-version" }}-{{ checksum "Gemfile.lock" }}
- v2-ruby-dependencies-{{ checksum "/tmp/.ruby-version" }}- - v3-ruby-dependencies-{{ checksum "/tmp/.ruby-version" }}-
- v2-ruby-dependencies- - v3-ruby-dependencies-
- &install_steps - &install_steps
steps: steps:
@ -42,11 +43,13 @@ aliases:
- *attach_workspace - *attach_workspace
- restore_cache: - restore_cache:
keys: keys:
- v1-node-dependencies-{{ checksum "yarn.lock" }} - v2-node-dependencies-{{ checksum "yarn.lock" }}
- v1-node-dependencies- - v2-node-dependencies-
- run: yarn install --frozen-lockfile - run:
name: Install yarn dependencies
command: yarn install --frozen-lockfile
- save_cache: - save_cache:
key: v1-node-dependencies-{{ checksum "yarn.lock" }} key: v2-node-dependencies-{{ checksum "yarn.lock" }}
paths: paths:
- ./node_modules/ - ./node_modules/
- *persist_to_workspace - *persist_to_workspace
@ -57,27 +60,28 @@ aliases:
command: | command: |
sudo apt-get update sudo apt-get update
sudo apt-get install -y libicu-dev libidn11-dev libprotobuf-dev protobuf-compiler 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 - &install_ruby_dependencies
steps: steps:
- *attach_workspace - *attach_workspace
- *install_system_dependencies - *install_system_dependencies
- run: ruby -e 'puts RUBY_VERSION' | tee /tmp/.ruby-version - run:
name: Set Ruby version
command: ruby -e 'puts RUBY_VERSION' | tee /tmp/.ruby-version
- *restore_ruby_dependencies - *restore_ruby_dependencies
- run: bundle config set clean 'true' - run:
- run: bundle config set deployment 'true' name: Set bundler settings
- run: bundle config set with 'pam_authentication' command: |
- run: bundle config set without 'development production' bundle config clean 'true'
- run: bundle config set frozen 'true' bundle config deployment 'true'
- run: bundle install --jobs 16 --retry 3 && bundle clean bundle config with 'pam_authentication'
bundle config without 'development production'
bundle config frozen 'true'
- run:
name: Install bundler dependencies
command: bundle check || (bundle install && bundle clean)
- save_cache: - save_cache:
key: v2-ruby-dependencies-{{ checksum "/tmp/.ruby-version" }}-{{ checksum "Gemfile.lock" }} key: v3-ruby-dependencies-{{ checksum "/tmp/.ruby-version" }}-{{ checksum "Gemfile.lock" }}
paths: paths:
- ./.bundle/ - ./.bundle/
- ./vendor/bundle/ - ./vendor/bundle/
@ -88,17 +92,26 @@ aliases:
- ./mastodon/vendor/bundle/ - ./mastodon/vendor/bundle/
- &test_steps - &test_steps
parallelism: 4
steps: steps:
- *attach_workspace - *attach_workspace
- *install_system_dependencies - *install_system_dependencies
- run: sudo apt-get install -y ffmpeg
- run: - run:
name: Prepare Tests name: Install FFMPEG
command: ./bin/rails parallel:create parallel:load_schema parallel:prepare command: sudo apt-get install -y ffmpeg
- run: - run:
name: Run Tests name: Load database schema
command: ./bin/retry bundle exec parallel_test ./spec/ --group-by filesize --type rspec command: ./bin/rails db:create db:schema:load db:seed
- run:
name: Run rspec in parallel
command: |
bundle exec rspec --profile 10 \
--format RspecJunitFormatter \
--out test_results/rspec.xml \
--format progress \
$(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings)
- store_test_results:
path: test_results
jobs: jobs:
install: install:
<<: *defaults <<: *defaults
@ -115,19 +128,14 @@ jobs:
environment: *ruby_environment environment: *ruby_environment
<<: *install_ruby_dependencies <<: *install_ruby_dependencies
install-ruby2.5:
<<: *defaults
docker:
- image: circleci/ruby:2.5-buster-node
environment: *ruby_environment
<<: *install_ruby_dependencies
build: build:
<<: *defaults <<: *defaults
steps: steps:
- *attach_workspace - *attach_workspace
- *install_system_dependencies - *install_system_dependencies
- run: ./bin/rails assets:precompile - run:
name: Precompile assets
command: ./bin/rails assets:precompile
- persist_to_workspace: - persist_to_workspace:
root: ~/projects/ root: ~/projects/
paths: paths:
@ -149,10 +157,10 @@ jobs:
- *install_system_dependencies - *install_system_dependencies
- run: - run:
name: Create database name: Create database
command: ./bin/rails parallel:create command: ./bin/rails db:create
- run: - run:
name: Run migrations name: Run migrations
command: ./bin/rails parallel:migrate command: ./bin/rails db:migrate
test-ruby2.7: test-ruby2.7:
<<: *defaults <<: *defaults
@ -178,35 +186,33 @@ jobs:
- image: circleci/redis:5-alpine - image: circleci/redis:5-alpine
<<: *test_steps <<: *test_steps
test-ruby2.5:
<<: *defaults
docker:
- image: circleci/ruby:2.5-buster-node
environment: *ruby_environment
- image: circleci/postgres:12.2
environment:
POSTGRES_USER: root
POSTGRES_HOST_AUTH_METHOD: trust
- image: circleci/redis:5-alpine
<<: *test_steps
test-webui: test-webui:
<<: *defaults <<: *defaults
docker: docker:
- image: circleci/node:12-buster - image: circleci/node:12-buster
steps: steps:
- *attach_workspace - *attach_workspace
- run: ./bin/retry yarn test:jest - run:
name: Run jest
command: yarn test:jest
check-i18n: check-i18n:
<<: *defaults <<: *defaults
steps: steps:
- *attach_workspace - *attach_workspace
- *install_system_dependencies - *install_system_dependencies
- run: bundle exec i18n-tasks check-normalized - run:
- run: bundle exec i18n-tasks unused -l en name: Check locale file normalization
- run: bundle exec i18n-tasks check-consistent-interpolations command: bundle exec i18n-tasks check-normalized
- run: bundle exec rake repo:check_locales_files - run:
name: Check for unused strings
command: bundle exec i18n-tasks unused -l en
- run:
name: Check for wrong string interpolations
command: bundle exec i18n-tasks check-consistent-interpolations
- run:
name: Check that all required locale files exist
command: bundle exec rake repo:check_locales_files
workflows: workflows:
version: 2 version: 2
@ -220,10 +226,6 @@ workflows:
requires: requires:
- install - install
- install-ruby2.7 - install-ruby2.7
- install-ruby2.5:
requires:
- install
- install-ruby2.7
- build: - build:
requires: requires:
- install-ruby2.7 - install-ruby2.7
@ -238,10 +240,6 @@ workflows:
requires: requires:
- install-ruby2.6 - install-ruby2.6
- build - build
- test-ruby2.5:
requires:
- install-ruby2.5
- build
- test-webui: - test-webui:
requires: requires:
- install - install

@ -20,7 +20,7 @@ gem 'makara', '~> 0.4'
gem 'pghero', '~> 2.4' gem 'pghero', '~> 2.4'
gem 'dotenv-rails', '~> 2.7' gem 'dotenv-rails', '~> 2.7'
gem 'aws-sdk-s3', '~> 1.61', require: false gem 'aws-sdk-s3', '~> 1.63', require: false
gem 'fog-core', '<= 2.1.0' gem 'fog-core', '<= 2.1.0'
gem 'fog-openstack', '~> 0.3', require: false gem 'fog-openstack', '~> 0.3', require: false
gem 'paperclip', '~> 6.0' gem 'paperclip', '~> 6.0'
@ -129,6 +129,7 @@ group :test do
gem 'simplecov', '~> 0.18', require: false gem 'simplecov', '~> 0.18', require: false
gem 'webmock', '~> 3.8' gem 'webmock', '~> 3.8'
gem 'parallel_tests', '~> 2.32' gem 'parallel_tests', '~> 2.32'
gem 'rspec_junit_formatter', '~> 0.4'
end end
group :development do group :development do

@ -92,7 +92,7 @@ GEM
av (0.9.0) av (0.9.0)
cocaine (~> 0.5.3) cocaine (~> 0.5.3)
aws-eventstream (1.1.0) aws-eventstream (1.1.0)
aws-partitions (1.296.0) aws-partitions (1.303.0)
aws-sdk-core (3.94.0) aws-sdk-core (3.94.0)
aws-eventstream (~> 1, >= 1.0.2) aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.239.0) aws-partitions (~> 1, >= 1.239.0)
@ -101,7 +101,7 @@ GEM
aws-sdk-kms (1.30.0) aws-sdk-kms (1.30.0)
aws-sdk-core (~> 3, >= 3.71.0) aws-sdk-core (~> 3, >= 3.71.0)
aws-sigv4 (~> 1.1) aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.61.2) aws-sdk-s3 (1.63.0)
aws-sdk-core (~> 3, >= 3.83.0) aws-sdk-core (~> 3, >= 3.83.0)
aws-sdk-kms (~> 1) aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.1) aws-sigv4 (~> 1.1)
@ -277,7 +277,7 @@ GEM
http-parser (~> 1.2.0) http-parser (~> 1.2.0)
http-cookie (1.0.3) http-cookie (1.0.3)
domain_name (~> 0.5) domain_name (~> 0.5)
http-form_data (2.2.0) http-form_data (2.3.0)
http-parser (1.2.1) http-parser (1.2.1)
ffi-compiler (>= 1.0, < 2.0) ffi-compiler (>= 1.0, < 2.0)
http_accept_language (2.1.1) http_accept_language (2.1.1)
@ -303,7 +303,7 @@ GEM
jmespath (1.4.0) jmespath (1.4.0)
json (2.3.0) json (2.3.0)
json-canonicalization (0.2.0) json-canonicalization (0.2.0)
json-ld (3.1.2) json-ld (3.1.3)
htmlentities (~> 4.3) htmlentities (~> 4.3)
json-canonicalization (~> 0.2) json-canonicalization (~> 0.2)
link_header (~> 0.0, >= 0.0.8) link_header (~> 0.0, >= 0.0.8)
@ -359,7 +359,7 @@ GEM
nokogiri (~> 1.10) nokogiri (~> 1.10)
mime-types (3.3.1) mime-types (3.3.1)
mime-types-data (~> 3.2015) mime-types-data (~> 3.2015)
mime-types-data (3.2019.1009) mime-types-data (3.2020.0425)
mimemagic (0.3.4) mimemagic (0.3.4)
mini_mime (1.0.2) mini_mime (1.0.2)
mini_portile2 (2.4.0) mini_portile2 (2.4.0)
@ -382,7 +382,7 @@ GEM
concurrent-ruby (~> 1.0, >= 1.0.2) concurrent-ruby (~> 1.0, >= 1.0.2)
sidekiq (>= 3.5) sidekiq (>= 3.5)
statsd-ruby (~> 1.4, >= 1.4.0) statsd-ruby (~> 1.4, >= 1.4.0)
oj (3.10.5) oj (3.10.6)
omniauth (1.9.1) omniauth (1.9.1)
hashie (>= 3.4.6) hashie (>= 3.4.6)
rack (>= 1.6.2, < 3) rack (>= 1.6.2, < 3)
@ -409,7 +409,7 @@ GEM
parallel parallel
parser (2.7.1.1) parser (2.7.1.1)
ast (~> 2.4.0) ast (~> 2.4.0)
parslet (1.8.2) parslet (2.0.0)
pastel (0.7.3) pastel (0.7.3)
equatable (~> 0.6) equatable (~> 0.6)
tty-color (~> 0.5) tty-color (~> 0.5)
@ -542,6 +542,8 @@ GEM
rspec-core (~> 3.0, >= 3.0.0) rspec-core (~> 3.0, >= 3.0.0)
sidekiq (>= 2.4.0) sidekiq (>= 2.4.0)
rspec-support (3.9.2) rspec-support (3.9.2)
rspec_junit_formatter (0.4.1)
rspec-core (>= 2, < 4, != 2.12.0)
rubocop (0.79.0) rubocop (0.79.0)
jaro_winkler (~> 1.5.1) jaro_winkler (~> 1.5.1)
parallel (~> 1.10) parallel (~> 1.10)
@ -554,7 +556,7 @@ GEM
rack (>= 1.1) rack (>= 1.1)
rubocop (>= 0.72.0) rubocop (>= 0.72.0)
ruby-progressbar (1.10.1) ruby-progressbar (1.10.1)
ruby-saml (1.9.0) ruby-saml (1.11.0)
nokogiri (>= 1.5.10) nokogiri (>= 1.5.10)
rufus-scheduler (3.6.0) rufus-scheduler (3.6.0)
fugit (~> 1.1, >= 1.1.6) fugit (~> 1.1, >= 1.1.6)
@ -668,7 +670,7 @@ DEPENDENCIES
active_record_query_trace (~> 1.7) active_record_query_trace (~> 1.7)
addressable (~> 2.7) addressable (~> 2.7)
annotate (~> 3.1) annotate (~> 3.1)
aws-sdk-s3 (~> 1.61) aws-sdk-s3 (~> 1.63)
better_errors (~> 2.6) better_errors (~> 2.6)
binding_of_caller (~> 0.7) binding_of_caller (~> 0.7)
blurhash (~> 0.1) blurhash (~> 0.1)
@ -765,6 +767,7 @@ DEPENDENCIES
rqrcode (~> 1.1) rqrcode (~> 1.1)
rspec-rails (~> 4.0) rspec-rails (~> 4.0)
rspec-sidekiq (~> 3.0) rspec-sidekiq (~> 3.0)
rspec_junit_formatter (~> 0.4)
rubocop (~> 0.79) rubocop (~> 0.79)
rubocop-rails (~> 2.5) rubocop-rails (~> 2.5)
ruby-progressbar (~> 1.10) ruby-progressbar (~> 1.10)

@ -28,7 +28,7 @@ class AccountsController < ApplicationController
end end
@pinned_statuses = cache_collection(@account.pinned_statuses, Status) if show_pinned_statuses? @pinned_statuses = cache_collection(@account.pinned_statuses, Status) if show_pinned_statuses?
@statuses = filtered_status_page(params) @statuses = filtered_status_page
@statuses = cache_collection(@statuses, Status) @statuses = cache_collection(@statuses, Status)
@rss_url = rss_url @rss_url = rss_url
@ -141,12 +141,12 @@ class AccountsController < ApplicationController
request.path.split('.').first.ends_with?(Addressable::URI.parse("/tagged/#{params[:tag]}").normalize) request.path.split('.').first.ends_with?(Addressable::URI.parse("/tagged/#{params[:tag]}").normalize)
end end
def filtered_status_page(params) def filtered_status_page
if params[:min_id].present? filtered_statuses.paginate_by_id(PAGE_SIZE, params_slice(:max_id, :min_id, :since_id))
filtered_statuses.paginate_by_min_id(PAGE_SIZE, params[:min_id]).reverse end
else
filtered_statuses.paginate_by_max_id(PAGE_SIZE, params[:max_id], params[:since_id]).to_a def params_slice(*keys)
end params.slice(*keys).permit(*keys)
end end
def restrict_fields_to def restrict_fields_to

@ -24,20 +24,23 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController
def set_size def set_size
case params[:id] case params[:id]
when 'featured' when 'featured'
@account.pinned_statuses.count @size = @account.pinned_statuses.count
else else
raise ActiveRecord::RecordNotFound not_found
end end
end end
def scope_for_collection def scope_for_collection
case params[:id] case params[:id]
when 'featured' when 'featured'
return Status.none if @account.blocking?(signed_request_account) # Because in public fetch mode we cache the response, there would be no
# benefit from performing the check below, since a blocked account or domain
@account.pinned_statuses # would likely be served the cache from the reverse proxy anyway
else if authorized_fetch_mode? && !signed_request_account.nil? && (@account.blocking?(signed_request_account) || (!signed_request_account.domain.nil? && @account.domain_blocking?(signed_request_account.domain)))
raise ActiveRecord::RecordNotFound Status.none
else
@account.pinned_statuses
end
end end
end end

@ -11,7 +11,7 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
before_action :set_cache_headers before_action :set_cache_headers
def show def show
expires_in(page_requested? ? 0 : 3.minutes, public: public_fetch_mode?) expires_in(page_requested? ? 0 : 3.minutes, public: public_fetch_mode? && !(signed_request_account.present? && page_requested?))
render json: outbox_presenter, serializer: ActivityPub::OutboxSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json' render json: outbox_presenter, serializer: ActivityPub::OutboxSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json'
end end
@ -50,12 +50,12 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
return unless page_requested? return unless page_requested?
@statuses = @account.statuses.permitted_for(@account, signed_request_account) @statuses = @account.statuses.permitted_for(@account, signed_request_account)
@statuses = params[:min_id].present? ? @statuses.paginate_by_min_id(LIMIT, params[:min_id]).reverse : @statuses.paginate_by_max_id(LIMIT, params[:max_id]) @statuses = @statuses.paginate_by_id(LIMIT, params_slice(:max_id, :min_id, :since_id))
@statuses = cache_collection(@statuses, Status) @statuses = cache_collection(@statuses, Status)
end end
def page_requested? def page_requested?
params[:page] == 'true' truthy_param?(:page)
end end
def page_params def page_params

@ -1,7 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
class ActivityPub::RepliesController < ActivityPub::BaseController class ActivityPub::RepliesController < ActivityPub::BaseController
include SignatureAuthentication include SignatureVerification
include Authorization include Authorization
include AccountOwnedConcern include AccountOwnedConcern
@ -19,15 +19,19 @@ class ActivityPub::RepliesController < ActivityPub::BaseController
private private
def pundit_user
signed_request_account
end
def set_status def set_status
@status = @account.statuses.find(params[:status_id]) @status = @account.statuses.find(params[:status_id])
authorize @status, :show? authorize @status, :show?
rescue Mastodon::NotPermittedError rescue Mastodon::NotPermittedError
raise ActiveRecord::RecordNotFound not_found
end end
def set_replies def set_replies
@replies = page_params[:only_other_accounts] ? Status.where.not(account_id: @account.id) : @account.statuses @replies = only_other_accounts? ? Status.where.not(account_id: @account.id) : @account.statuses
@replies = @replies.where(in_reply_to_id: @status.id, visibility: [:public, :unlisted]) @replies = @replies.where(in_reply_to_id: @status.id, visibility: [:public, :unlisted])
@replies = @replies.paginate_by_min_id(DESCENDANTS_LIMIT, params[:min_id]) @replies = @replies.paginate_by_min_id(DESCENDANTS_LIMIT, params[:min_id])
end end
@ -38,7 +42,7 @@ class ActivityPub::RepliesController < ActivityPub::BaseController
type: :unordered, type: :unordered,
part_of: account_status_replies_url(@account, @status), part_of: account_status_replies_url(@account, @status),
next: next_page, next: next_page,
items: @replies.map { |status| status.local ? status : status.uri } items: @replies.map { |status| status.local? ? status : status.uri }
) )
return page if page_requested? return page if page_requested?
@ -51,16 +55,21 @@ class ActivityPub::RepliesController < ActivityPub::BaseController
end end
def page_requested? def page_requested?
params[:page] == 'true' truthy_param?(:page)
end
def only_other_accounts?
truthy_param?(:only_other_accounts)
end end
def next_page def next_page
only_other_accounts = !(@replies&.last&.account_id == @account.id && @replies.size == DESCENDANTS_LIMIT) only_other_accounts = !(@replies&.last&.account_id == @account.id && @replies.size == DESCENDANTS_LIMIT)
account_status_replies_url( account_status_replies_url(
@account, @account,
@status, @status,
page: true, page: true,
min_id: only_other_accounts && !page_params[:only_other_accounts] ? nil : @replies&.last&.id, min_id: only_other_accounts && !only_other_accounts? ? nil : @replies&.last&.id,
only_other_accounts: only_other_accounts only_other_accounts: only_other_accounts
) )
end end

@ -18,7 +18,7 @@ class Api::V1::Polls::VotesController < Api::BaseController
@poll = Poll.attached.find(params[:poll_id]) @poll = Poll.attached.find(params[:poll_id])
authorize @poll.status, :show? authorize @poll.status, :show?
rescue Mastodon::NotPermittedError rescue Mastodon::NotPermittedError
raise ActiveRecord::RecordNotFound not_found
end end
def vote_params def vote_params

@ -17,7 +17,7 @@ class Api::V1::PollsController < Api::BaseController
@poll = Poll.attached.find(params[:id]) @poll = Poll.attached.find(params[:id])
authorize @poll.status, :show? authorize @poll.status, :show?
rescue Mastodon::NotPermittedError rescue Mastodon::NotPermittedError
raise ActiveRecord::RecordNotFound not_found
end end
def refresh_poll def refresh_poll

@ -4,6 +4,7 @@ class Api::V1::Push::SubscriptionsController < Api::BaseController
before_action -> { doorkeeper_authorize! :push } before_action -> { doorkeeper_authorize! :push }
before_action :require_user! before_action :require_user!
before_action :set_web_push_subscription before_action :set_web_push_subscription
before_action :check_web_push_subscription, only: [:show, :update]
def create def create
@web_subscription&.destroy! @web_subscription&.destroy!
@ -21,16 +22,11 @@ class Api::V1::Push::SubscriptionsController < Api::BaseController
end end
def show def show
raise ActiveRecord::RecordNotFound if @web_subscription.nil?
render json: @web_subscription, serializer: REST::WebPushSubscriptionSerializer render json: @web_subscription, serializer: REST::WebPushSubscriptionSerializer
end end
def update def update
raise ActiveRecord::RecordNotFound if @web_subscription.nil?
@web_subscription.update!(data: data_params) @web_subscription.update!(data: data_params)
render json: @web_subscription, serializer: REST::WebPushSubscriptionSerializer render json: @web_subscription, serializer: REST::WebPushSubscriptionSerializer
end end
@ -45,12 +41,17 @@ class Api::V1::Push::SubscriptionsController < Api::BaseController
@web_subscription = ::Web::PushSubscription.find_by(access_token_id: doorkeeper_token.id) @web_subscription = ::Web::PushSubscription.find_by(access_token_id: doorkeeper_token.id)
end end
def check_web_push_subscription
not_found if @web_subscription.nil?
end
def subscription_params def subscription_params
params.require(:subscription).permit(:endpoint, keys: [:auth, :p256dh]) params.require(:subscription).permit(:endpoint, keys: [:auth, :p256dh])
end end
def data_params def data_params
return {} if params[:data].blank? return {} if params[:data].blank?
params.require(:data).permit(alerts: [:follow, :follow_request, :favourite, :reblog, :mention, :poll]) params.require(:data).permit(alerts: [:follow, :follow_request, :favourite, :reblog, :mention, :poll])
end end
end end

@ -28,8 +28,7 @@ class Api::V1::Statuses::MutesController < Api::BaseController
@status = Status.find(params[:status_id]) @status = Status.find(params[:status_id])
authorize @status, :show? authorize @status, :show?
rescue Mastodon::NotPermittedError rescue Mastodon::NotPermittedError
# Reraise in order to get a 404 instead of a 403 error code not_found
raise ActiveRecord::RecordNotFound
end end
def set_conversation def set_conversation

@ -68,7 +68,7 @@ class Api::V1::StatusesController < Api::BaseController
@status = Status.find(params[:id]) @status = Status.find(params[:id])
authorize @status, :show? authorize @status, :show?
rescue Mastodon::NotPermittedError rescue Mastodon::NotPermittedError
raise ActiveRecord::RecordNotFound not_found
end end
def set_thread def set_thread

@ -33,7 +33,7 @@ class MediaController < ApplicationController
def verify_permitted_status! def verify_permitted_status!
authorize @media_attachment.status, :show? authorize @media_attachment.status, :show?
rescue Mastodon::NotPermittedError rescue Mastodon::NotPermittedError
raise ActiveRecord::RecordNotFound not_found
end end
def check_playable def check_playable

@ -42,7 +42,7 @@ class RemoteInteractionController < ApplicationController
@status = Status.find(params[:id]) @status = Status.find(params[:id])
authorize @status, :show? authorize @status, :show?
rescue Mastodon::NotPermittedError rescue Mastodon::NotPermittedError
raise ActiveRecord::RecordNotFound not_found
end end
def set_body_classes def set_body_classes

@ -49,7 +49,7 @@ class StatusesController < ApplicationController
def embed def embed
use_pack 'embed' use_pack 'embed'
return not_found if @status.hidden? return not_found if @status.hidden? || @status.reblog?
expires_in 180, public: true expires_in 180, public: true
response.headers['X-Frame-Options'] = 'ALLOWALL' response.headers['X-Frame-Options'] = 'ALLOWALL'

@ -10,7 +10,7 @@ delegate(document, '#account_display_name', 'input', ({ target }) => {
if (target.value) { if (target.value) {
name.innerHTML = emojify(escapeTextContentForBrowser(target.value)); name.innerHTML = emojify(escapeTextContentForBrowser(target.value));
} else { } else {
name.textContent = document.querySelector('#default_account_display_name').textContent; name.textContent = name.textContent = target.dataset.default;
} }
} }
}); });

@ -55,7 +55,7 @@ export function updateTimeline(timeline, status, accept) {
export function deleteFromTimelines(id) { export function deleteFromTimelines(id) {
return (dispatch, getState) => { return (dispatch, getState) => {
const accountId = getState().getIn(['statuses', id, 'account']); const accountId = getState().getIn(['statuses', id, 'account']);
const references = getState().get('statuses').filter(status => status.get('reblog') === id).map(status => [status.get('id'), status.get('account')]); const references = getState().get('statuses').filter(status => status.get('reblog') === id).map(status => status.get('id'));
const reblogOf = getState().getIn(['statuses', id, 'reblog'], null); const reblogOf = getState().getIn(['statuses', id, 'reblog'], null);
dispatch({ dispatch({

@ -46,7 +46,7 @@ class DropdownMenu extends React.PureComponent {
document.addEventListener('keydown', this.handleKeyDown, false); document.addEventListener('keydown', this.handleKeyDown, false);
document.addEventListener('touchend', this.handleDocumentClick, listenerOptions); document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
if (this.focusedItem && this.props.openedViaKeyboard) { if (this.focusedItem && this.props.openedViaKeyboard) {
this.focusedItem.focus(); this.focusedItem.focus({ preventScroll: true });
} }
this.setState({ mounted: true }); this.setState({ mounted: true });
} }

@ -64,9 +64,9 @@ export default class ComposerOptionsDropdownContent extends React.PureComponent
document.addEventListener('click', this.handleDocumentClick, false); document.addEventListener('click', this.handleDocumentClick, false);
document.addEventListener('touchend', this.handleDocumentClick, withPassive); document.addEventListener('touchend', this.handleDocumentClick, withPassive);
if (this.focusedItem) { if (this.focusedItem) {
this.focusedItem.focus(); this.focusedItem.focus({ preventScroll: true });
} else { } else {
this.node.firstChild.focus(); this.node.firstChild.focus({ preventScroll: true });
} }
this.setState({ mounted: true }); this.setState({ mounted: true });
} }

@ -99,15 +99,13 @@ function main() {
delegate(document, '.custom-emoji', 'mouseout', getEmojiAnimationHandler('data-static')); delegate(document, '.custom-emoji', 'mouseout', getEmojiAnimationHandler('data-static'));
delegate(document, '.status__content__spoiler-link', 'click', function() { delegate(document, '.status__content__spoiler-link', 'click', function() {
const contentEl = this.parentNode.parentNode.querySelector('.e-content'); const statusEl = this.parentNode.parentNode;
if (contentEl.style.display === 'block') { if (statusEl.dataset.spoiler === 'expanded') {
contentEl.style.display = 'none'; statusEl.dataset.spoiler = 'folded';
this.parentNode.style.marginBottom = 0;
this.textContent = (new IntlMessageFormat(messages['status.show_more'] || 'Show more', locale)).format(); this.textContent = (new IntlMessageFormat(messages['status.show_more'] || 'Show more', locale)).format();
} else { } else {
contentEl.style.display = 'block'; statusEl.dataset.spoiler = 'expanded';
this.parentNode.style.marginBottom = null;
this.textContent = (new IntlMessageFormat(messages['status.show_less'] || 'Show less', locale)).format(); this.textContent = (new IntlMessageFormat(messages['status.show_less'] || 'Show less', locale)).format();
} }
@ -115,8 +113,8 @@ function main() {
}); });
[].forEach.call(document.querySelectorAll('.status__content__spoiler-link'), (spoilerLink) => { [].forEach.call(document.querySelectorAll('.status__content__spoiler-link'), (spoilerLink) => {
const contentEl = spoilerLink.parentNode.parentNode.querySelector('.e-content'); const statusEl = spoilerLink.parentNode.parentNode;
const message = (contentEl.style.display === 'block') ? (messages['status.show_less'] || 'Show less') : (messages['status.show_more'] || 'Show more'); const message = (statusEl.dataset.spoiler === 'expanded') ? (messages['status.show_less'] || 'Show less') : (messages['status.show_more'] || 'Show more');
spoilerLink.textContent = (new IntlMessageFormat(message, locale)).format(); spoilerLink.textContent = (new IntlMessageFormat(message, locale)).format();
}); });
}); });

@ -24,7 +24,7 @@ const importStatuses = (state, statuses) =>
const deleteStatus = (state, id, references) => { const deleteStatus = (state, id, references) => {
references.forEach(ref => { references.forEach(ref => {
state = deleteStatus(state, ref[0], []); state = deleteStatus(state, ref, []);
}); });
return state.delete(id); return state.delete(id);

@ -94,7 +94,7 @@ const updateTimeline = (state, timeline, status, usePendingItems, filtered) => {
})); }));
}; };
const deleteStatus = (state, id, accountId, references, exclude_account = null) => { const deleteStatus = (state, id, references, exclude_account = null) => {
state.keySeq().forEach(timeline => { state.keySeq().forEach(timeline => {
if (exclude_account === null || (timeline !== `account:${exclude_account}` && !timeline.startsWith(`account:${exclude_account}:`))) { if (exclude_account === null || (timeline !== `account:${exclude_account}` && !timeline.startsWith(`account:${exclude_account}:`))) {
const helper = list => list.filterNot(item => item === id); const helper = list => list.filterNot(item => item === id);
@ -104,7 +104,7 @@ const deleteStatus = (state, id, accountId, references, exclude_account = null)
// Remove reblogs of deleted status // Remove reblogs of deleted status
references.forEach(ref => { references.forEach(ref => {
state = deleteStatus(state, ref[0], ref[1], [], exclude_account); state = deleteStatus(state, ref, [], exclude_account);
}); });
return state; return state;
@ -122,8 +122,8 @@ const filterTimelines = (state, relationship, statuses) => {
return; return;
} }
references = statuses.filter(item => item.get('reblog') === status.get('id')).map(item => [item.get('id'), item.get('account')]); references = statuses.filter(item => item.get('reblog') === status.get('id')).map(item => item.get('id'));
state = deleteStatus(state, status.get('id'), status.get('account'), references, relationship.id); state = deleteStatus(state, status.get('id'), references, relationship.id);
}); });
return state; return state;
@ -155,7 +155,7 @@ export default function timelines(state = initialState, action) {
case TIMELINE_UPDATE: case TIMELINE_UPDATE:
return updateTimeline(state, action.timeline, fromJS(action.status), action.usePendingItems, action.filtered); return updateTimeline(state, action.timeline, fromJS(action.status), action.usePendingItems, action.filtered);
case TIMELINE_DELETE: case TIMELINE_DELETE:
return deleteStatus(state, action.id, action.accountId, action.references, action.reblogOf); return deleteStatus(state, action.id, action.references, action.reblogOf);
case TIMELINE_CLEAR: case TIMELINE_CLEAR:
return clearTimeline(state, action.timeline); return clearTimeline(state, action.timeline);
case ACCOUNT_BLOCK_SUCCESS: case ACCOUNT_BLOCK_SUCCESS:

@ -760,8 +760,13 @@ $small-breakpoint: 960px;
} }
} }
&__counters__wrapper {
display: flex;
}
&__counter { &__counter {
padding: 10px; padding: 10px;
width: 50%;
strong { strong {
font-family: $font-display, sans-serif; font-family: $font-display, sans-serif;

@ -567,6 +567,18 @@ body,
} }
} }
.special-action-button,
.back-link {
text-align: right;
flex: 1 1 auto;
}
.action-buttons {
display: flex;
overflow: hidden;
justify-content: space-between;
}
.spacer { .spacer {
flex: 1 1 auto; flex: 1 1 auto;
} }
@ -904,3 +916,11 @@ a.name-tag,
} }
} }
} }
.account-badges {
margin: -2px 0;
}
.dashboard__counters.admin-account-counters {
margin-top: 10px;
}

@ -150,3 +150,19 @@ button {
height: 100%; height: 100%;
} }
} }
.logo-resources {
display: none;
}
// NoScript adds a __ns__pop2top class to the full ancestry of blocked elements,
// to set the z-index to a high value, which messes with modals and dropdowns.
// Blocked elements can in theory only be media and frames/embeds, so they
// should only appear in statuses, under divs and articles.
body,
div,
article {
.__ns__pop2top {
z-index: unset !important;
}
}

@ -80,6 +80,12 @@
&-base { &-base {
@include avatar-radius(); @include avatar-radius();
@include avatar-size(36px); @include avatar-size(36px);
img {
@include avatar-radius;
width: 100%;
height: 100%;
}
} }
&-overlay { &-overlay {
@ -90,6 +96,12 @@
bottom: 0; bottom: 0;
right: 0; right: 0;
z-index: 1; z-index: 1;
img {
@include avatar-radius;
width: 100%;
height: 100%;
}
} }
} }

@ -133,6 +133,10 @@ code {
} }
} }
.otp-hint {
margin-bottom: 25px;
}
.card { .card {
margin-bottom: 15px; margin-bottom: 15px;
} }
@ -276,6 +280,14 @@ code {
margin-bottom: 25px; margin-bottom: 25px;
} }
} }
.fields-group.invited-by {
margin-bottom: 30px;
.hint {
text-align: center;
}
}
} }
.input.radio_buttons .radio label { .input.radio_buttons .radio label {
@ -626,6 +638,15 @@ code {
@media screen and (max-width: 740px) and (min-width: 441px) { @media screen and (max-width: 740px) and (min-width: 441px) {
margin-top: 40px; margin-top: 40px;
} }
&.translation-prompt {
text-align: unset;
color: unset;
a {
text-decoration: underline;
}
}
} }
.form-footer { .form-footer {

@ -25,6 +25,36 @@
} }
} }
progress {
border: 0;
display: block;
width: 100%;
height: 5px;
appearance: none;
background: transparent;
&::-webkit-progress-bar {
background: transparent;
}
// Those rules need to be entirely separate or they won't work, hence the
// duplication
&::-moz-progress-bar {
border-radius: 4px;
background: darken($ui-primary-color, 5%);
}
&::-ms-fill {
border-radius: 4px;
background: darken($ui-primary-color, 5%);
}
&::-webkit-progress-value {
border-radius: 4px;
background: darken($ui-primary-color, 5%);
}
}
&__option { &__option {
position: relative; position: relative;
display: flex; display: flex;

@ -124,6 +124,16 @@
.embed, .embed,
.public-layout { .public-layout {
.status__content[data-spoiler=folded] {
.e-content {
display: none;
}
p:first-child {
margin-bottom: 0;
}
}
.detailed-status { .detailed-status {
padding: 15px; padding: 15px;
} }
@ -162,6 +172,13 @@
.video-player { .video-player {
margin-top: 10px; margin-top: 10px;
} }
&__action-bar-button {
font-size: 18px;
width: 23.1429px;
height: 23.1429px;
line-height: 23.15px;
}
} }
} }

@ -42,7 +42,7 @@ export function updateTimeline(timeline, status, accept) {
export function deleteFromTimelines(id) { export function deleteFromTimelines(id) {
return (dispatch, getState) => { return (dispatch, getState) => {
const accountId = getState().getIn(['statuses', id, 'account']); const accountId = getState().getIn(['statuses', id, 'account']);
const references = getState().get('statuses').filter(status => status.get('reblog') === id).map(status => [status.get('id'), status.get('account')]); const references = getState().get('statuses').filter(status => status.get('reblog') === id).map(status => status.get('id'));
const reblogOf = getState().getIn(['statuses', id, 'reblog'], null); const reblogOf = getState().getIn(['statuses', id, 'reblog'], null);
dispatch({ dispatch({

@ -46,7 +46,7 @@ class DropdownMenu extends React.PureComponent {
document.addEventListener('keydown', this.handleKeyDown, false); document.addEventListener('keydown', this.handleKeyDown, false);
document.addEventListener('touchend', this.handleDocumentClick, listenerOptions); document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
if (this.focusedItem && this.props.openedViaKeyboard) { if (this.focusedItem && this.props.openedViaKeyboard) {
this.focusedItem.focus(); this.focusedItem.focus({ preventScroll: true });
} }
this.setState({ mounted: true }); this.setState({ mounted: true });
} }

@ -100,7 +100,7 @@ class PrivacyDropdownMenu extends React.PureComponent {
componentDidMount () { componentDidMount () {
document.addEventListener('click', this.handleDocumentClick, false); document.addEventListener('click', this.handleDocumentClick, false);
document.addEventListener('touchend', this.handleDocumentClick, listenerOptions); document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
if (this.focusedItem) this.focusedItem.focus(); if (this.focusedItem) this.focusedItem.focus({ preventScroll: true });
this.setState({ mounted: true }); this.setState({ mounted: true });
} }

@ -25,7 +25,7 @@ const importStatuses = (state, statuses) =>
const deleteStatus = (state, id, references) => { const deleteStatus = (state, id, references) => {
references.forEach(ref => { references.forEach(ref => {
state = deleteStatus(state, ref[0], []); state = deleteStatus(state, ref, []);
}); });
return state.delete(id); return state.delete(id);

@ -89,7 +89,7 @@ const updateTimeline = (state, timeline, status, usePendingItems) => {
})); }));
}; };
const deleteStatus = (state, id, accountId, references, exclude_account = null) => { const deleteStatus = (state, id, references, exclude_account = null) => {
state.keySeq().forEach(timeline => { state.keySeq().forEach(timeline => {
if (exclude_account === null || (timeline !== `account:${exclude_account}` && !timeline.startsWith(`account:${exclude_account}:`))) { if (exclude_account === null || (timeline !== `account:${exclude_account}` && !timeline.startsWith(`account:${exclude_account}:`))) {
const helper = list => list.filterNot(item => item === id); const helper = list => list.filterNot(item => item === id);
@ -99,7 +99,7 @@ const deleteStatus = (state, id, accountId, references, exclude_account = null)
// Remove reblogs of deleted status // Remove reblogs of deleted status
references.forEach(ref => { references.forEach(ref => {
state = deleteStatus(state, ref[0], ref[1], [], exclude_account); state = deleteStatus(state, ref, [], exclude_account);
}); });
return state; return state;
@ -117,8 +117,8 @@ const filterTimelines = (state, relationship, statuses) => {
return; return;
} }
references = statuses.filter(item => item.get('reblog') === status.get('id')).map(item => [item.get('id'), item.get('account')]); references = statuses.filter(item => item.get('reblog') === status.get('id')).map(item => item.get('id'));
state = deleteStatus(state, status.get('id'), status.get('account'), references, relationship.id); state = deleteStatus(state, status.get('id'), references, relationship.id);
}); });
return state; return state;
@ -150,7 +150,7 @@ export default function timelines(state = initialState, action) {
case TIMELINE_UPDATE: case TIMELINE_UPDATE:
return updateTimeline(state, action.timeline, fromJS(action.status), action.usePendingItems); return updateTimeline(state, action.timeline, fromJS(action.status), action.usePendingItems);
case TIMELINE_DELETE: case TIMELINE_DELETE:
return deleteStatus(state, action.id, action.accountId, action.references, action.reblogOf); return deleteStatus(state, action.id, action.references, action.reblogOf);
case TIMELINE_CLEAR: case TIMELINE_CLEAR:
return clearTimeline(state, action.timeline); return clearTimeline(state, action.timeline);
case ACCOUNT_BLOCK_SUCCESS: case ACCOUNT_BLOCK_SUCCESS:

@ -103,15 +103,13 @@ function main() {
delegate(document, '.custom-emoji', 'mouseout', getEmojiAnimationHandler('data-static')); delegate(document, '.custom-emoji', 'mouseout', getEmojiAnimationHandler('data-static'));
delegate(document, '.status__content__spoiler-link', 'click', function() { delegate(document, '.status__content__spoiler-link', 'click', function() {
const contentEl = this.parentNode.parentNode.querySelector('.e-content'); const statusEl = this.parentNode.parentNode;
if (contentEl.style.display === 'block') { if (statusEl.dataset.spoiler === 'expanded') {
contentEl.style.display = 'none'; statusEl.dataset.spoiler = 'folded';
this.parentNode.style.marginBottom = 0;
this.textContent = (new IntlMessageFormat(messages['status.show_more'] || 'Show more', locale)).format(); this.textContent = (new IntlMessageFormat(messages['status.show_more'] || 'Show more', locale)).format();
} else { } else {
contentEl.style.display = 'block'; statusEl.dataset.spoiler = 'expanded';
this.parentNode.style.marginBottom = null;
this.textContent = (new IntlMessageFormat(messages['status.show_less'] || 'Show less', locale)).format(); this.textContent = (new IntlMessageFormat(messages['status.show_less'] || 'Show less', locale)).format();
} }
@ -119,8 +117,8 @@ function main() {
}); });
[].forEach.call(document.querySelectorAll('.status__content__spoiler-link'), (spoilerLink) => { [].forEach.call(document.querySelectorAll('.status__content__spoiler-link'), (spoilerLink) => {
const contentEl = spoilerLink.parentNode.parentNode.querySelector('.e-content'); const statusEl = spoilerLink.parentNode.parentNode;
const message = (contentEl.style.display === 'block') ? (messages['status.show_less'] || 'Show less') : (messages['status.show_more'] || 'Show more'); const message = (statusEl.dataset.spoiler === 'expanded') ? (messages['status.show_less'] || 'Show less') : (messages['status.show_more'] || 'Show more');
spoilerLink.textContent = (new IntlMessageFormat(message, locale)).format(); spoilerLink.textContent = (new IntlMessageFormat(message, locale)).format();
}); });
}); });

@ -757,8 +757,13 @@ $small-breakpoint: 960px;
} }
} }
&__counters__wrapper {
display: flex;
}
&__counter { &__counter {
padding: 10px; padding: 10px;
width: 50%;
strong { strong {
font-family: $font-display, sans-serif; font-family: $font-display, sans-serif;

@ -583,6 +583,18 @@ body,
} }
} }
.special-action-button,
.back-link {
text-align: right;
flex: 1 1 auto;
}
.action-buttons {
display: flex;
overflow: hidden;
justify-content: space-between;
}
.spacer { .spacer {
flex: 1 1 auto; flex: 1 1 auto;
} }
@ -920,3 +932,11 @@ a.name-tag,
} }
} }
} }
.account-badges {
margin: -2px 0;
}
.dashboard__counters.admin-account-counters {
margin-top: 10px;
}

@ -229,3 +229,19 @@ button {
} }
} }
} }
.logo-resources {
display: none;
}
// NoScript adds a __ns__pop2top class to the full ancestry of blocked elements,
// to set the z-index to a high value, which messes with modals and dropdowns.
// Blocked elements can in theory only be media and frames/embeds, so they
// should only appear in statuses, under divs and articles.
body,
div,
article {
.__ns__pop2top {
z-index: unset !important;
}
}

@ -1362,6 +1362,12 @@ a .account__avatar {
&-base { &-base {
@include avatar-radius; @include avatar-radius;
@include avatar-size(36px); @include avatar-size(36px);
img {
@include avatar-radius;
width: 100%;
height: 100%;
}
} }
&-overlay { &-overlay {
@ -1372,6 +1378,12 @@ a .account__avatar {
bottom: 0; bottom: 0;
right: 0; right: 0;
z-index: 1; z-index: 1;
img {
@include avatar-radius;
width: 100%;
height: 100%;
}
} }
} }

@ -142,6 +142,10 @@ code {
} }
} }
.otp-hint {
margin-bottom: 25px;
}
.card { .card {
margin-bottom: 15px; margin-bottom: 15px;
} }
@ -285,6 +289,14 @@ code {
margin-bottom: 25px; margin-bottom: 25px;
} }
} }
.fields-group.invited-by {
margin-bottom: 30px;
.hint {
text-align: center;
}
}
} }
.input.radio_buttons .radio label { .input.radio_buttons .radio label {
@ -635,6 +647,15 @@ code {
@media screen and (max-width: 740px) and (min-width: 441px) { @media screen and (max-width: 740px) and (min-width: 441px) {
margin-top: 40px; margin-top: 40px;
} }
&.translation-prompt {
text-align: unset;
color: unset;
a {
text-decoration: underline;
}
}
} }
.form-footer { .form-footer {

@ -19,6 +19,36 @@
} }
} }
progress {
border: 0;
display: block;
width: 100%;
height: 5px;
appearance: none;
background: transparent;
&::-webkit-progress-bar {
background: transparent;
}
// Those rules need to be entirely separate or they won't work, hence the
// duplication
&::-moz-progress-bar {
border-radius: 4px;
background: darken($ui-primary-color, 5%);
}
&::-ms-fill {
border-radius: 4px;
background: darken($ui-primary-color, 5%);
}
&::-webkit-progress-value {
border-radius: 4px;
background: darken($ui-primary-color, 5%);
}
}
&__option { &__option {
position: relative; position: relative;
display: flex; display: flex;

@ -128,6 +128,16 @@
.embed, .embed,
.public-layout { .public-layout {
.status__content[data-spoiler=folded] {
.e-content {
display: none;
}
p:first-child {
margin-bottom: 0;
}
}
.detailed-status { .detailed-status {
padding: 15px; padding: 15px;
} }
@ -159,5 +169,12 @@
.video-player { .video-player {
margin-top: 10px; margin-top: 10px;
} }
&__action-bar-button {
font-size: 18px;
width: 23.1429px;
height: 23.1429px;
line-height: 23.15px;
}
} }
} }

@ -3,50 +3,52 @@
# #
# Table name: accounts # Table name: accounts
# #
# id :bigint(8) not null, primary key # id :bigint(8) not null, primary key
# username :string default(""), not null # username :string default(""), not null
# domain :string # domain :string
# secret :string default(""), not null # secret :string default(""), not null
# private_key :text # private_key :text
# public_key :text default(""), not null # public_key :text default(""), not null
# remote_url :string default(""), not null # remote_url :string default(""), not null
# salmon_url :string default(""), not null # salmon_url :string default(""), not null
# hub_url :string default(""), not null # hub_url :string default(""), not null
# created_at :datetime not null # created_at :datetime not null
# updated_at :datetime not null # updated_at :datetime not null
# note :text default(""), not null # note :text default(""), not null
# display_name :string default(""), not null # display_name :string default(""), not null
# uri :string default(""), not null # uri :string default(""), not null
# url :string # url :string
# avatar_file_name :string # avatar_file_name :string
# avatar_content_type :string # avatar_content_type :string
# avatar_file_size :integer # avatar_file_size :integer
# avatar_updated_at :datetime # avatar_updated_at :datetime
# header_file_name :string # header_file_name :string
# header_content_type :string # header_content_type :string
# header_file_size :integer # header_file_size :integer
# header_updated_at :datetime # header_updated_at :datetime
# avatar_remote_url :string # avatar_remote_url :string
# subscription_expires_at :datetime # subscription_expires_at :datetime
# locked :boolean default(FALSE), not null # locked :boolean default(FALSE), not null
# header_remote_url :string default(""), not null # header_remote_url :string default(""), not null
# last_webfingered_at :datetime # last_webfingered_at :datetime
# inbox_url :string default(""), not null # inbox_url :string default(""), not null
# outbox_url :string default(""), not null # outbox_url :string default(""), not null
# shared_inbox_url :string default(""), not null # shared_inbox_url :string default(""), not null
# followers_url :string default(""), not null # followers_url :string default(""), not null
# protocol :integer default("ostatus"), not null # protocol :integer default("ostatus"), not null
# memorial :boolean default(FALSE), not null # memorial :boolean default(FALSE), not null
# moved_to_account_id :bigint(8) # moved_to_account_id :bigint(8)
# featured_collection_url :string # featured_collection_url :string
# fields :jsonb # fields :jsonb
# actor_type :string # actor_type :string
# discoverable :boolean # discoverable :boolean
# also_known_as :string is an Array # also_known_as :string is an Array
# silenced_at :datetime # silenced_at :datetime
# suspended_at :datetime # suspended_at :datetime
# trust_level :integer # trust_level :integer
# hide_collections :boolean # hide_collections :boolean
# avatar_storage_schema_version :integer
# header_storage_schema_version :integer
# #
class Account < ApplicationRecord class Account < ApplicationRecord

@ -82,7 +82,7 @@ module Omniauthable
username = starting_username username = starting_username
i = 0 i = 0
while Account.exists?(username: username) while Account.exists?(username: username, domain: nil)
i += 1 i += 1
username = "#{starting_username}_#{i}" username = "#{starting_username}_#{i}"
end end

@ -3,20 +3,21 @@
# #
# Table name: custom_emojis # Table name: custom_emojis
# #
# id :bigint(8) not null, primary key # id :bigint(8) not null, primary key
# shortcode :string default(""), not null # shortcode :string default(""), not null
# domain :string # domain :string
# image_file_name :string # image_file_name :string
# image_content_type :string # image_content_type :string
# image_file_size :integer # image_file_size :integer
# image_updated_at :datetime # image_updated_at :datetime
# created_at :datetime not null # created_at :datetime not null
# updated_at :datetime not null # updated_at :datetime not null
# disabled :boolean default(FALSE), not null # disabled :boolean default(FALSE), not null
# uri :string # uri :string
# image_remote_url :string # image_remote_url :string
# visible_in_picker :boolean default(TRUE), not null # visible_in_picker :boolean default(TRUE), not null
# category_id :bigint(8) # category_id :bigint(8)
# image_storage_schema_version :integer
# #
class CustomEmoji < ApplicationRecord class CustomEmoji < ApplicationRecord

@ -3,23 +3,24 @@
# #
# Table name: media_attachments # Table name: media_attachments
# #
# id :bigint(8) not null, primary key # id :bigint(8) not null, primary key
# status_id :bigint(8) # status_id :bigint(8)
# file_file_name :string # file_file_name :string
# file_content_type :string # file_content_type :string
# file_file_size :integer # file_file_size :integer
# file_updated_at :datetime # file_updated_at :datetime
# remote_url :string default(""), not null # remote_url :string default(""), not null
# created_at :datetime not null # created_at :datetime not null
# updated_at :datetime not null # updated_at :datetime not null
# shortcode :string # shortcode :string
# type :integer default("image"), not null # type :integer default("image"), not null
# file_meta :json # file_meta :json
# account_id :bigint(8) # account_id :bigint(8)
# description :text # description :text
# scheduled_status_id :bigint(8) # scheduled_status_id :bigint(8)
# blurhash :string # blurhash :string
# processing :integer # processing :integer
# file_storage_schema_version :integer
# #
class MediaAttachment < ApplicationRecord class MediaAttachment < ApplicationRecord

@ -3,25 +3,26 @@
# #
# Table name: preview_cards # Table name: preview_cards
# #
# id :bigint(8) not null, primary key # id :bigint(8) not null, primary key
# url :string default(""), not null # url :string default(""), not null
# title :string default(""), not null # title :string default(""), not null
# description :string default(""), not null # description :string default(""), not null
# image_file_name :string # image_file_name :string
# image_content_type :string # image_content_type :string
# image_file_size :integer # image_file_size :integer
# image_updated_at :datetime # image_updated_at :datetime
# type :integer default("link"), not null # type :integer default("link"), not null
# html :text default(""), not null # html :text default(""), not null
# author_name :string default(""), not null # author_name :string default(""), not null
# author_url :string default(""), not null # author_url :string default(""), not null
# provider_name :string default(""), not null # provider_name :string default(""), not null
# provider_url :string default(""), not null # provider_url :string default(""), not null
# width :integer default(0), not null # width :integer default(0), not null
# height :integer default(0), not null # height :integer default(0), not null
# created_at :datetime not null # created_at :datetime not null
# updated_at :datetime not null # updated_at :datetime not null
# embed_url :string default(""), not null # embed_url :string default(""), not null
# image_storage_schema_version :integer
# #
class PreviewCard < ApplicationRecord class PreviewCard < ApplicationRecord
@ -47,6 +48,10 @@ class PreviewCard < ApplicationRecord
before_save :extract_dimensions, if: :link? before_save :extract_dimensions, if: :link?
def local?
false
end
def missing_image? def missing_image?
width.present? && height.present? && image_file_name.blank? width.present? && height.present? && image_file_name.blank?
end end

@ -206,12 +206,8 @@ class Status < ApplicationRecord
def title def title
if destroyed? if destroyed?
"#{account.acct} deleted status" "#{account.acct} deleted status"
elsif reblog?
preview = sensitive ? '<sensitive>' : text.slice(0, 10).split("\n")[0]
"#{account.acct} shared #{reblog.account.acct}'s: #{preview}"
else else
preview = sensitive ? '<sensitive>' : text.slice(0, 20).split("\n")[0] reblog? ? "#{account.acct} shared a status by #{reblog.account.acct}" : "New status by #{account.acct}"
"#{account.acct}: #{preview}"
end end
end end
@ -404,7 +400,7 @@ class Status < ApplicationRecord
if account.nil? if account.nil?
where(visibility: visibility).not_local_only where(visibility: visibility).not_local_only
elsif target_account.blocking?(account) # get rid of blocked peeps elsif target_account.blocking?(account) || (account.domain.present? && target_account.domain_blocking?(account.domain)) # get rid of blocked peeps
none none
elsif account.id == target_account.id # author can see own stuff elsif account.id == target_account.id # author can see own stuff
all all

@ -5,7 +5,7 @@ class REST::InstanceSerializer < ActiveModel::Serializer
attributes :uri, :title, :short_description, :description, :email, attributes :uri, :title, :short_description, :description, :email,
:version, :urls, :stats, :thumbnail, :max_toot_chars, :poll_limits, :version, :urls, :stats, :thumbnail, :max_toot_chars, :poll_limits,
:languages, :registrations, :approval_required :languages, :registrations, :approval_required, :invites_enabled
has_one :contact_account, serializer: REST::AccountSerializer has_one :contact_account, serializer: REST::AccountSerializer
@ -76,6 +76,10 @@ class REST::InstanceSerializer < ActiveModel::Serializer
Setting.registrations_mode == 'approved' Setting.registrations_mode == 'approved'
end end
def invites_enabled
Setting.min_invite_role == 'user'
end
private private
def instance_presenter def instance_presenter

@ -25,7 +25,18 @@ class FetchResourceService < BaseService
end end
def perform_request(&block) def perform_request(&block)
Request.new(:get, @url).add_headers('Accept' => ACCEPT_HEADER).on_behalf_of(Account.representative).perform(&block) Request.new(:get, @url).tap do |request|
request.add_headers('Accept' => ACCEPT_HEADER)
# In a real setting we want to sign all outgoing requests,
# in case the remote server has secure mode enabled and requires
# authentication on all resources. However, during development,
# sending request signatures with an inaccessible host is useless
# and prevents even public resources from being fetched, so
# don't do it
request.on_behalf_of(Account.representative) unless Rails.env.development?
end.perform(&block)
end end
def process_response(response, terminal = false) def process_response(response, terminal = false)

@ -68,11 +68,11 @@
.hero-widget__footer__column .hero-widget__footer__column
%h4= t 'about.server_stats' %h4= t 'about.server_stats'
%div{ style: 'display: flex' } .hero-widget__counters__wrapper
.hero-widget__counter{ style: 'width: 50%' } .hero-widget__counter
%strong= number_to_human @instance_presenter.user_count, strip_insignificant_zeros: true %strong= number_to_human @instance_presenter.user_count, strip_insignificant_zeros: true
%span= t 'about.user_count_after', count: @instance_presenter.user_count %span= t 'about.user_count_after', count: @instance_presenter.user_count
.hero-widget__counter{ style: 'width: 50%' } .hero-widget__counter
%strong= number_to_human @instance_presenter.active_user_count, strip_insignificant_zeros: true %strong= number_to_human @instance_presenter.active_user_count, strip_insignificant_zeros: true
%span %span
= t 'about.active_count_after' = t 'about.active_count_after'

@ -9,8 +9,10 @@
= link_to ActivityPub::TagManager.instance.url_for(moved_to_account), class: 'detailed-status__display-name p-author h-card', target: '_blank', rel: 'me noopener noreferrer' do = link_to ActivityPub::TagManager.instance.url_for(moved_to_account), class: 'detailed-status__display-name p-author h-card', target: '_blank', rel: 'me noopener noreferrer' do
.detailed-status__display-avatar .detailed-status__display-avatar
.account__avatar-overlay .account__avatar-overlay
.account__avatar-overlay-base{ style: "background-image: url('#{moved_to_account.avatar.url(:original)}')" } .account__avatar-overlay-base
.account__avatar-overlay-overlay{ style: "background-image: url('#{account.avatar.url(:original)}')" } = image_tag moved_to_account.avatar_static_url
.account__avatar-overlay-overlay
= image_tag account.avatar_static_url
%span.display-name %span.display-name
%bdi %bdi

@ -2,7 +2,7 @@
%td %td
= admin_account_link_to(account) = admin_account_link_to(account)
%td %td
%div{ style: 'margin: -2px 0' }= account_badge(account, all: true) %div.account-badges= account_badge(account, all: true)
%td %td
- if account.user_current_sign_in_ip - if account.user_current_sign_in_ip
%samp.ellipsized-ip{ title: account.user_current_sign_in_ip }= account.user_current_sign_in_ip %samp.ellipsized-ip{ title: account.user_current_sign_in_ip }= account.user_current_sign_in_ip

@ -31,7 +31,7 @@
%div %div
.account__header__content.emojify= Formatter.instance.simplified_format(account, custom_emojify: true) .account__header__content.emojify= Formatter.instance.simplified_format(account, custom_emojify: true)
.dashboard__counters{ style: 'margin-top: 10px' } .dashboard__counters.admin-account-counters
%div %div
= link_to admin_account_statuses_path(@account.id) do = link_to admin_account_statuses_path(@account.id) do
.dashboard__counters__num= number_with_delimiter @account.statuses_count .dashboard__counters__num= number_with_delimiter @account.statuses_count
@ -178,18 +178,8 @@
= @account.shared_inbox_url = @account.shared_inbox_url
= fa_icon DeliveryFailureTracker.available?(@account.shared_inbox_url) ? 'check': 'times' = fa_icon DeliveryFailureTracker.available?(@account.shared_inbox_url) ? 'check': 'times'
%div{ style: 'overflow: hidden' } %div.action-buttons
%div{ style: 'float: right' } %div
- if @account.local?
= link_to t('admin.accounts.reset_password'), admin_account_reset_path(@account.id), method: :create, class: 'button' if can?(:reset_password, @account.user)
- if @account.user&.otp_required_for_login?
= link_to t('admin.accounts.disable_two_factor_authentication'), admin_user_two_factor_authentication_path(@account.user.id), method: :delete, class: 'button' if can?(:disable_2fa, @account.user)
- if !@account.memorial? && @account.user_approved?
= link_to t('admin.accounts.memorialize'), memorialize_admin_account_path(@account.id), method: :post, data: { confirm: t('admin.accounts.are_you_sure') }, class: 'button button--destructive' if can?(:memorialize, @account)
- else
= link_to t('admin.accounts.redownload'), redownload_admin_account_path(@account.id), method: :post, class: 'button' if can?(:redownload, @account)
%div{ style: 'float: left' }
- if @account.local? && @account.user_approved? - if @account.local? && @account.user_approved?
= link_to t('admin.accounts.warn'), new_admin_account_action_path(@account.id, type: 'none'), class: 'button' if can?(:warn, @account) = link_to t('admin.accounts.warn'), new_admin_account_action_path(@account.id, type: 'none'), class: 'button' if can?(:warn, @account)
- if @account.silenced? - if @account.silenced?
@ -216,6 +206,16 @@
- else - else
= link_to t('admin.domain_blocks.add_new'), new_admin_domain_block_path(_domain: @account.domain), class: 'button button--destructive' = link_to t('admin.domain_blocks.add_new'), new_admin_domain_block_path(_domain: @account.domain), class: 'button button--destructive'
%div
- if @account.local?
= link_to t('admin.accounts.reset_password'), admin_account_reset_path(@account.id), method: :create, class: 'button' if can?(:reset_password, @account.user)
- if @account.user&.otp_required_for_login?
= link_to t('admin.accounts.disable_two_factor_authentication'), admin_user_two_factor_authentication_path(@account.user.id), method: :delete, class: 'button' if can?(:disable_2fa, @account.user)
- if !@account.memorial? && @account.user_approved?
= link_to t('admin.accounts.memorialize'), memorialize_admin_account_path(@account.id), method: :post, data: { confirm: t('admin.accounts.are_you_sure') }, class: 'button button--destructive' if can?(:memorialize, @account)
- else
= link_to t('admin.accounts.redownload'), redownload_admin_account_path(@account.id), method: :post, class: 'button' if can?(:redownload, @account)
%hr.spacer/ %hr.spacer/
- unless @warnings.empty? - unless @warnings.empty?

@ -10,7 +10,7 @@
- unless whitelist_mode? - unless whitelist_mode?
%li= filter_link_to t('admin.instances.moderation.limited'), limited: '1' %li= filter_link_to t('admin.instances.moderation.limited'), limited: '1'
%div{ style: 'flex: 1 1 auto; text-align: right' } %div.special-action-button
- if whitelist_mode? - if whitelist_mode?
= link_to t('admin.domain_allows.add_new'), new_admin_domain_allow_path, class: 'button' = link_to t('admin.domain_allows.add_new'), new_admin_domain_allow_path, class: 'button'
- else - else

@ -45,11 +45,11 @@
%hr.spacer/ %hr.spacer/
%div{ style: 'overflow: hidden' } %div.action-buttons
%div{ style: 'float: left' } %div
= link_to t('admin.accounts.title'), admin_accounts_path(remote: '1', by_domain: @instance.domain), class: 'button' = link_to t('admin.accounts.title'), admin_accounts_path(remote: '1', by_domain: @instance.domain), class: 'button'
%div{ style: 'float: right' } %div
- if @domain_allow - if @domain_allow
= link_to t('admin.domain_allows.undo'), admin_domain_allow_path(@domain_allow), class: 'button button--destructive', data: { confirm: t('admin.accounts.are_you_sure'), method: :delete } = link_to t('admin.domain_allows.undo'), admin_domain_allow_path(@domain_allow), class: 'button button--destructive', data: { confirm: t('admin.accounts.are_you_sure'), method: :delete }
- elsif @domain_block - elsif @domain_block

@ -22,9 +22,9 @@
%hr.spacer/ %hr.spacer/
%div{ style: 'overflow: hidden' } %div.action-buttons
%div{ style: 'float: right' }
= link_to t('admin.accounts.reject_all'), reject_all_admin_pending_accounts_path, method: :post, data: { confirm: t('admin.accounts.are_you_sure') }, class: 'button button--destructive'
%div %div
= link_to t('admin.accounts.approve_all'), approve_all_admin_pending_accounts_path, method: :post, data: { confirm: t('admin.accounts.are_you_sure') }, class: 'button' = link_to t('admin.accounts.approve_all'), approve_all_admin_pending_accounts_path, method: :post, data: { confirm: t('admin.accounts.are_you_sure') }, class: 'button'
%div
= link_to t('admin.accounts.reject_all'), reject_all_admin_pending_accounts_path, method: :post, data: { confirm: t('admin.accounts.are_you_sure') }, class: 'button button--destructive'

@ -17,7 +17,7 @@
%li= filter_link_to t('admin.accounts.location.local'), location: 'local' %li= filter_link_to t('admin.accounts.location.local'), location: 'local'
%li= filter_link_to t('admin.accounts.location.remote'), location: 'remote' %li= filter_link_to t('admin.accounts.location.remote'), location: 'remote'
.back-link{ style: 'flex: 1 1 auto; text-align: right' } .back-link
= link_to admin_account_path(@account.id) do = link_to admin_account_path(@account.id) do
= fa_icon 'chevron-left fw' = fa_icon 'chevron-left fw'
= t('admin.statuses.back_to_account') = t('admin.statuses.back_to_account')

@ -65,9 +65,11 @@
%hr.spacer %hr.spacer
%div{ style: 'overflow: hidden; margin-bottom: 20px; clear: both' } %div.action-buttons
%div
- if @report.unresolved? - if @report.unresolved?
%div{ style: 'float: right' } %div
- if @report.target_account.local? - if @report.target_account.local?
= link_to t('admin.accounts.warn'), new_admin_account_action_path(@report.target_account_id, type: 'none', report_id: @report.id), class: 'button' = link_to t('admin.accounts.warn'), new_admin_account_action_path(@report.target_account_id, type: 'none', report_id: @report.id), class: 'button'
= link_to t('admin.accounts.disable'), new_admin_account_action_path(@report.target_account_id, type: 'disable', report_id: @report.id), class: 'button button--destructive' = link_to t('admin.accounts.disable'), new_admin_account_action_path(@report.target_account_id, type: 'disable', report_id: @report.id), class: 'button button--destructive'

@ -9,7 +9,7 @@
%ul %ul
%li= link_to t('admin.statuses.no_media'), admin_account_statuses_path(@account.id, current_params.merge(media: nil)), class: !params[:media] && 'selected' %li= link_to t('admin.statuses.no_media'), admin_account_statuses_path(@account.id, current_params.merge(media: nil)), class: !params[:media] && 'selected'
%li= link_to t('admin.statuses.with_media'), admin_account_statuses_path(@account.id, current_params.merge(media: true)), class: params[:media] && 'selected' %li= link_to t('admin.statuses.with_media'), admin_account_statuses_path(@account.id, current_params.merge(media: true)), class: params[:media] && 'selected'
.back-link{ style: 'flex: 1 1 auto; text-align: right' } .back-link
= link_to admin_account_path(@account.id) do = link_to admin_account_path(@account.id) do
= fa_icon 'chevron-left fw' = fa_icon 'chevron-left fw'
= t('admin.statuses.back_to_account') = t('admin.statuses.back_to_account')

@ -4,7 +4,7 @@
= "@#{@account.acct}" = "@#{@account.acct}"
.filters .filters
.back-link{ style: 'flex: 1 1 auto; text-align: right' } .back-link
= link_to admin_account_path(@account.id) do = link_to admin_account_path(@account.id) do
%i.fa.fa-chevron-left.fa-fw %i.fa.fa-chevron-left.fa-fw
= t('admin.statuses.back_to_account') = t('admin.statuses.back_to_account')

@ -68,9 +68,9 @@
- if params[:pending_review] == '1' || params[:unreviewed] == '1' - if params[:pending_review] == '1' || params[:unreviewed] == '1'
%hr.spacer/ %hr.spacer/
%div{ style: 'overflow: hidden' } %div.action-buttons
%div{ style: 'float: right' }
= link_to t('admin.accounts.reject_all'), reject_all_admin_tags_path, method: :post, data: { confirm: t('admin.accounts.are_you_sure') }, class: 'button button--destructive'
%div %div
= link_to t('admin.accounts.approve_all'), approve_all_admin_tags_path, method: :post, data: { confirm: t('admin.accounts.are_you_sure') }, class: 'button' = link_to t('admin.accounts.approve_all'), approve_all_admin_tags_path, method: :post, data: { confirm: t('admin.accounts.are_you_sure') }, class: 'button'
%div
= link_to t('admin.accounts.reject_all'), reject_all_admin_tags_path, method: :post, data: { confirm: t('admin.accounts.are_you_sure') }, class: 'button button--destructive'

@ -9,7 +9,6 @@
= image_tag account.avatar.url, alt: '', width: 48, height: 48, class: 'u-photo' = image_tag account.avatar.url, alt: '', width: 48, height: 48, class: 'u-photo'
.display-name .display-name
%span{ id: "default_account_display_name", style: "display: none" }= account.username
%bdi %bdi
%strong.emojify.p-name= display_name(account, custom_emojify: true) %strong.emojify.p-name= display_name(account, custom_emojify: true)
%span %span

@ -8,8 +8,8 @@
= render 'shared/error_messages', object: resource = render 'shared/error_messages', object: resource
- if @invite.present? && @invite.autofollow? - if @invite.present? && @invite.autofollow?
.fields-group{ style: 'margin-bottom: 30px' } .fields-group.invited-by
%p.hint{ style: 'text-align: center' }= t('invites.invited_by') %p.hint= t('invites.invited_by')
= render 'application/card', account: @invite.user.account = render 'application/card', account: @invite.user.account
= f.simple_fields_for :account do |ff| = f.simple_fields_for :account do |ff|

@ -2,7 +2,7 @@
= t('auth.login') = t('auth.login')
= simple_form_for(resource, as: resource_name, url: session_path(resource_name), method: :post) do |f| = simple_form_for(resource, as: resource_name, url: session_path(resource_name), method: :post) do |f|
%p.hint{ style: 'margin-bottom: 25px' }= t('simple_form.hints.sessions.otp') %p.hint.otp-hint= t('simple_form.hints.sessions.otp')
.fields-group .fields-group
= f.input :otp_attempt, type: :number, wrapper: :with_label, label: t('simple_form.labels.defaults.otp_attempt'), input_html: { 'aria-label' => t('simple_form.labels.defaults.otp_attempt'), :autocomplete => 'off' }, autofocus: true = f.input :otp_attempt, type: :number, wrapper: :with_label, label: t('simple_form.labels.defaults.otp_attempt'), input_html: { 'aria-label' => t('simple_form.labels.defaults.otp_attempt'), :autocomplete => 'off' }, autofocus: true

@ -28,7 +28,6 @@
= image_tag account.avatar.url, alt: '', width: 48, height: 48, class: 'u-photo' = image_tag account.avatar.url, alt: '', width: 48, height: 48, class: 'u-photo'
.display-name .display-name
%span{ id: "default_account_display_name", style: "display: none" }= account.username
%bdi %bdi
%strong.emojify.p-name= display_name(account, custom_emojify: true) %strong.emojify.p-name= display_name(account, custom_emojify: true)
%span= acct(account) %span= acct(account)

@ -40,6 +40,6 @@
%body{ class: body_classes } %body{ class: body_classes }
= content_for?(:content) ? yield(:content) : yield = content_for?(:content) ? yield(:content) : yield
%div{ style: 'display: none'} .logo-resources
= render file: Rails.root.join('app', 'javascript', 'images', 'logo_transparent.svg') = render file: Rails.root.join('app', 'javascript', 'images', 'logo_transparent.svg')
= render file: Rails.root.join('app', 'javascript', 'images', 'logo_full.svg') = render file: Rails.root.join('app', 'javascript', 'images', 'logo_full.svg')

@ -23,5 +23,5 @@
%body.embed %body.embed
= yield = yield
%div{ style: 'display: none'} .logo-resources
= render file: Rails.root.join('app', 'javascript', 'images', 'logo_transparent.svg') = render file: Rails.root.join('app', 'javascript', 'images', 'logo_transparent.svg')

@ -12,5 +12,5 @@
- else - else
%p= t('about.browse_local_posts') %p= t('about.browse_local_posts')
#mastodon-timeline{ data: { props: Oj.dump(default_props) }} #mastodon-timeline{ data: { props: Oj.dump(default_props.merge(local: !Setting.show_known_fediverse_at_about_page)) }}
#modal-container #modal-container

@ -9,8 +9,8 @@
= 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 = 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 - unless I18n.locale == :en
.flash-message{ style: "text-align: unset; color: unset" } .flash-message.translation-prompt
#{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")} #{t 'appearance.localization.body'} #{content_tag(:a, t('appearance.localization.guide_link_text'), href: t('appearance.localization.guide_link'), target: "_blank", rel: "noopener")}
%h4= t 'appearance.advanced_web_interface' %h4= t 'appearance.advanced_web_interface'

@ -9,7 +9,7 @@
.fields-row .fields-row
.fields-row__column.fields-group.fields-row__column-6 .fields-row__column.fields-group.fields-row__column-6
= f.input :display_name, wrapper: :with_label, input_html: { maxlength: Account::MAX_DISPLAY_NAME_LENGTH }, hint: false = f.input :display_name, wrapper: :with_label, input_html: { maxlength: Account::MAX_DISPLAY_NAME_LENGTH, data: { default: @account.username } }, hint: false
= f.input :note, wrapper: :with_label, input_html: { maxlength: Account::MAX_NOTE_LENGTH }, hint: false = f.input :note, wrapper: :with_label, input_html: { maxlength: Account::MAX_NOTE_LENGTH }, hint: false
.fields-row .fields-row

@ -15,12 +15,12 @@
= account_action_button(status.account) = account_action_button(status.account)
.status__content.emojify< .status__content.emojify{ :data => ({ spoiler: current_account&.user&.setting_expand_spoilers ? 'expanded' : 'folded' } if status.spoiler_text?) }<
- if status.spoiler_text? - if status.spoiler_text?
%p{ :style => ('margin-bottom: 0' unless current_account&.user&.setting_expand_spoilers) }< %p<
%span.p-summary> #{Formatter.instance.format_spoiler(status, autoplay: autoplay)}&nbsp; %span.p-summary> #{Formatter.instance.format_spoiler(status, autoplay: autoplay)}&nbsp;
%button.status__content__spoiler-link= t('statuses.show_more') %button.status__content__spoiler-link= t('statuses.show_more')
.e-content{ style: "display: #{!current_account&.user&.setting_expand_spoilers && status.spoiler_text? ? 'none' : 'block'}; direction: #{rtl_status?(status) ? 'rtl' : 'ltr'}" } .e-content{ dir: rtl_status?(status) ? 'rtl' : 'ltr' }
= Formatter.instance.format(status, custom_emojify: true, autoplay: autoplay) = Formatter.instance.format(status, custom_emojify: true, autoplay: autoplay)
- if status.preloadable_poll - if status.preloadable_poll
= react_component :poll, disabled: true, poll: ActiveModelSerializers::SerializableResource.new(status.preloadable_poll, serializer: REST::PollSerializer, scope: current_user, scope_name: :current_user).as_json do = react_component :poll, disabled: true, poll: ActiveModelSerializers::SerializableResource.new(status.preloadable_poll, serializer: REST::PollSerializer, scope: current_user, scope_name: :current_user).as_json do

@ -10,13 +10,15 @@
- percent = total_votes_count > 0 ? 100 * option.votes_count / total_votes_count : 0 - percent = total_votes_count > 0 ? 100 * option.votes_count / total_votes_count : 0
%label.poll__option>< %label.poll__option><
%span.poll__number>< %span.poll__number><
- if own_votes.include?(index)
%i.poll__voted__mark.fa.fa-check
= "#{percent.round}%" = "#{percent.round}%"
%span.poll__option__text %span.poll__option__text
= Formatter.instance.format_poll_option(status, option, autoplay: autoplay) = Formatter.instance.format_poll_option(status, option, autoplay: autoplay)
- if own_votes.include?(index)
%span.poll__voted
%i.poll__voted__mark.fa.fa-check
%span.poll__chart{ style: "width: #{percent}%" } %progress{ max: 100, value: percent < 1 ? 1 : percent, 'aria-hidden': 'true' }
%span.poll__chart
- else - else
%label.poll__option>< %label.poll__option><
%span.poll__input{ class: poll.multiple? ? 'checkbox' : nil}>< %span.poll__input{ class: poll.multiple? ? 'checkbox' : nil}><

@ -19,12 +19,12 @@
%span.display-name__account %span.display-name__account
= acct(status.account) = acct(status.account)
= fa_icon('lock') if status.account.locked? = fa_icon('lock') if status.account.locked?
.status__content.emojify< .status__content.emojify{ :data => ({ spoiler: current_account&.user&.setting_expand_spoilers ? 'expanded' : 'folded' } if status.spoiler_text?) }<
- if status.spoiler_text? - if status.spoiler_text?
%p{ :style => ('margin-bottom: 0' unless current_account&.user&.setting_expand_spoilers) }< %p<
%span.p-summary> #{Formatter.instance.format_spoiler(status, autoplay: autoplay)}&nbsp; %span.p-summary> #{Formatter.instance.format_spoiler(status, autoplay: autoplay)}&nbsp;
%button.status__content__spoiler-link= t('statuses.show_more') %button.status__content__spoiler-link= t('statuses.show_more')
.e-content{ style: "display: #{!current_account&.user&.setting_expand_spoilers && status.spoiler_text? ? 'none' : 'block'}; direction: #{rtl_status?(status) ? 'rtl' : 'ltr'}" }< .e-content{ dir: rtl_status?(status) ? 'rtl' : 'ltr' }<
= Formatter.instance.format(status, custom_emojify: true, autoplay: autoplay) = Formatter.instance.format(status, custom_emojify: true, autoplay: autoplay)
- if status.preloadable_poll - if status.preloadable_poll
= react_component :poll, disabled: true, poll: ActiveModelSerializers::SerializableResource.new(status.preloadable_poll, serializer: REST::PollSerializer, scope: current_user, scope_name: :current_user).as_json do = react_component :poll, disabled: true, poll: ActiveModelSerializers::SerializableResource.new(status.preloadable_poll, serializer: REST::PollSerializer, scope: current_user, scope_name: :current_user).as_json do
@ -51,18 +51,18 @@
.status__action-bar .status__action-bar
.status__action-bar__counter .status__action-bar__counter
= link_to remote_interaction_path(status, type: :reply), class: 'status__action-bar-button icon-button modal-button', style: 'font-size: 18px; width: 23.1429px; height: 23.1429px; line-height: 23.15px;' do = link_to remote_interaction_path(status, type: :reply), class: 'status__action-bar-button icon-button modal-button' do
- if status.in_reply_to_id.nil? - if status.in_reply_to_id.nil?
= fa_icon 'reply fw' = fa_icon 'reply fw'
- else - else
= fa_icon 'reply-all fw' = fa_icon 'reply-all fw'
.status__action-bar__counter__label= obscured_counter status.replies_count .status__action-bar__counter__label= obscured_counter status.replies_count
= link_to remote_interaction_path(status, type: :reblog), class: 'status__action-bar-button icon-button modal-button', style: 'font-size: 18px; width: 23.1429px; height: 23.1429px; line-height: 23.15px;' do = link_to remote_interaction_path(status, type: :reblog), class: 'status__action-bar-button icon-button modal-button' do
- if status.distributable? - if status.distributable?
= fa_icon 'retweet fw' = fa_icon 'retweet fw'
- elsif status.private_visibility? || status.limited_visibility? - elsif status.private_visibility? || status.limited_visibility?
= fa_icon 'lock fw' = fa_icon 'lock fw'
- else - else
= fa_icon 'envelope fw' = fa_icon 'envelope fw'
= link_to remote_interaction_path(status, type: :favourite), class: 'status__action-bar-button icon-button modal-button', style: 'font-size: 18px; width: 23.1429px; height: 23.1429px; line-height: 23.15px;' do = link_to remote_interaction_path(status, type: :favourite), class: 'status__action-bar-button icon-button modal-button' do
= fa_icon 'star fw' = fa_icon 'star fw'

@ -10,9 +10,25 @@ Paperclip.interpolates :filename do |attachment, style|
end end
end end
Paperclip.interpolates :prefix_path do |attachment, style|
if attachment.storage_schema_version >= 1 && attachment.instance.respond_to?(:local?) && !attachment.instance.local?
'cache' + File::SEPARATOR
else
''
end
end
Paperclip.interpolates :prefix_url do |attachment, style|
if attachment.storage_schema_version >= 1 && attachment.instance.respond_to?(:local?) && !attachment.instance.local?
'cache/'
else
''
end
end
Paperclip::Attachment.default_options.merge!( Paperclip::Attachment.default_options.merge!(
use_timestamp: false, use_timestamp: false,
path: ':class/:attachment/:id_partition/:style/:filename', path: ':prefix_url:class/:attachment/:id_partition/:style/:filename',
storage: :fog storage: :fog
) )
@ -91,7 +107,7 @@ else
Paperclip::Attachment.default_options.merge!( Paperclip::Attachment.default_options.merge!(
storage: :filesystem, storage: :filesystem,
use_timestamp: true, use_timestamp: true,
path: File.join(ENV.fetch('PAPERCLIP_ROOT_PATH', File.join(':rails_root', 'public', 'system')), ':class', ':attachment', ':id_partition', ':style', ':filename'), path: File.join(ENV.fetch('PAPERCLIP_ROOT_PATH', File.join(':rails_root', 'public', 'system')), ':prefix_path:class', ':attachment', ':id_partition', ':style', ':filename'),
url: ENV.fetch('PAPERCLIP_ROOT_URL', '/system') + '/:class/:attachment/:id_partition/:style/:filename', url: ENV.fetch('PAPERCLIP_ROOT_URL', '/system') + '/:prefix_url:class/:attachment/:id_partition/:style/:filename',
) )
end end

@ -0,0 +1,9 @@
class AddStorageSchemaVersion < ActiveRecord::Migration[5.2]
def change
add_column :preview_cards, :image_storage_schema_version, :integer
add_column :accounts, :avatar_storage_schema_version, :integer
add_column :accounts, :header_storage_schema_version, :integer
add_column :media_attachments, :file_storage_schema_version, :integer
add_column :custom_emojis, :image_storage_schema_version, :integer
end
end

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2020_04_07_202420) do ActiveRecord::Schema.define(version: 2020_04_17_125749) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
@ -172,6 +172,8 @@ ActiveRecord::Schema.define(version: 2020_04_07_202420) do
t.datetime "suspended_at" t.datetime "suspended_at"
t.integer "trust_level" t.integer "trust_level"
t.boolean "hide_collections" t.boolean "hide_collections"
t.integer "avatar_storage_schema_version"
t.integer "header_storage_schema_version"
t.index "(((setweight(to_tsvector('simple'::regconfig, (display_name)::text), 'A'::\"char\") || setweight(to_tsvector('simple'::regconfig, (username)::text), 'B'::\"char\")) || setweight(to_tsvector('simple'::regconfig, (COALESCE(domain, ''::character varying))::text), 'C'::\"char\")))", name: "search_index", using: :gin t.index "(((setweight(to_tsvector('simple'::regconfig, (display_name)::text), 'A'::\"char\") || setweight(to_tsvector('simple'::regconfig, (username)::text), 'B'::\"char\")) || setweight(to_tsvector('simple'::regconfig, (COALESCE(domain, ''::character varying))::text), 'C'::\"char\")))", name: "search_index", using: :gin
t.index "lower((username)::text), lower((domain)::text)", name: "index_accounts_on_username_and_domain_lower", unique: true t.index "lower((username)::text), lower((domain)::text)", name: "index_accounts_on_username_and_domain_lower", unique: true
t.index ["moved_to_account_id"], name: "index_accounts_on_moved_to_account_id" t.index ["moved_to_account_id"], name: "index_accounts_on_moved_to_account_id"
@ -299,6 +301,7 @@ ActiveRecord::Schema.define(version: 2020_04_07_202420) do
t.string "image_remote_url" t.string "image_remote_url"
t.boolean "visible_in_picker", default: true, null: false t.boolean "visible_in_picker", default: true, null: false
t.bigint "category_id" t.bigint "category_id"
t.integer "image_storage_schema_version"
t.index ["shortcode", "domain"], name: "index_custom_emojis_on_shortcode_and_domain", unique: true t.index ["shortcode", "domain"], name: "index_custom_emojis_on_shortcode_and_domain", unique: true
end end
@ -465,6 +468,7 @@ ActiveRecord::Schema.define(version: 2020_04_07_202420) do
t.bigint "scheduled_status_id" t.bigint "scheduled_status_id"
t.string "blurhash" t.string "blurhash"
t.integer "processing" t.integer "processing"
t.integer "file_storage_schema_version"
t.index ["account_id"], name: "index_media_attachments_on_account_id" t.index ["account_id"], name: "index_media_attachments_on_account_id"
t.index ["scheduled_status_id"], name: "index_media_attachments_on_scheduled_status_id" t.index ["scheduled_status_id"], name: "index_media_attachments_on_scheduled_status_id"
t.index ["shortcode"], name: "index_media_attachments_on_shortcode", unique: true t.index ["shortcode"], name: "index_media_attachments_on_shortcode", unique: true
@ -605,6 +609,7 @@ ActiveRecord::Schema.define(version: 2020_04_07_202420) do
t.datetime "created_at", null: false t.datetime "created_at", null: false
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
t.string "embed_url", default: "", null: false t.string "embed_url", default: "", null: false
t.integer "image_storage_schema_version"
t.index ["url"], name: "index_preview_cards_on_url", unique: true t.index ["url"], name: "index_preview_cards_on_url", unique: true
end end

@ -0,0 +1,12 @@
/* global path */
/*
Preferences | Languages & Frameworks | JavaScript | Webpack | webpack configuration file
jetbrains://WebStorm/settings?name=Languages+%26+Frameworks--JavaScript--Webpack
*/
module.exports = {
resolve: {
alias: {
'mastodon': path.resolve(__dirname, 'app/javascript/mastodon'),
},
},
};

@ -11,6 +11,7 @@ require_relative 'mastodon/statuses_cli'
require_relative 'mastodon/domains_cli' require_relative 'mastodon/domains_cli'
require_relative 'mastodon/preview_cards_cli' require_relative 'mastodon/preview_cards_cli'
require_relative 'mastodon/cache_cli' require_relative 'mastodon/cache_cli'
require_relative 'mastodon/upgrade_cli'
require_relative 'mastodon/version' require_relative 'mastodon/version'
module Mastodon module Mastodon
@ -49,6 +50,9 @@ module Mastodon
desc 'cache SUBCOMMAND ...ARGS', 'Manage cache' desc 'cache SUBCOMMAND ...ARGS', 'Manage cache'
subcommand 'cache', Mastodon::CacheCLI subcommand 'cache', Mastodon::CacheCLI
desc 'upgrade SUBCOMMAND ...ARGS', 'Various version upgrade utilities'
subcommand 'upgrade', Mastodon::UpgradeCLI
option :dry_run, type: :boolean option :dry_run, type: :boolean
desc 'self-destruct', 'Erase the server from the federation' desc 'self-destruct', 'Erase the server from the federation'
long_desc <<~LONG_DESC long_desc <<~LONG_DESC

@ -10,6 +10,10 @@ Paperclip.options[:log] = false
module Mastodon module Mastodon
module CLIHelper module CLIHelper
def dry_run?
options[:dry_run]
end
def create_progress_bar(total = nil) def create_progress_bar(total = nil)
ProgressBar.create(total: total, format: '%c/%u |%b%i| %e') ProgressBar.create(total: total, format: '%c/%u |%b%i| %e')
end end

@ -23,7 +23,7 @@ module Mastodon
Existing emoji will be skipped unless the --overwrite option Existing emoji will be skipped unless the --overwrite option
is provided, in which case they will be overwritten. is provided, in which case they will be overwritten.
You can specifiy a --category under which the emojis will be You can specify a --category under which the emojis will be
grouped together. grouped together.
With the --prefix option, a prefix can be added to all With the --prefix option, a prefix can be added to all
@ -72,6 +72,48 @@ module Mastodon
say("Imported #{imported}, skipped #{skipped}, failed to import #{failed}", color(imported, skipped, failed)) say("Imported #{imported}, skipped #{skipped}, failed to import #{failed}", color(imported, skipped, failed))
end end
option :category
option :overwrite, type: :boolean
desc 'export PATH', 'Export emoji to a TAR GZIP archive at PATH'
long_desc <<-LONG_DESC
Exports custom emoji to 'export.tar.gz' at PATH.
The --category option dumps only the specified category.
If this option is not specified, all emoji will be exported.
The --overwrite option will overwrite an existing archive.
LONG_DESC
def export(path)
exported = 0
category = CustomEmojiCategory.find_by(name: options[:category])
export_file_name = File.join(path, 'export.tar.gz')
if File.file?(export_file_name) && !options[:overwrite]
say("Archive already exists! Use '--overwrite' to overwrite it!")
exit 1
end
if category.nil? && options[:category]
say("Unable to find category '#{options[:category]}'!")
exit 1
end
File.open(export_file_name, 'wb') do |file|
Zlib::GzipWriter.wrap(file) do |gzip|
Gem::Package::TarWriter.new(gzip) do |tar|
scope = !options[:category] || category.nil? ? CustomEmoji.local : category.emojis
scope.find_each do |emoji|
say("Adding '#{emoji.shortcode}'...")
tar.add_file_simple(emoji.shortcode + File.extname(emoji.image_file_name), 0o644, emoji.image_file_size) do |io|
io.write Paperclip.io_adapters.for(emoji.image).read
exported += 1
end
end
end
end
end
say("Exported #{exported}")
end
option :remote_only, type: :boolean option :remote_only, type: :boolean
desc 'purge', 'Remove all custom emoji' desc 'purge', 'Remove all custom emoji'
long_desc <<-LONG_DESC long_desc <<-LONG_DESC

@ -85,7 +85,9 @@ module Mastodon
record_map = preload_records_from_mixed_objects(objects) record_map = preload_records_from_mixed_objects(objects)
objects.each do |object| objects.each do |object|
path_segments = object.key.split('/') path_segments = object.key.split('/')
path_segments.delete('cache')
model_name = path_segments.first.classify model_name = path_segments.first.classify
attachment_name = path_segments[1].singularize attachment_name = path_segments[1].singularize
record_id = path_segments[2..-2].join.to_i record_id = path_segments[2..-2].join.to_i
@ -120,8 +122,11 @@ module Mastodon
Find.find(File.join(*[root_path, prefix].compact)) do |path| Find.find(File.join(*[root_path, prefix].compact)) do |path|
next if File.directory?(path) next if File.directory?(path)
key = path.gsub("#{root_path}#{File::SEPARATOR}", '') key = path.gsub("#{root_path}#{File::SEPARATOR}", '')
path_segments = key.split(File::SEPARATOR)
path_segments = key.split(File::SEPARATOR)
path_segments.delete('cache')
model_name = path_segments.first.classify model_name = path_segments.first.classify
record_id = path_segments[2..-2].join.to_i record_id = path_segments[2..-2].join.to_i
attachment_name = path_segments[1].singularize attachment_name = path_segments[1].singularize
@ -229,10 +234,13 @@ module Mastodon
desc 'lookup URL', 'Lookup where media is displayed by passing a media URL' desc 'lookup URL', 'Lookup where media is displayed by passing a media URL'
def lookup(url) def lookup(url)
path = Addressable::URI.parse(url).path path = Addressable::URI.parse(url).path
path_segments = path.split('/')[2..-1] path_segments = path.split('/')[2..-1]
model_name = path_segments.first.classify path_segments.delete('cache')
record_id = path_segments[2..-2].join.to_i
model_name = path_segments.first.classify
record_id = path_segments[2..-2].join.to_i
unless PRELOAD_MODEL_WHITELIST.include?(model_name) unless PRELOAD_MODEL_WHITELIST.include?(model_name)
say("Cannot find corresponding model: #{model_name}", :red) say("Cannot find corresponding model: #{model_name}", :red)
@ -276,7 +284,9 @@ module Mastodon
preload_map = Hash.new { |hash, key| hash[key] = [] } preload_map = Hash.new { |hash, key| hash[key] = [] }
objects.map do |object| objects.map do |object|
segments = object.key.split('/') segments = object.key.split('/')
segments.delete('cache')
model_name = segments.first.classify model_name = segments.first.classify
record_id = segments[2..-2].join.to_i record_id = segments[2..-2].join.to_i

@ -0,0 +1,148 @@
# frozen_string_literal: true
require_relative '../../config/boot'
require_relative '../../config/environment'
require_relative 'cli_helper'
module Mastodon
class UpgradeCLI < Thor
include CLIHelper
def self.exit_on_failure?
true
end
CURRENT_STORAGE_SCHEMA_VERSION = 1
option :dry_run, type: :boolean, default: false
option :verbose, type: :boolean, default: false, aliases: [:v]
desc 'storage-schema', 'Upgrade storage schema of various file attachments to the latest version'
long_desc <<~LONG_DESC
Iterates over every file attachment of every record and, if its storage schema is outdated, performs the
necessary upgrade to the latest one. In practice this means e.g. moving files to different directories.
Will most likely take a long time.
LONG_DESC
def storage_schema
progress = create_progress_bar(nil)
dry_run = dry_run? ? ' (DRY RUN)' : ''
records = 0
klasses = [
Account,
CustomEmoji,
MediaAttachment,
PreviewCard,
]
klasses.each do |klass|
attachment_names = klass.attachment_definitions.keys
klass.find_each do |record|
attachment_names.each do |attachment_name|
attachment = record.public_send(attachment_name)
next if attachment.blank? || attachment.storage_schema_version >= CURRENT_STORAGE_SCHEMA_VERSION
attachment.styles.each_key do |style|
case Paperclip::Attachment.default_options[:storage]
when :s3
upgrade_storage_s3(progress, attachment, style)
when :fog
upgrade_storage_fog(progress, attachment, style)
when :filesystem
upgrade_storage_filesystem(progress, attachment, style)
end
progress.increment
end
attachment.instance_write(:storage_schema_version, CURRENT_STORAGE_SCHEMA_VERSION)
end
if record.changed?
record.save unless dry_run?
records += 1
end
end
end
progress.total = progress.progress
progress.finish
say("Upgraded storage schema of #{records} records#{dry_run}", :green, true)
end
private
def upgrade_storage_s3(progress, attachment, style)
previous_storage_schema_version = attachment.storage_schema_version
object = attachment.s3_object(style)
attachment.instance_write(:storage_schema_version, CURRENT_STORAGE_SCHEMA_VERSION)
upgraded_path = attachment.path(style)
if upgraded_path != object.key && object.exists?
progress.log("Moving #{object.key} to #{upgraded_path}") if options[:verbose]
begin
object.move_to(upgraded_path) unless dry_run?
rescue => e
progress.log(pastel.red("Error processing #{object.key}: #{e}"))
end
end
# Because we move files style-by-style, it's important to restore
# previous version at the end. The upgrade will be recorded after
# all styles are updated
attachment.instance_write(:storage_schema_version, previous_storage_schema_version)
end
def upgrade_storage_fog(_progress, _attachment, _style)
say('The fog storage driver is not supported for this operation at this time', :red)
exit(1)
end
def upgrade_storage_filesystem(progress, attachment, style)
previous_storage_schema_version = attachment.storage_schema_version
previous_path = attachment.path(style)
attachment.instance_write(:storage_schema_version, CURRENT_STORAGE_SCHEMA_VERSION)
upgraded_path = attachment.path(style)
if upgraded_path != previous_path && File.exist?(previous_path)
progress.log("Moving #{previous_path} to #{upgraded_path}") if options[:verbose]
begin
unless dry_run?
FileUtils.mkdir_p(File.dirname(upgraded_path))
FileUtils.mv(previous_path, upgraded_path)
begin
FileUtils.rmdir(previous_path, parents: true)
rescue Errno::ENOTEMPTY
# OK
end
end
rescue => e
progress.log(pastel.red("Error processing #{previous_path}: #{e}"))
unless dry_run?
begin
FileUtils.rmdir(upgraded_path, parents: true)
rescue Errno::ENOTEMPTY
# OK
end
end
end
end
# Because we move files style-by-style, it's important to restore
# previous version at the end. The upgrade will be recorded after
# all styles are updated
attachment.instance_write(:storage_schema_version, previous_storage_schema_version)
end
end
end

@ -14,6 +14,15 @@ module Paperclip
end end
end end
def storage_schema_version
instance_read(:storage_schema_version) || 0
end
def assign_attributes
super
instance_write(:storage_schema_version, 1)
end
def variant?(other_filename) def variant?(other_filename)
return true if original_filename == other_filename return true if original_filename == other_filename
return false if original_filename.nil? return false if original_filename.nil?

@ -3,21 +3,133 @@
require 'rails_helper' require 'rails_helper'
RSpec.describe ActivityPub::CollectionsController, type: :controller do RSpec.describe ActivityPub::CollectionsController, type: :controller do
describe 'POST #show' do let!(:account) { Fabricate(:account) }
let(:account) { Fabricate(:account) } let(:remote_account) { nil }
context 'id is "featured"' do before do
it 'returns 200 with "application/activity+json"' do allow(controller).to receive(:signed_request_account).and_return(remote_account)
post :show, params: { id: 'featured', account_username: account.username }
expect(response).to have_http_status(200) Fabricate(:status_pin, account: account)
expect(response.content_type).to eq 'application/activity+json' Fabricate(:status_pin, account: account)
Fabricate(:status, account: account, visibility: :private)
end
describe 'GET #show' do
context 'when id is "featured"' do
context 'without signature' do
let(:remote_account) { nil }
before do
get :show, params: { id: 'featured', account_username: account.username }
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns application/activity+json' do
expect(response.content_type).to eq 'application/activity+json'
end
it 'returns public Cache-Control header' do
expect(response.headers['Cache-Control']).to include 'public'
end
it 'returns orderedItems with pinned statuses' do
json = body_as_json
expect(json[:orderedItems]).to be_an Array
expect(json[:orderedItems].size).to eq 2
end
end
context 'with signature' do
let(:remote_account) { Fabricate(:account, domain: 'example.com') }
context do
before do
get :show, params: { id: 'featured', account_username: account.username }
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns application/activity+json' do
expect(response.content_type).to eq 'application/activity+json'
end
it 'returns public Cache-Control header' do
expect(response.headers['Cache-Control']).to include 'public'
end
it 'returns orderedItems with pinned statuses' do
json = body_as_json
expect(json[:orderedItems]).to be_an Array
expect(json[:orderedItems].size).to eq 2
end
end
context 'in authorized fetch mode' do
before do
allow(controller).to receive(:authorized_fetch_mode?).and_return(true)
end
context 'when signed request account is blocked' do
before do
account.block!(remote_account)
get :show, params: { id: 'featured', account_username: account.username }
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns application/activity+json' do
expect(response.content_type).to eq 'application/activity+json'
end
it 'returns private Cache-Control header' do
expect(response.headers['Cache-Control']).to include 'private'
end
it 'returns empty orderedItems' do
json = body_as_json
expect(json[:orderedItems]).to be_an Array
expect(json[:orderedItems].size).to eq 0
end
end
context 'when signed request account is domain blocked' do
before do
account.block_domain!(remote_account.domain)
get :show, params: { id: 'featured', account_username: account.username }
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns application/activity+json' do
expect(response.content_type).to eq 'application/activity+json'
end
it 'returns private Cache-Control header' do
expect(response.headers['Cache-Control']).to include 'private'
end
it 'returns empty orderedItems' do
json = body_as_json
expect(json[:orderedItems]).to be_an Array
expect(json[:orderedItems].size).to eq 0
end
end
end
end end
end end
context 'id is not "featured"' do context 'when id is not "featured"' do
it 'returns 404' do it 'returns http not found' do
post :show, params: { id: 'hoge', account_username: account.username } get :show, params: { id: 'hoge', account_username: account.username }
expect(response).to have_http_status(404) expect(response).to have_http_status(404)
end end
end end

@ -3,25 +3,31 @@
require 'rails_helper' require 'rails_helper'
RSpec.describe ActivityPub::InboxesController, type: :controller do RSpec.describe ActivityPub::InboxesController, type: :controller do
let(:remote_account) { nil }
before do
allow(controller).to receive(:signed_request_account).and_return(remote_account)
end
describe 'POST #create' do describe 'POST #create' do
context 'with signed_request_account' do context 'with signature' do
it 'returns 202' do let(:remote_account) { Fabricate(:account, domain: 'example.com', protocol: :activitypub) }
allow(controller).to receive(:signed_request_account) do
Fabricate(:account)
end
before do
post :create, body: '{}' post :create, body: '{}'
end
it 'returns http accepted' do
expect(response).to have_http_status(202) expect(response).to have_http_status(202)
end end
end end
context 'without signed_request_account' do context 'without signature' do
it 'returns 401' do before do
allow(controller).to receive(:signed_request_account) do
false
end
post :create, body: '{}' post :create, body: '{}'
end
it 'returns http not authorized' do
expect(response).to have_http_status(401) expect(response).to have_http_status(401)
end end
end end

@ -4,20 +4,174 @@ RSpec.describe ActivityPub::OutboxesController, type: :controller do
let!(:account) { Fabricate(:account) } let!(:account) { Fabricate(:account) }
before do before do
Fabricate(:status, account: account) Fabricate(:status, account: account, visibility: :public)
Fabricate(:status, account: account, visibility: :unlisted)
Fabricate(:status, account: account, visibility: :private)
Fabricate(:status, account: account, visibility: :direct)
Fabricate(:status, account: account, visibility: :limited)
end
before do
allow(controller).to receive(:signed_request_account).and_return(remote_account)
end end
describe 'GET #show' do describe 'GET #show' do
before do context 'without signature' do
get :show, params: { account_username: account.username } let(:remote_account) { nil }
end
before do
get :show, params: { account_username: account.username, page: page }
end
context 'with page not requested' do
let(:page) { nil }
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns application/activity+json' do
expect(response.content_type).to eq 'application/activity+json'
end
it 'returns totalItems' do
json = body_as_json
expect(json[:totalItems]).to eq 4
end
it 'returns http success' do it 'returns public Cache-Control header' do
expect(response).to have_http_status(200) expect(response.headers['Cache-Control']).to include 'public'
end
end
context 'with page requested' do
let(:page) { 'true' }
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns application/activity+json' do
expect(response.content_type).to eq 'application/activity+json'
end
it 'returns orderedItems with public or unlisted statuses' do
json = body_as_json
expect(json[:orderedItems]).to be_an Array
expect(json[:orderedItems].size).to eq 2
expect(json[:orderedItems].all? { |item| item[:to].include?(ActivityPub::TagManager::COLLECTIONS[:public]) || item[:cc].include?(ActivityPub::TagManager::COLLECTIONS[:public]) }).to be true
end
it 'returns public Cache-Control header' do
expect(response.headers['Cache-Control']).to include 'public'
end
end
end end
it 'returns application/activity+json' do context 'with signature' do
expect(response.content_type).to eq 'application/activity+json' let(:remote_account) { Fabricate(:account, domain: 'example.com') }
let(:page) { 'true' }
context 'when signed request account does not follow account' do
before do
get :show, params: { account_username: account.username, page: page }
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns application/activity+json' do
expect(response.content_type).to eq 'application/activity+json'
end
it 'returns orderedItems with public or unlisted statuses' do
json = body_as_json
expect(json[:orderedItems]).to be_an Array
expect(json[:orderedItems].size).to eq 2
expect(json[:orderedItems].all? { |item| item[:to].include?(ActivityPub::TagManager::COLLECTIONS[:public]) || item[:cc].include?(ActivityPub::TagManager::COLLECTIONS[:public]) }).to be true
end
it 'returns private Cache-Control header' do
expect(response.headers['Cache-Control']).to eq 'max-age=0, private'
end
end
context 'when signed request account follows account' do
before do
remote_account.follow!(account)
get :show, params: { account_username: account.username, page: page }
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns application/activity+json' do
expect(response.content_type).to eq 'application/activity+json'
end
it 'returns orderedItems with private statuses' do
json = body_as_json
expect(json[:orderedItems]).to be_an Array
expect(json[:orderedItems].size).to eq 3
expect(json[:orderedItems].all? { |item| item[:to].include?(ActivityPub::TagManager::COLLECTIONS[:public]) || item[:cc].include?(ActivityPub::TagManager::COLLECTIONS[:public]) || item[:to].include?(account_followers_url(account, ActionMailer::Base.default_url_options)) }).to be true
end
it 'returns private Cache-Control header' do
expect(response.headers['Cache-Control']).to eq 'max-age=0, private'
end
end
context 'when signed request account is blocked' do
before do
account.block!(remote_account)
get :show, params: { account_username: account.username, page: page }
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns application/activity+json' do
expect(response.content_type).to eq 'application/activity+json'
end
it 'returns empty orderedItems' do
json = body_as_json
expect(json[:orderedItems]).to be_an Array
expect(json[:orderedItems].size).to eq 0
end
it 'returns private Cache-Control header' do
expect(response.headers['Cache-Control']).to eq 'max-age=0, private'
end
end
context 'when signed request account is domain blocked' do
before do
account.block_domain!(remote_account.domain)
get :show, params: { account_username: account.username, page: page }
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns application/activity+json' do
expect(response.content_type).to eq 'application/activity+json'
end
it 'returns empty orderedItems' do
json = body_as_json
expect(json[:orderedItems]).to be_an Array
expect(json[:orderedItems].size).to eq 0
end
it 'returns private Cache-Control header' do
expect(response.headers['Cache-Control']).to eq 'max-age=0, private'
end
end
end end
end end
end end

@ -0,0 +1,196 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe ActivityPub::RepliesController, type: :controller do
let(:status) { Fabricate(:status, visibility: parent_visibility) }
let(:remote_account) { nil }
before do
allow(controller).to receive(:signed_request_account).and_return(remote_account)
Fabricate(:status, thread: status, visibility: :public)
Fabricate(:status, thread: status, visibility: :public)
Fabricate(:status, thread: status, visibility: :private)
Fabricate(:status, account: status.account, thread: status, visibility: :public)
Fabricate(:status, account: status.account, thread: status, visibility: :private)
end
describe 'GET #index' do
context 'with no signature' do
before do
get :index, params: { account_username: status.account.username, status_id: status.id }
end
context 'when status is public' do
let(:parent_visibility) { :public }
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns application/activity+json' do
expect(response.content_type).to eq 'application/activity+json'
end
it 'returns public Cache-Control header' do
expect(response.headers['Cache-Control']).to include 'public'
end
it 'returns items with account\'s own replies' do
json = body_as_json
expect(json[:first]).to be_a Hash
expect(json[:first][:items]).to be_an Array
expect(json[:first][:items].size).to eq 1
expect(json[:first][:items].all? { |item| item[:to].include?(ActivityPub::TagManager::COLLECTIONS[:public]) || item[:cc].include?(ActivityPub::TagManager::COLLECTIONS[:public]) }).to be true
end
end
context 'when status is private' do
let(:parent_visibility) { :private }
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
context 'when status is direct' do
let(:parent_visibility) { :direct }
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
end
context 'with signature' do
let(:remote_account) { Fabricate(:account, domain: 'example.com') }
let(:only_other_accounts) { nil }
context do
before do
get :index, params: { account_username: status.account.username, status_id: status.id, only_other_accounts: only_other_accounts }
end
context 'when status is public' do
let(:parent_visibility) { :public }
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns application/activity+json' do
expect(response.content_type).to eq 'application/activity+json'
end
it 'returns public Cache-Control header' do
expect(response.headers['Cache-Control']).to include 'public'
end
context 'without only_other_accounts' do
it 'returns items with account\'s own replies' do
json = body_as_json
expect(json[:first]).to be_a Hash
expect(json[:first][:items]).to be_an Array
expect(json[:first][:items].size).to eq 1
expect(json[:first][:items].all? { |item| item[:to].include?(ActivityPub::TagManager::COLLECTIONS[:public]) || item[:cc].include?(ActivityPub::TagManager::COLLECTIONS[:public]) }).to be true
end
end
context 'with only_other_accounts' do
let(:only_other_accounts) { 'true' }
it 'returns items with other public or unlisted replies' do
json = body_as_json
expect(json[:first]).to be_a Hash
expect(json[:first][:items]).to be_an Array
expect(json[:first][:items].size).to eq 2
expect(json[:first][:items].all? { |item| item[:to].include?(ActivityPub::TagManager::COLLECTIONS[:public]) || item[:cc].include?(ActivityPub::TagManager::COLLECTIONS[:public]) }).to be true
end
end
end
context 'when status is private' do
let(:parent_visibility) { :private }
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
context 'when status is direct' do
let(:parent_visibility) { :direct }
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
end
context 'when signed request account is blocked' do
before do
status.account.block!(remote_account)
get :index, params: { account_username: status.account.username, status_id: status.id }
end
context 'when status is public' do
let(:parent_visibility) { :public }
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
context 'when status is private' do
let(:parent_visibility) { :private }
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
context 'when status is direct' do
let(:parent_visibility) { :direct }
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
end
context 'when signed request account is domain blocked' do
before do
status.account.block_domain!(remote_account.domain)
get :index, params: { account_username: status.account.username, status_id: status.id }
end
context 'when status is public' do
let(:parent_visibility) { :public }
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
context 'when status is private' do
let(:parent_visibility) { :private }
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
context 'when status is direct' do
let(:parent_visibility) { :direct }
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
end
end
end
end

@ -5,128 +5,821 @@ require 'rails_helper'
describe StatusesController do describe StatusesController do
render_views render_views
describe '#show' do describe 'GET #show' do
context 'account is suspended' do let(:account) { Fabricate(:account) }
it 'returns gone' do let(:status) { Fabricate(:status, account: account) }
account = Fabricate(:account, suspended: true)
status = Fabricate(:status, account: account)
context 'when account is suspended' do
let(:account) { Fabricate(:account, suspended: true) }
before do
get :show, params: { account_username: account.username, id: status.id } get :show, params: { account_username: account.username, id: status.id }
end
it 'returns http gone' do
expect(response).to have_http_status(410) expect(response).to have_http_status(410)
end end
end end
context 'status is not permitted' do context 'when status is a reblog' do
it 'raises ActiveRecord::RecordNotFound' do let(:original_account) { Fabricate(:account, domain: 'example.com') }
user = Fabricate(:user) let(:original_status) { Fabricate(:status, account: original_account, url: 'https://example.com/123') }
status = Fabricate(:status) let(:status) { Fabricate(:status, account: account, reblog: original_status) }
status.account.block!(user.account)
sign_in(user) before do
get :show, params: { account_username: status.account.username, id: status.id } get :show, params: { account_username: status.account.username, id: status.id }
end
expect(response).to have_http_status(404) it 'redirects to the original status' do
expect(response).to redirect_to(original_status.url)
end end
end end
context 'status is a reblog' do context 'when status is public' do
it 'redirects to the original status' do before do
original_account = Fabricate(:account, domain: 'example.com') get :show, params: { account_username: status.account.username, id: status.id, format: format }
original_status = Fabricate(:status, account: original_account, uri: 'tag:example.com,2017:foo', url: 'https://example.com/123') end
status = Fabricate(:status, reblog: original_status)
get :show, params: { account_username: status.account.username, id: status.id } context 'as HTML' do
let(:format) { 'html' }
expect(response).to redirect_to(original_status.url) it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns Link header' do
expect(response.headers['Link'].to_s).to include 'activity+json'
end
it 'returns Vary header' do
expect(response.headers['Vary']).to eq 'Accept'
end
it 'returns public Cache-Control header' do
expect(response.headers['Cache-Control']).to include 'public'
end
it 'renders status' do
expect(response).to render_template(:show)
expect(response.body).to include status.text
end
end
context 'as JSON' do
let(:format) { 'json' }
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns Link header' do
expect(response.headers['Link'].to_s).to include 'activity+json'
end
it 'returns Vary header' do
expect(response.headers['Vary']).to eq 'Accept'
end
it 'returns public Cache-Control header' do
expect(response.headers['Cache-Control']).to include 'public'
end
it 'returns Content-Type header' do
expect(response.headers['Content-Type']).to include 'application/activity+json'
end
it 'renders ActivityPub Note object' do
json = body_as_json
expect(json[:content]).to include status.text
end
end end
end end
context 'account is not suspended and status is permitted' do context 'when status is private' do
it 'assigns @account' do let(:status) { Fabricate(:status, account: account, visibility: :private) }
status = Fabricate(:status)
get :show, params: { account_username: status.account.username, id: status.id } before do
expect(assigns(:account)).to eq status.account get :show, params: { account_username: status.account.username, id: status.id, format: format }
end end
it 'assigns @status' do context 'as JSON' do
status = Fabricate(:status) let(:format) { 'json' }
get :show, params: { account_username: status.account.username, id: status.id }
expect(assigns(:status)).to eq status it 'returns http not found' do
expect(response).to have_http_status(404)
end
end end
it 'assigns @ancestors for ancestors of the status if it is a reply' do context 'as HTML' do
ancestor = Fabricate(:status) let(:format) { 'html' }
status = Fabricate(:status, in_reply_to_id: ancestor.id)
get :show, params: { account_username: status.account.username, id: status.id } it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
end
context 'when status is direct' do
let(:status) { Fabricate(:status, account: account, visibility: :direct) }
expect(assigns(:ancestors)).to eq [ancestor] before do
get :show, params: { account_username: status.account.username, id: status.id, format: format }
end end
it 'assigns @ancestors for [] if it is not a reply' do context 'as JSON' do
status = Fabricate(:status) let(:format) { 'json' }
get :show, params: { account_username: status.account.username, id: status.id }
expect(assigns(:ancestors)).to eq [] it 'returns http not found' do
expect(response).to have_http_status(404)
end
end end
it 'assigns @descendant_threads for a thread with several statuses' do context 'as HTML' do
status = Fabricate(:status) let(:format) { 'html' }
child = Fabricate(:status, in_reply_to_id: status.id)
grandchild = Fabricate(:status, in_reply_to_id: child.id)
get :show, params: { account_username: status.account.username, id: status.id } it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
end
context 'when signed-in' do
let(:user) { Fabricate(:user) }
expect(assigns(:descendant_threads)[0][:statuses].pluck(:id)).to eq [child.id, grandchild.id] before do
sign_in(user)
end end
it 'assigns @descendant_threads for several threads sharing the same descendant' do context 'when account blocks user' do
status = Fabricate(:status) before do
child = Fabricate(:status, in_reply_to_id: status.id) account.block!(user.account)
grandchildren = 2.times.map { Fabricate(:status, in_reply_to_id: child.id) } get :show, params: { account_username: status.account.username, id: status.id }
end
get :show, params: { account_username: status.account.username, id: status.id } it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
context 'when status is public' do
before do
get :show, params: { account_username: status.account.username, id: status.id, format: format }
end
context 'as HTML' do
let(:format) { 'html' }
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns Link header' do
expect(response.headers['Link'].to_s).to include 'activity+json'
end
expect(assigns(:descendant_threads)[0][:statuses].pluck(:id)).to eq [child.id, grandchildren[0].id] it 'returns Vary header' do
expect(assigns(:descendant_threads)[1][:statuses].pluck(:id)).to eq [grandchildren[1].id] expect(response.headers['Vary']).to eq 'Accept'
end
it 'returns no Cache-Control header' do
expect(response.headers).to_not include 'Cache-Control'
end
it 'renders status' do
expect(response).to render_template(:show)
expect(response.body).to include status.text
end
end
context 'as JSON' do
let(:format) { 'json' }
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns Link header' do
expect(response.headers['Link'].to_s).to include 'activity+json'
end
it 'returns Vary header' do
expect(response.headers['Vary']).to eq 'Accept'
end
it 'returns public Cache-Control header' do
expect(response.headers['Cache-Control']).to include 'public'
end
it 'returns Content-Type header' do
expect(response.headers['Content-Type']).to include 'application/activity+json'
end
it 'renders ActivityPub Note object' do
json = body_as_json
expect(json[:content]).to include status.text
end
end
end end
it 'assigns @max_descendant_thread_id for the last thread if it is hitting the status limit' do context 'when status is private' do
stub_const 'StatusControllerConcern::DESCENDANTS_LIMIT', 1 let(:status) { Fabricate(:status, account: account, visibility: :private) }
status = Fabricate(:status)
child = Fabricate(:status, in_reply_to_id: status.id)
get :show, params: { account_username: status.account.username, id: status.id } context 'when user is authorized to see it' do
before do
user.account.follow!(account)
get :show, params: { account_username: status.account.username, id: status.id, format: format }
end
context 'as HTML' do
let(:format) { 'html' }
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns Link header' do
expect(response.headers['Link'].to_s).to include 'activity+json'
end
it 'returns Vary header' do
expect(response.headers['Vary']).to eq 'Accept'
end
expect(assigns(:descendant_threads)).to eq [] it 'returns no Cache-Control header' do
expect(assigns(:max_descendant_thread_id)).to eq child.id expect(response.headers).to_not include 'Cache-Control'
end
it 'renders status' do
expect(response).to render_template(:show)
expect(response.body).to include status.text
end
end
context 'as JSON' do
let(:format) { 'json' }
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns Link header' do
expect(response.headers['Link'].to_s).to include 'activity+json'
end
it 'returns Vary header' do
expect(response.headers['Vary']).to eq 'Accept'
end
it 'returns private Cache-Control header' do
expect(response.headers['Cache-Control']).to include 'private'
end
it 'returns Content-Type header' do
expect(response.headers['Content-Type']).to include 'application/activity+json'
end
it 'renders ActivityPub Note object' do
json = body_as_json
expect(json[:content]).to include status.text
end
end
end
context 'when user is not authorized to see it' do
before do
get :show, params: { account_username: status.account.username, id: status.id, format: format }
end
context 'as JSON' do
let(:format) { 'json' }
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
context 'as HTML' do
let(:format) { 'html' }
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
end
end end
it 'assigns @descendant_threads for threads with :next_status key if they are hitting the depth limit' do context 'when status is direct' do
stub_const 'StatusControllerConcern::DESCENDANTS_DEPTH_LIMIT', 2 let(:status) { Fabricate(:status, account: account, visibility: :direct) }
status = Fabricate(:status)
child0 = Fabricate(:status, in_reply_to_id: status.id)
child1 = Fabricate(:status, in_reply_to_id: child0.id)
child2 = Fabricate(:status, in_reply_to_id: child0.id)
get :show, params: { account_username: status.account.username, id: status.id } context 'when user is authorized to see it' do
before do
Fabricate(:mention, account: user.account, status: status)
get :show, params: { account_username: status.account.username, id: status.id, format: format }
end
context 'as HTML' do
let(:format) { 'html' }
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns Link header' do
expect(response.headers['Link'].to_s).to include 'activity+json'
end
it 'returns Vary header' do
expect(response.headers['Vary']).to eq 'Accept'
end
it 'returns no Cache-Control header' do
expect(response.headers).to_not include 'Cache-Control'
end
it 'renders status' do
expect(response).to render_template(:show)
expect(response.body).to include status.text
end
end
context 'as JSON' do
let(:format) { 'json' }
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns Link header' do
expect(response.headers['Link'].to_s).to include 'activity+json'
end
it 'returns Vary header' do
expect(response.headers['Vary']).to eq 'Accept'
end
it 'returns private Cache-Control header' do
expect(response.headers['Cache-Control']).to include 'private'
end
it 'returns Content-Type header' do
expect(response.headers['Content-Type']).to include 'application/activity+json'
end
it 'renders ActivityPub Note object' do
json = body_as_json
expect(json[:content]).to include status.text
end
end
end
context 'when user is not authorized to see it' do
before do
get :show, params: { account_username: status.account.username, id: status.id, format: format }
end
context 'as JSON' do
let(:format) { 'json' }
expect(assigns(:descendant_threads)[0][:statuses].pluck(:id)).not_to include child1.id it 'returns http not found' do
expect(assigns(:descendant_threads)[1][:statuses].pluck(:id)).not_to include child2.id expect(response).to have_http_status(404)
expect(assigns(:descendant_threads)[0][:next_status].id).to eq child1.id end
expect(assigns(:descendant_threads)[1][:next_status].id).to eq child2.id end
context 'as HTML' do
let(:format) { 'html' }
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
end
end end
end
it 'returns a success' do context 'with signature' do
status = Fabricate(:status) let(:remote_account) { Fabricate(:account, domain: 'example.com') }
get :show, params: { account_username: status.account.username, id: status.id }
before do
allow(controller).to receive(:signed_request_account).and_return(remote_account)
end
context 'when account blocks account' do
before do
account.block!(remote_account)
get :show, params: { account_username: status.account.username, id: status.id }
end
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
context 'when account domain blocks account' do
before do
account.block_domain!(remote_account.domain)
get :show, params: { account_username: status.account.username, id: status.id }
end
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
context 'when status is public' do
before do
get :show, params: { account_username: status.account.username, id: status.id, format: format }
end
context 'as HTML' do
let(:format) { 'html' }
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns Link header' do
expect(response.headers['Link'].to_s).to include 'activity+json'
end
it 'returns Vary header' do
expect(response.headers['Vary']).to eq 'Accept'
end
it 'returns no Cache-Control header' do
expect(response.headers).to_not include 'Cache-Control'
end
it 'renders status' do
expect(response).to render_template(:show)
expect(response.body).to include status.text
end
end
context 'as JSON' do
let(:format) { 'json' }
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns Link header' do
expect(response.headers['Link'].to_s).to include 'activity+json'
end
it 'returns Vary header' do
expect(response.headers['Vary']).to eq 'Accept'
end
it 'returns public Cache-Control header' do
expect(response.headers['Cache-Control']).to include 'public'
end
it 'returns Content-Type header' do
expect(response.headers['Content-Type']).to include 'application/activity+json'
end
it 'renders ActivityPub Note object' do
json = body_as_json
expect(json[:content]).to include status.text
end
end
end
context 'when status is private' do
let(:status) { Fabricate(:status, account: account, visibility: :private) }
context 'when user is authorized to see it' do
before do
remote_account.follow!(account)
get :show, params: { account_username: status.account.username, id: status.id, format: format }
end
context 'as HTML' do
let(:format) { 'html' }
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns Link header' do
expect(response.headers['Link'].to_s).to include 'activity+json'
end
it 'returns Vary header' do
expect(response.headers['Vary']).to eq 'Accept'
end
it 'returns no Cache-Control header' do
expect(response.headers).to_not include 'Cache-Control'
end
it 'renders status' do
expect(response).to render_template(:show)
expect(response.body).to include status.text
end
end
context 'as JSON' do
let(:format) { 'json' }
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns Link header' do
expect(response.headers['Link'].to_s).to include 'activity+json'
end
it 'returns Vary header' do
expect(response.headers['Vary']).to eq 'Accept'
end
it 'returns private Cache-Control header' do
expect(response.headers['Cache-Control']).to include 'private'
end
it 'returns Content-Type header' do
expect(response.headers['Content-Type']).to include 'application/activity+json'
end
it 'renders ActivityPub Note object' do
json = body_as_json
expect(json[:content]).to include status.text
end
end
end
context 'when user is not authorized to see it' do
before do
get :show, params: { account_username: status.account.username, id: status.id, format: format }
end
context 'as JSON' do
let(:format) { 'json' }
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
context 'as HTML' do
let(:format) { 'html' }
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
end
end
context 'when status is direct' do
let(:status) { Fabricate(:status, account: account, visibility: :direct) }
context 'when user is authorized to see it' do
before do
Fabricate(:mention, account: remote_account, status: status)
get :show, params: { account_username: status.account.username, id: status.id, format: format }
end
context 'as HTML' do
let(:format) { 'html' }
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns Link header' do
expect(response.headers['Link'].to_s).to include 'activity+json'
end
it 'returns Vary header' do
expect(response.headers['Vary']).to eq 'Accept'
end
it 'returns no Cache-Control header' do
expect(response.headers).to_not include 'Cache-Control'
end
it 'renders status' do
expect(response).to render_template(:show)
expect(response.body).to include status.text
end
end
context 'as JSON' do
let(:format) { 'json' }
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns Link header' do
expect(response.headers['Link'].to_s).to include 'activity+json'
end
it 'returns Vary header' do
expect(response.headers['Vary']).to eq 'Accept'
end
it 'returns private Cache-Control header' do
expect(response.headers['Cache-Control']).to include 'private'
end
it 'returns Content-Type header' do
expect(response.headers['Content-Type']).to include 'application/activity+json'
end
it 'renders ActivityPub Note object' do
json = body_as_json
expect(json[:content]).to include status.text
end
end
end
context 'when user is not authorized to see it' do
before do
get :show, params: { account_username: status.account.username, id: status.id, format: format }
end
context 'as JSON' do
let(:format) { 'json' }
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
context 'as HTML' do
let(:format) { 'html' }
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
end
end
end
end
describe 'GET #activity' do
let(:account) { Fabricate(:account) }
let(:status) { Fabricate(:status, account: account) }
context 'when account is suspended' do
let(:account) { Fabricate(:account, suspended: true) }
before do
get :activity, params: { account_username: account.username, id: status.id }
end
it 'returns http gone' do
expect(response).to have_http_status(410)
end
end
context 'when status is public' do
pending
end
context 'when status is private' do
pending
end
context 'when status is direct' do
pending
end
context 'when signed-in' do
context 'when status is public' do
pending
end
context 'when status is private' do
context 'when user is authorized to see it' do
pending
end
context 'when user is not authorized to see it' do
pending
end
end
context 'when status is direct' do
context 'when user is authorized to see it' do
pending
end
context 'when user is not authorized to see it' do
pending
end
end
end
context 'with signature' do
context 'when status is public' do
pending
end
context 'when status is private' do
context 'when user is authorized to see it' do
pending
end
context 'when user is not authorized to see it' do
pending
end
end
context 'when status is direct' do
context 'when user is authorized to see it' do
pending
end
context 'when user is not authorized to see it' do
pending
end
end
end
end
describe 'GET #embed' do
let(:account) { Fabricate(:account) }
let(:status) { Fabricate(:status, account: account) }
context 'when account is suspended' do
let(:account) { Fabricate(:account, suspended: true) }
before do
get :embed, params: { account_username: account.username, id: status.id }
end
it 'returns http gone' do
expect(response).to have_http_status(410)
end
end
context 'when status is a reblog' do
let(:original_account) { Fabricate(:account, domain: 'example.com') }
let(:original_status) { Fabricate(:status, account: original_account, url: 'https://example.com/123') }
let(:status) { Fabricate(:status, account: account, reblog: original_status) }
before do
get :embed, params: { account_username: status.account.username, id: status.id }
end
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
context 'when status is public' do
before do
get :embed, params: { account_username: status.account.username, id: status.id }
end
it 'returns http success' do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end end
it 'renders statuses/show' do it 'returns Link header' do
status = Fabricate(:status) expect(response.headers['Link'].to_s).to include 'activity+json'
get :show, params: { account_username: status.account.username, id: status.id } end
expect(response).to render_template 'statuses/show'
it 'returns Vary header' do
expect(response.headers['Vary']).to eq 'Accept'
end
it 'returns public Cache-Control header' do
expect(response.headers['Cache-Control']).to include 'public'
end
it 'renders status' do
expect(response).to render_template(:embed)
expect(response.body).to include status.text
end
end
context 'when status is private' do
let(:status) { Fabricate(:status, account: account, visibility: :private) }
before do
get :embed, params: { account_username: status.account.username, id: status.id }
end
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
context 'when status is direct' do
let(:status) { Fabricate(:status, account: account, visibility: :direct) }
before do
get :embed, params: { account_username: status.account.username, id: status.id }
end
it 'returns http not found' do
expect(response).to have_http_status(404)
end end
end end
end end

@ -1,4 +1,4 @@
Fabricator(:status_pin) do Fabricator(:status_pin) do
account account
status status { |attrs| Fabricate(:status, account: attrs[:account], visibility: :public) }
end end

@ -96,20 +96,16 @@ RSpec.describe Status, type: :model do
context 'unless destroyed?' do context 'unless destroyed?' do
context 'if reblog?' do context 'if reblog?' do
it 'returns "#{account.acct} shared #{reblog.account.acct}\'s: #{preview}"' do it 'returns "#{account.acct} shared a status by #{reblog.account.acct}"' do
reblog = subject.reblog = other reblog = subject.reblog = other
preview = subject.text.slice(0, 10).split("\n")[0] expect(subject.title).to eq "#{account.acct} shared a status by #{reblog.account.acct}"
expect(subject.title).to(
eq "#{account.acct} shared #{reblog.account.acct}'s: #{preview}"
)
end end
end end
context 'unless reblog?' do context 'unless reblog?' do
it 'returns "#{account.acct}: #{preview}"' do it 'returns "New status by #{account.acct}"' do
subject.reblog = nil subject.reblog = nil
preview = subject.text.slice(0, 20).split("\n")[0] expect(subject.title).to eq "New status by #{account.acct}"
expect(subject.title).to eq "#{account.acct}: #{preview}"
end end
end end
end end

@ -21,7 +21,11 @@ RSpec.describe FetchResourceService, type: :service do
context 'when OpenSSL::SSL::SSLError is raised' do context 'when OpenSSL::SSL::SSLError is raised' do
before do before do
allow(Request).to receive_message_chain(:new, :add_headers, :on_behalf_of, :perform).and_raise(OpenSSL::SSL::SSLError) request = double()
allow(Request).to receive(:new).and_return(request)
allow(request).to receive(:add_headers)
allow(request).to receive(:on_behalf_of)
allow(request).to receive(:perform).and_raise(OpenSSL::SSL::SSLError)
end end
it { is_expected.to be_nil } it { is_expected.to be_nil }
@ -29,7 +33,11 @@ RSpec.describe FetchResourceService, type: :service do
context 'when HTTP::ConnectionError is raised' do context 'when HTTP::ConnectionError is raised' do
before do before do
allow(Request).to receive_message_chain(:new, :add_headers, :on_behalf_of, :perform).and_raise(HTTP::ConnectionError) request = double()
allow(Request).to receive(:new).and_return(request)
allow(request).to receive(:add_headers)
allow(request).to receive(:on_behalf_of)
allow(request).to receive(:perform).and_raise(HTTP::ConnectionError)
end end
it { is_expected.to be_nil } it { is_expected.to be_nil }

@ -1327,9 +1327,9 @@
integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
"@types/q@^1.5.1": "@types/q@^1.5.1":
version "1.5.1" version "1.5.2"
resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.1.tgz#48fd98c1561fe718b61733daed46ff115b496e18" resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8"
integrity sha512-eqz8c/0kwNi/OEHQfvIuJVLTst3in0e7uTKeuY+WL/zfKn0xVujOTp42bS/vUUokhK5P2BppLd9JXMOMHcgbjA== integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==
"@types/react@16.4.6": "@types/react@16.4.6":
version "16.4.6" version "16.4.6"
@ -1632,7 +1632,7 @@ ajv@^4.7.0:
co "^4.6.0" co "^4.6.0"
json-stable-stringify "^1.0.1" json-stable-stringify "^1.0.1"
ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.5.5, ajv@^6.9.1: ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.9.1:
version "6.10.2" version "6.10.2"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52"
integrity sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw== integrity sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==
@ -1652,6 +1652,16 @@ ajv@^6.12.0:
json-schema-traverse "^0.4.1" json-schema-traverse "^0.4.1"
uri-js "^4.2.2" uri-js "^4.2.2"
ajv@^6.5.5:
version "6.12.2"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.2.tgz#c629c5eced17baf314437918d2da88c99d5958cd"
integrity sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==
dependencies:
fast-deep-equal "^3.1.1"
fast-json-stable-stringify "^2.0.0"
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"
alphanum-sort@^1.0.0: alphanum-sort@^1.0.0:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3"
@ -1971,9 +1981,9 @@ aws-sign2@~0.7.0:
integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=
aws4@^1.8.0: aws4@^1.8.0:
version "1.8.0" version "1.9.1"
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e"
integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ== integrity sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==
axios@^0.18.0: axios@^0.18.0:
version "0.18.1" version "0.18.1"
@ -2249,9 +2259,9 @@ bindings@^1.5.0:
file-uri-to-path "1.0.0" file-uri-to-path "1.0.0"
bluebird@^3.5.5: bluebird@^3.5.5:
version "3.5.5" version "3.7.2"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.5.tgz#a8d0afd73251effbbd5fe384a77d73003c17a71f" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
integrity sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w== integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
blurhash@^1.1.3: blurhash@^1.1.3:
version "1.1.3" version "1.1.3"
@ -2461,11 +2471,6 @@ buffer@^4.3.0:
ieee754 "^1.1.4" ieee754 "^1.1.4"
isarray "^1.0.0" isarray "^1.0.0"
builtin-modules@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f"
integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=
builtin-status-codes@^3.0.0: builtin-status-codes@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
@ -2482,9 +2487,9 @@ bytes@3.1.0:
integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==
cacache@^12.0.2, cacache@^12.0.3: cacache@^12.0.2, cacache@^12.0.3:
version "12.0.3" version "12.0.4"
resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.3.tgz#be99abba4e1bf5df461cd5a2c1071fc432573390" resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.4.tgz#668bcbd105aeb5f1d92fe25570ec9525c8faa40c"
integrity sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw== integrity sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==
dependencies: dependencies:
bluebird "^3.5.5" bluebird "^3.5.5"
chownr "^1.1.1" chownr "^1.1.1"
@ -2703,16 +2708,11 @@ chokidar@^2.1.8:
optionalDependencies: optionalDependencies:
fsevents "^1.2.7" fsevents "^1.2.7"
chownr@^1.1.1: chownr@^1.1.1, chownr@^1.1.2:
version "1.1.4" version "1.1.4"
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b"
integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==
chownr@^1.1.2:
version "1.1.3"
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.3.tgz#42d837d5239688d55f303003a508230fa6727142"
integrity sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==
chrome-trace-event@^1.0.2: chrome-trace-event@^1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz#234090ee97c7d4ad1a2c4beae27505deffc608a4" resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz#234090ee97c7d4ad1a2c4beae27505deffc608a4"
@ -2818,7 +2818,7 @@ co@^4.6.0:
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=
coa@~2.0.1: coa@^2.0.2:
version "2.0.2" version "2.0.2"
resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.2.tgz#43f6c21151b4ef2bf57187db0d73de229e3e7ec3" resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.2.tgz#43f6c21151b4ef2bf57187db0d73de229e3e7ec3"
integrity sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA== integrity sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==
@ -2880,15 +2880,10 @@ color@^3.0.0:
color-convert "^1.9.1" color-convert "^1.9.1"
color-string "^1.5.2" color-string "^1.5.2"
colors@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63"
integrity sha1-FopHAXVran9RoSzgyXv6KMCE7WM=
combined-stream@^1.0.6, combined-stream@~1.0.6: combined-stream@^1.0.6, combined-stream@~1.0.6:
version "1.0.7" version "1.0.8"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.7.tgz#2d1d24317afb8abe95d6d2c0b07b57813539d828" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
integrity sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w== integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
dependencies: dependencies:
delayed-stream "~1.0.0" delayed-stream "~1.0.0"
@ -3052,9 +3047,9 @@ core-js-compat@^3.6.2:
semver "7.0.0" semver "7.0.0"
core-js-pure@^3.0.0: core-js-pure@^3.0.0:
version "3.6.4" version "3.6.5"
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.4.tgz#4bf1ba866e25814f149d4e9aaa08c36173506e3a" resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.5.tgz#c79e75f5e38dbc85a662d91eea52b8256d53b813"
integrity sha512-epIhRLkXdgv32xIUFaaAry2wdxZYBi6bgM7cB136dzzXXa+dFyRLTZeLUJxnd8ShrmyVXBub63n2NHo2JAt8Cw== integrity sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA==
core-js@^2.4.0: core-js@^2.4.0:
version "2.6.1" version "2.6.1"
@ -3235,18 +3230,18 @@ css-loader@^3.4.2:
postcss-value-parser "^4.0.2" postcss-value-parser "^4.0.2"
schema-utils "^2.6.0" schema-utils "^2.6.0"
css-select-base-adapter@~0.1.0: css-select-base-adapter@^0.1.1:
version "0.1.1" version "0.1.1"
resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7" resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7"
integrity sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w== integrity sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==
css-select@^2.0.0: css-select@^2.0.0:
version "2.0.2" version "2.1.0"
resolved "https://registry.yarnpkg.com/css-select/-/css-select-2.0.2.tgz#ab4386cec9e1f668855564b17c3733b43b2a5ede" resolved "https://registry.yarnpkg.com/css-select/-/css-select-2.1.0.tgz#6a34653356635934a81baca68d0255432105dbef"
integrity sha512-dSpYaDVoWaELjvZ3mS6IKZM/y2PMPa/XYoEfYNZePL4U/XgyxZNroHEHReDx/d+VgXh9VbCTtFqLkFbmeqeaRQ== integrity sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==
dependencies: dependencies:
boolbase "^1.0.0" boolbase "^1.0.0"
css-what "^2.1.2" css-what "^3.2.1"
domutils "^1.7.0" domutils "^1.7.0"
nth-check "^1.0.2" nth-check "^1.0.2"
@ -3265,37 +3260,37 @@ css-system-font-keywords@^1.0.0:
resolved "https://registry.yarnpkg.com/css-system-font-keywords/-/css-system-font-keywords-1.0.0.tgz#85c6f086aba4eb32c571a3086affc434b84823ed" resolved "https://registry.yarnpkg.com/css-system-font-keywords/-/css-system-font-keywords-1.0.0.tgz#85c6f086aba4eb32c571a3086affc434b84823ed"
integrity sha1-hcbwhquk6zLFcaMIav/ENLhII+0= integrity sha1-hcbwhquk6zLFcaMIav/ENLhII+0=
css-tree@1.0.0-alpha.28: css-tree@1.0.0-alpha.37:
version "1.0.0-alpha.28" version "1.0.0-alpha.37"
resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.28.tgz#8e8968190d886c9477bc8d61e96f61af3f7ffa7f" resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.37.tgz#98bebd62c4c1d9f960ec340cf9f7522e30709a22"
integrity sha512-joNNW1gCp3qFFzj4St6zk+Wh/NBv0vM5YbEreZk0SD4S23S+1xBKb6cLDg2uj4P4k/GUMlIm6cKIDqIG+vdt0w== integrity sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==
dependencies: dependencies:
mdn-data "~1.1.0" mdn-data "2.0.4"
source-map "^0.5.3" source-map "^0.6.1"
css-tree@1.0.0-alpha.29: css-tree@1.0.0-alpha.39:
version "1.0.0-alpha.29" version "1.0.0-alpha.39"
resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.29.tgz#3fa9d4ef3142cbd1c301e7664c1f352bd82f5a39" resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.39.tgz#2bff3ffe1bb3f776cf7eefd91ee5cba77a149eeb"
integrity sha512-sRNb1XydwkW9IOci6iB2xmy8IGCj6r/fr+JWitvJ2JxQRPzN3T4AGGVWCMlVmVwM1gtgALJRmGIlWv5ppnGGkg== integrity sha512-7UvkEYgBAHRG9Nt980lYxjsTrCyHFN53ky3wVsDkiMdVqylqRt+Zc+jm5qw7/qyOvN2dHSYtX0e4MbCCExSvnA==
dependencies: dependencies:
mdn-data "~1.1.0" mdn-data "2.0.6"
source-map "^0.5.3" source-map "^0.6.1"
css-unit-converter@^1.1.1: css-unit-converter@^1.1.1:
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/css-unit-converter/-/css-unit-converter-1.1.1.tgz#d9b9281adcfd8ced935bdbaba83786897f64e996" resolved "https://registry.yarnpkg.com/css-unit-converter/-/css-unit-converter-1.1.1.tgz#d9b9281adcfd8ced935bdbaba83786897f64e996"
integrity sha1-2bkoGtz9jO2TW9urqDeGiX9k6ZY= integrity sha1-2bkoGtz9jO2TW9urqDeGiX9k6ZY=
css-url-regex@^1.1.0: css-what@2.1:
version "1.1.0"
resolved "https://registry.yarnpkg.com/css-url-regex/-/css-url-regex-1.1.0.tgz#83834230cc9f74c457de59eebd1543feeb83b7ec"
integrity sha1-g4NCMMyfdMRX3lnuvRVD/uuDt+w=
css-what@2.1, css-what@^2.1.2:
version "2.1.3" version "2.1.3"
resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2" resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2"
integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg== integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==
css-what@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.2.1.tgz#f4a8f12421064621b456755e34a03a2c22df5da1"
integrity sha512-WwOrosiQTvyms+Ti5ZC5vGEK0Vod3FTt1ca+payZqvKuGJF+dq7bG63DstxtN0dpm6FxY27a/zS3Wten+gEtGw==
cssesc@^2.0.0: cssesc@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-2.0.0.tgz#3b13bd1bb1cb36e1bcb5a4dcd27f54c5dcb35703" resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-2.0.0.tgz#3b13bd1bb1cb36e1bcb5a4dcd27f54c5dcb35703"
@ -3374,12 +3369,12 @@ cssnano@^4.1.10:
is-resolvable "^1.0.0" is-resolvable "^1.0.0"
postcss "^7.0.0" postcss "^7.0.0"
csso@^3.5.0: csso@^4.0.2:
version "3.5.1" version "4.0.3"
resolved "https://registry.yarnpkg.com/csso/-/csso-3.5.1.tgz#7b9eb8be61628973c1b261e169d2f024008e758b" resolved "https://registry.yarnpkg.com/csso/-/csso-4.0.3.tgz#0d9985dc852c7cc2b2cacfbbe1079014d1a8e903"
integrity sha512-vrqULLffYU1Q2tLdJvaCYbONStnfkfimRxXNaGjxMldI0C7JPBC4rB1RyjhfdZ4m1frm8pM9uRPKH3d2knZ8gg== integrity sha512-NL3spysxUkcrOgnpsT4Xdl2aiEiBG6bXswAABQVHcMrfjjBisFOKwLDOmf4wf32aPdcJws1zds2B0Rg+jqMyHQ==
dependencies: dependencies:
css-tree "1.0.0-alpha.29" css-tree "1.0.0-alpha.39"
cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0": cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0":
version "0.3.8" version "0.3.8"
@ -3961,7 +3956,7 @@ error-stack-parser@^2.0.6:
dependencies: dependencies:
stackframe "^1.1.1" stackframe "^1.1.1"
es-abstract@^1.13.0, es-abstract@^1.17.0, es-abstract@^1.17.0-next.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.5, es-abstract@^1.5.1: es-abstract@^1.13.0, es-abstract@^1.17.0, es-abstract@^1.17.0-next.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.5:
version "1.17.5" version "1.17.5"
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.5.tgz#d8c9d1d66c8981fb9200e2251d799eee92774ae9" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.5.tgz#d8c9d1d66c8981fb9200e2251d799eee92774ae9"
integrity sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg== integrity sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==
@ -4524,9 +4519,9 @@ fast-deep-equal@^3.1.1:
integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA== integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==
fast-json-stable-stringify@^2.0.0: fast-json-stable-stringify@^2.0.0:
version "2.0.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
fast-levenshtein@~2.0.6: fast-levenshtein@~2.0.6:
version "2.0.6" version "2.0.6"
@ -4560,9 +4555,9 @@ fb-watchman@^2.0.0:
bser "^2.0.0" bser "^2.0.0"
figgy-pudding@^3.5.1: figgy-pudding@^3.5.1:
version "3.5.1" version "3.5.2"
resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790" resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e"
integrity sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w== integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==
figures@^1.3.5: figures@^1.3.5:
version "1.7.0" version "1.7.0"
@ -4722,9 +4717,9 @@ flat-cache@^2.0.1:
write "1.0.3" write "1.0.3"
flatted@^2.0.0: flatted@^2.0.0:
version "2.0.0" version "2.0.2"
resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.0.tgz#55122b6536ea496b4b44893ee2608141d10d9916" resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138"
integrity sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg== integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==
flush-write-stream@^1.0.0: flush-write-stream@^1.0.0:
version "1.1.1" version "1.1.1"
@ -4890,9 +4885,9 @@ functional-red-black-tree@^1.0.1:
integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
functions-have-names@^1.2.0: functions-have-names@^1.2.0:
version "1.2.0" version "1.2.1"
resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.0.tgz#83da7583e4ea0c9ac5ff530f73394b033e0bf77d" resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.1.tgz#a981ac397fa0c9964551402cdc5533d7a4d52f91"
integrity sha512-zKXyzksTeaCSw5wIX79iCA40YAa6CJMJgNg9wdkU/ERBrIdPSimPICYiLp65lRbSBqtiHql/HZfS2DyI/AH6tQ== integrity sha512-j48B/ZI7VKs3sgeI2cZp7WXWmZXu7Iq5pl5/vptV5N2mq+DGFuS/ulaDjtaoLpYzuD6u8UgrUKHfgo7fDTSiBA==
gauge@~2.7.3: gauge@~2.7.3:
version "2.7.4" version "2.7.4"
@ -5119,7 +5114,7 @@ har-schema@^2.0.0:
resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=
har-validator@~5.1.0: har-validator@~5.1.3:
version "5.1.3" version "5.1.3"
resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080" resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080"
integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g== integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==
@ -5275,9 +5270,9 @@ hoopy@^0.1.4:
integrity sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ== integrity sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==
hosted-git-info@^2.1.4: hosted-git-info@^2.1.4:
version "2.7.1" version "2.8.8"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488"
integrity sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w== integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==
hpack.js@^2.1.6: hpack.js@^2.1.6:
version "2.1.6" version "2.1.6"
@ -5759,13 +5754,6 @@ is-buffer@^2.0.2:
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.3.tgz#4ecf3fcf749cbd1e472689e109ac66261a25e725" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.3.tgz#4ecf3fcf749cbd1e472689e109ac66261a25e725"
integrity sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw== integrity sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==
is-builtin-module@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe"
integrity sha1-VAVy0096wxGfj3bDDLwbHgN6/74=
dependencies:
builtin-modules "^1.0.0"
is-callable@^1.1.4, is-callable@^1.1.5: is-callable@^1.1.4, is-callable@^1.1.5:
version "1.1.5" version "1.1.5"
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.5.tgz#f7e46b596890456db74e7f6e976cb3273d06faab" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.5.tgz#f7e46b596890456db74e7f6e976cb3273d06faab"
@ -6561,7 +6549,7 @@ js-string-escape@1.0.1:
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
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: 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" version "3.13.1"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847"
integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==
@ -6928,9 +6916,9 @@ lodash@^4.0.0, lodash@^4.13.1, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11,
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
loglevel@^1.6.6: loglevel@^1.6.6:
version "1.6.6" version "1.6.8"
resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.6.tgz#0ee6300cc058db6b3551fa1c4bf73b83bb771312" resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.8.tgz#8a25fb75d092230ecd4457270d80b54e28011171"
integrity sha512-Sgr5lbboAUBo3eXCSPL4/KoVz3ROKquOjcctxmHIt+vol2DrqTQe3SwkKKuYhEiWB5kYa13YyopJ69deJ1irzQ== integrity sha512-bsU7+gc9AJ2SqpzxwU3+1fedl8zAntbtC5XYlt3s2j1hJcn2PsXSmgN8TaLG/J1/2mod4+cE/3vNL70/c1RNCA==
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0: loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0:
version "1.4.0" version "1.4.0"
@ -7013,10 +7001,15 @@ md5.js@^1.3.4:
inherits "^2.0.1" inherits "^2.0.1"
safe-buffer "^5.1.2" safe-buffer "^5.1.2"
mdn-data@~1.1.0: mdn-data@2.0.4:
version "1.1.4" version "2.0.4"
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-1.1.4.tgz#50b5d4ffc4575276573c4eedb8780812a8419f01" resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b"
integrity sha512-FSYbp3lyKjyj3E7fMl6rYvUdX0FBXaluGqlFoYESWQlyUTq8R+wp0rkFxoYFqZlHCvsUXGjyJmLQSnXToYhOSA== integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==
mdn-data@2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.6.tgz#852dc60fcaa5daa2e8cf6c9189c440ed3e042978"
integrity sha512-rQvjv71olwNHgiTbfPZFkJtjNMciWgswYeciZhtvWLO8bmX3TnhyA62I6sTWOyZssWHJJjY6/KiWwqQsWWsqOA==
media-typer@0.3.0: media-typer@0.3.0:
version "0.3.0" version "0.3.0"
@ -7100,34 +7093,22 @@ miller-rabin@^4.0.0:
bn.js "^4.0.0" bn.js "^4.0.0"
brorand "^1.0.1" brorand "^1.0.1"
mime-db@1.40.0: mime-db@1.44.0:
version "1.40.0" version "1.44.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92"
integrity sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA== integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==
"mime-db@>= 1.40.0 < 2": "mime-db@>= 1.40.0 < 2":
version "1.42.0" version "1.42.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.42.0.tgz#3e252907b4c7adb906597b4b65636272cf9e7bac" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.42.0.tgz#3e252907b4c7adb906597b4b65636272cf9e7bac"
integrity sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ== integrity sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ==
mime-db@~1.37.0: mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24:
version "1.37.0" version "2.1.27"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.37.0.tgz#0b6a0ce6fdbe9576e25f1f2d2fde8830dc0ad0d8" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f"
integrity sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg== integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==
mime-types@^2.1.12, mime-types@~2.1.19:
version "2.1.21"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.21.tgz#28995aa1ecb770742fe6ae7e58f9181c744b3f96"
integrity sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==
dependencies: dependencies:
mime-db "~1.37.0" mime-db "1.44.0"
mime-types@~2.1.17, mime-types@~2.1.24:
version "2.1.24"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.24.tgz#b6f8d0b3e951efb77dedeca194cff6d16f676f81"
integrity sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==
dependencies:
mime-db "1.40.0"
mime@1.6.0: mime@1.6.0:
version "1.6.0" version "1.6.0"
@ -7253,14 +7234,7 @@ mixin-deep@^1.2.0:
for-in "^1.0.2" for-in "^1.0.2"
is-extendable "^1.0.1" is-extendable "^1.0.1"
mkdirp@^0.5, mkdirp@^0.5.3, mkdirp@~0.5.1: mkdirp@^0.5, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@~0.5.1:
version "0.5.4"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.4.tgz#fd01504a6797ec5c9be81ff43d204961ed64a512"
integrity sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw==
dependencies:
minimist "^1.2.5"
mkdirp@^0.5.0, mkdirp@^0.5.1:
version "0.5.5" version "0.5.5"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
@ -7272,10 +7246,10 @@ mkdirp@^1.0.4:
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
moo@^0.4.3: moo@^0.5.0:
version "0.4.3" version "0.5.1"
resolved "https://registry.yarnpkg.com/moo/-/moo-0.4.3.tgz#3f847a26f31cf625a956a87f2b10fbc013bfd10e" resolved "https://registry.yarnpkg.com/moo/-/moo-0.5.1.tgz#7aae7f384b9b09f620b6abf6f74ebbcd1b65dbc4"
integrity sha512-gFD2xGCl8YFgGHsqJ9NKRVdwlioeW3mI1iqfLNYQOv0+6JRwG58Zk9DIGQgyIaffSYaO1xsKnMaYzzNr1KyIAw== integrity sha512-I1mnb5xn4fO80BH9BLcF0yLypy2UKl+Cb01Fu0hJRkJjlCRtxZMWkTdAtDd5ZqCOxtCkhmRwyI57vWT+1iZ67w==
mousetrap@^1.5.2: mousetrap@^1.5.2:
version "1.6.5" version "1.6.5"
@ -7360,12 +7334,12 @@ natural-compare@^1.4.0:
integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
nearley@^2.7.10: nearley@^2.7.10:
version "2.16.0" version "2.19.2"
resolved "https://registry.yarnpkg.com/nearley/-/nearley-2.16.0.tgz#77c297d041941d268290ec84b739d0ee297e83a7" resolved "https://registry.yarnpkg.com/nearley/-/nearley-2.19.2.tgz#40cafbf235121ae94b1aa1e585890d24fade182d"
integrity sha512-Tr9XD3Vt/EujXbZBv6UAHYoLUSMQAxSsTnm9K3koXzjzNWY195NqALeyrzLZBKzAkL3gl92BcSogqrHjD8QuUg== integrity sha512-h6lygT0BWAGErDvoE2LfI+tDeY2+UUrqG5dcBPdCmjnjud9z1wE0P7ljb85iNbE93YA+xJLpoSYGMuUqhnSSSA==
dependencies: dependencies:
commander "^2.19.0" commander "^2.19.0"
moo "^0.4.3" moo "^0.5.0"
railroad-diagrams "^1.0.0" railroad-diagrams "^1.0.0"
randexp "0.4.6" randexp "0.4.6"
semver "^5.4.1" semver "^5.4.1"
@ -7484,12 +7458,12 @@ nopt@^4.0.1:
osenv "^0.1.4" osenv "^0.1.4"
normalize-package-data@^2.3.2: normalize-package-data@^2.3.2:
version "2.4.0" version "2.5.0"
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
integrity sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw== integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==
dependencies: dependencies:
hosted-git-info "^2.1.4" hosted-git-info "^2.1.4"
is-builtin-module "^1.0.0" resolve "^1.10.0"
semver "2 || 3 || 4 || 5" semver "2 || 3 || 4 || 5"
validate-npm-package-license "^3.0.1" validate-npm-package-license "^3.0.1"
@ -7666,13 +7640,13 @@ object.fromentries@^2.0.2:
function-bind "^1.1.1" function-bind "^1.1.1"
has "^1.0.3" has "^1.0.3"
object.getownpropertydescriptors@^2.0.3: object.getownpropertydescriptors@^2.1.0:
version "2.0.3" version "2.1.0"
resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16" resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz#369bf1f9592d8ab89d712dced5cb81c7c5352649"
integrity sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY= integrity sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==
dependencies: dependencies:
define-properties "^1.1.2" define-properties "^1.1.3"
es-abstract "^1.5.1" es-abstract "^1.17.0-next.1"
object.pick@^1.3.0: object.pick@^1.3.0:
version "1.3.0" version "1.3.0"
@ -7681,7 +7655,7 @@ object.pick@^1.3.0:
dependencies: dependencies:
isobject "^3.0.1" isobject "^3.0.1"
object.values@^1.0.4, object.values@^1.1.0, object.values@^1.1.1: object.values@^1.1.0, object.values@^1.1.1:
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.1.tgz#68a99ecde356b7e9295a3c5e0ce31dc8c953de5e" resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.1.tgz#68a99ecde356b7e9295a3c5e0ce31dc8c953de5e"
integrity sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA== integrity sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==
@ -8745,10 +8719,10 @@ prr@~1.0.1:
resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476"
integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY=
psl@^1.1.24, psl@^1.1.28: psl@^1.1.28:
version "1.1.31" version "1.8.0"
resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.31.tgz#e9aa86d0101b5b105cbe93ac6b784cd547276184" resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24"
integrity sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw== integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==
public-encrypt@^4.0.0: public-encrypt@^4.0.0:
version "4.0.3" version "4.0.3"
@ -8792,7 +8766,7 @@ punycode@1.3.2:
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=
punycode@^1.2.4, punycode@^1.4.1: punycode@^1.2.4:
version "1.4.1" version "1.4.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= integrity sha1-wNWmOycYgArY4esPpSachN1BhF4=
@ -9223,10 +9197,10 @@ read-pkg@^3.0.0:
normalize-package-data "^2.3.2" normalize-package-data "^2.3.2"
path-type "^3.0.0" path-type "^3.0.0"
"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: "readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.6, readable-stream@~2.3.6:
version "2.3.6" version "2.3.7"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
dependencies: dependencies:
core-util-is "~1.0.0" core-util-is "~1.0.0"
inherits "~2.0.3" inherits "~2.0.3"
@ -9236,10 +9210,10 @@ read-pkg@^3.0.0:
string_decoder "~1.1.1" string_decoder "~1.1.1"
util-deprecate "~1.0.1" util-deprecate "~1.0.1"
readable-stream@^2.0.2, readable-stream@^2.0.6: readable-stream@^2.0.1, readable-stream@^2.3.3:
version "2.3.7" version "2.3.6"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf"
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==
dependencies: dependencies:
core-util-is "~1.0.0" core-util-is "~1.0.0"
inherits "~2.0.3" inherits "~2.0.3"
@ -9493,9 +9467,9 @@ request-promise-native@^1.0.5:
tough-cookie ">=2.3.3" tough-cookie ">=2.3.3"
request@^2.87.0: request@^2.87.0:
version "2.88.0" version "2.88.2"
resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3"
integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg== integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==
dependencies: dependencies:
aws-sign2 "~0.7.0" aws-sign2 "~0.7.0"
aws4 "^1.8.0" aws4 "^1.8.0"
@ -9504,7 +9478,7 @@ request@^2.87.0:
extend "~3.0.2" extend "~3.0.2"
forever-agent "~0.6.1" forever-agent "~0.6.1"
form-data "~2.3.2" form-data "~2.3.2"
har-validator "~5.1.0" har-validator "~5.1.3"
http-signature "~1.2.0" http-signature "~1.2.0"
is-typedarray "~1.0.0" is-typedarray "~1.0.0"
isstream "~0.1.2" isstream "~0.1.2"
@ -9514,7 +9488,7 @@ request@^2.87.0:
performance-now "^2.1.0" performance-now "^2.1.0"
qs "~6.5.2" qs "~6.5.2"
safe-buffer "^5.1.2" safe-buffer "^5.1.2"
tough-cookie "~2.4.3" tough-cookie "~2.5.0"
tunnel-agent "^0.6.0" tunnel-agent "^0.6.0"
uuid "^3.3.2" uuid "^3.3.2"
@ -9621,10 +9595,10 @@ resolve@1.1.7:
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=
resolve@^1.12.0, resolve@^1.15.1, resolve@^1.3.2, resolve@^1.5.0, resolve@^1.8.1: resolve@^1.10.0, resolve@^1.12.0, resolve@^1.15.1, resolve@^1.3.2, resolve@^1.5.0, resolve@^1.8.1:
version "1.15.1" version "1.17.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.15.1.tgz#27bdcdeffeaf2d6244b95bb0f9f4b4653451f3e8" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444"
integrity sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w== integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==
dependencies: dependencies:
path-parse "^1.0.6" path-parse "^1.0.6"
@ -10168,7 +10142,7 @@ source-map@0.5.6:
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412"
integrity sha1-dc449SvwczxafwwRjYEzSiu19BI= integrity sha1-dc449SvwczxafwwRjYEzSiu19BI=
source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7: source-map@^0.5.0, source-map@^0.5.6, source-map@^0.5.7:
version "0.5.7" version "0.5.7"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
@ -10187,9 +10161,9 @@ spdx-correct@^3.0.0:
spdx-license-ids "^3.0.0" spdx-license-ids "^3.0.0"
spdx-exceptions@^2.1.0: spdx-exceptions@^2.1.0:
version "2.2.0" version "2.3.0"
resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz#2ea450aee74f2a89bfb94519c07fcd6f41322977" resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d"
integrity sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA== integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==
spdx-expression-parse@^3.0.0: spdx-expression-parse@^3.0.0:
version "3.0.0" version "3.0.0"
@ -10200,9 +10174,9 @@ spdx-expression-parse@^3.0.0:
spdx-license-ids "^3.0.0" spdx-license-ids "^3.0.0"
spdx-license-ids@^3.0.0: spdx-license-ids@^3.0.0:
version "3.0.3" version "3.0.5"
resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.3.tgz#81c0ce8f21474756148bbb5f3bfc0f36bf15d76e" resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz#3694b5804567a458d3c8045842a6358632f62654"
integrity sha512-uBIcIl3Ih6Phe3XHK1NqboJLdGfwr1UN3k6wSD1dZpmPsIkb8AGNbZYJ1fOBk834+Gxy8rpfDxrS6XLEMZMY2g== integrity sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==
spdy-transport@^3.0.0: spdy-transport@^3.0.0:
version "3.0.0" version "3.0.0"
@ -10276,7 +10250,7 @@ ssri@^7.0.0:
figgy-pudding "^3.5.1" figgy-pudding "^3.5.1"
minipass "^3.1.1" minipass "^3.1.1"
stable@~0.1.6: stable@^0.1.8:
version "0.1.8" version "0.1.8"
resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf"
integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==
@ -10590,22 +10564,21 @@ supports-color@^7.0.0, supports-color@^7.1.0:
has-flag "^4.0.0" has-flag "^4.0.0"
svgo@^1.0.0: svgo@^1.0.0:
version "1.1.1" version "1.3.2"
resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.1.1.tgz#12384b03335bcecd85cfa5f4e3375fed671cb985" resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.3.2.tgz#b6dc511c063346c9e415b81e43401145b96d4167"
integrity sha512-GBkJbnTuFpM4jFbiERHDWhZc/S/kpHToqmZag3aEBjPYK44JAN2QBjvrGIxLOoCyMZjuFQIfTO2eJd8uwLY/9g== integrity sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==
dependencies: dependencies:
coa "~2.0.1" chalk "^2.4.1"
colors "~1.1.2" coa "^2.0.2"
css-select "^2.0.0" css-select "^2.0.0"
css-select-base-adapter "~0.1.0" css-select-base-adapter "^0.1.1"
css-tree "1.0.0-alpha.28" css-tree "1.0.0-alpha.37"
css-url-regex "^1.1.0" csso "^4.0.2"
csso "^3.5.0" js-yaml "^3.13.1"
js-yaml "^3.12.0"
mkdirp "~0.5.1" mkdirp "~0.5.1"
object.values "^1.0.4" object.values "^1.1.0"
sax "~1.2.4" sax "~1.2.4"
stable "~0.1.6" stable "^0.1.8"
unquote "~1.1.1" unquote "~1.1.1"
util.promisify "~1.0.0" util.promisify "~1.0.0"
@ -10873,7 +10846,7 @@ toidentifier@1.0.0:
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553"
integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==
tough-cookie@>=2.3.3, tough-cookie@^2.3.4: tough-cookie@>=2.3.3, tough-cookie@^2.3.4, tough-cookie@~2.5.0:
version "2.5.0" version "2.5.0"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"
integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==
@ -10881,14 +10854,6 @@ tough-cookie@>=2.3.3, tough-cookie@^2.3.4:
psl "^1.1.28" psl "^1.1.28"
punycode "^2.1.1" punycode "^2.1.1"
tough-cookie@~2.4.3:
version "2.4.3"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781"
integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==
dependencies:
psl "^1.1.24"
punycode "^1.4.1"
tr46@^1.0.1: tr46@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09"
@ -11104,12 +11069,14 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1:
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
util.promisify@^1.0.0, util.promisify@~1.0.0: util.promisify@^1.0.0, util.promisify@~1.0.0:
version "1.0.0" version "1.0.1"
resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030" resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.1.tgz#6baf7774b80eeb0f7520d8b81d07982a59abbaee"
integrity sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA== integrity sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==
dependencies: dependencies:
define-properties "^1.1.2" define-properties "^1.1.3"
object.getownpropertydescriptors "^2.0.3" es-abstract "^1.17.2"
has-symbols "^1.0.1"
object.getownpropertydescriptors "^2.1.0"
util@0.10.3: util@0.10.3:
version "0.10.3" version "0.10.3"

Loading…
Cancel
Save