Merge pull request #1324 from ThibG/glitch-soc/merge-upstream
Merge upstream changes
This commit is contained in:
		
						commit
						75b0fa8b76
					
				| 
						 | 
				
			
			@ -5,12 +5,13 @@ aliases:
 | 
			
		|||
    docker:
 | 
			
		||||
      - image: circleci/ruby:2.7-buster-node
 | 
			
		||||
        environment: &ruby_environment
 | 
			
		||||
          BUNDLE_JOBS: 3
 | 
			
		||||
          BUNDLE_RETRY: 3
 | 
			
		||||
          BUNDLE_APP_CONFIG: ./.bundle/
 | 
			
		||||
          BUNDLE_PATH: ./vendor/bundle/
 | 
			
		||||
          DB_HOST: localhost
 | 
			
		||||
          DB_USER: root
 | 
			
		||||
          RAILS_ENV: test
 | 
			
		||||
          PARALLEL_TEST_PROCESSORS: 4
 | 
			
		||||
          ALLOW_NOPAM: true
 | 
			
		||||
          CONTINUOUS_INTEGRATION: true
 | 
			
		||||
          DISABLE_SIMPLECOV: true
 | 
			
		||||
| 
						 | 
				
			
			@ -32,9 +33,9 @@ aliases:
 | 
			
		|||
  - &restore_ruby_dependencies
 | 
			
		||||
    restore_cache:
 | 
			
		||||
      keys:
 | 
			
		||||
        - v2-ruby-dependencies-{{ checksum "/tmp/.ruby-version" }}-{{ checksum "Gemfile.lock" }}
 | 
			
		||||
        - v2-ruby-dependencies-{{ checksum "/tmp/.ruby-version" }}-
 | 
			
		||||
        - v2-ruby-dependencies-
 | 
			
		||||
        - v3-ruby-dependencies-{{ checksum "/tmp/.ruby-version" }}-{{ checksum "Gemfile.lock" }}
 | 
			
		||||
        - v3-ruby-dependencies-{{ checksum "/tmp/.ruby-version" }}-
 | 
			
		||||
        - v3-ruby-dependencies-
 | 
			
		||||
 | 
			
		||||
  - &install_steps
 | 
			
		||||
    steps:
 | 
			
		||||
| 
						 | 
				
			
			@ -42,11 +43,13 @@ aliases:
 | 
			
		|||
      - *attach_workspace
 | 
			
		||||
      - restore_cache:
 | 
			
		||||
          keys:
 | 
			
		||||
            - v1-node-dependencies-{{ checksum "yarn.lock" }}
 | 
			
		||||
            - v1-node-dependencies-
 | 
			
		||||
      - run: yarn install --frozen-lockfile
 | 
			
		||||
            - v2-node-dependencies-{{ checksum "yarn.lock" }}
 | 
			
		||||
            - v2-node-dependencies-
 | 
			
		||||
      - run:
 | 
			
		||||
          name: Install yarn dependencies
 | 
			
		||||
          command: yarn install --frozen-lockfile
 | 
			
		||||
      - save_cache:
 | 
			
		||||
          key: v1-node-dependencies-{{ checksum "yarn.lock" }}
 | 
			
		||||
          key: v2-node-dependencies-{{ checksum "yarn.lock" }}
 | 
			
		||||
          paths:
 | 
			
		||||
            - ./node_modules/
 | 
			
		||||
      - *persist_to_workspace
 | 
			
		||||
| 
						 | 
				
			
			@ -57,27 +60,28 @@ aliases:
 | 
			
		|||
        command: |
 | 
			
		||||
          sudo apt-get update
 | 
			
		||||
          sudo apt-get install -y libicu-dev libidn11-dev libprotobuf-dev protobuf-compiler
 | 
			
		||||
          
 | 
			
		||||
          ## TODO: FIX THESE BUSTER DEPENDANCES
 | 
			
		||||
          sudo wget http://ftp.au.debian.org/debian/pool/main/i/icu/libicu57_57.1-6+deb9u3_amd64.deb
 | 
			
		||||
          sudo dpkg -i libicu57_57.1-6+deb9u3_amd64.deb
 | 
			
		||||
          sudo wget http://ftp.au.debian.org/debian/pool/main/p/protobuf/libprotobuf10_3.0.0-9_amd64.deb
 | 
			
		||||
          sudo dpkg -i libprotobuf10_3.0.0-9_amd64.deb
 | 
			
		||||
 | 
			
		||||
  - &install_ruby_dependencies
 | 
			
		||||
      steps:
 | 
			
		||||
        - *attach_workspace
 | 
			
		||||
        - *install_system_dependencies
 | 
			
		||||
        - run: ruby -e 'puts RUBY_VERSION' | tee /tmp/.ruby-version
 | 
			
		||||
        - run:
 | 
			
		||||
            name: Set Ruby version
 | 
			
		||||
            command: ruby -e 'puts RUBY_VERSION' | tee /tmp/.ruby-version
 | 
			
		||||
        - *restore_ruby_dependencies
 | 
			
		||||
        - run: bundle config set clean 'true'
 | 
			
		||||
        - run: bundle config set deployment 'true'
 | 
			
		||||
        - run: bundle config set with 'pam_authentication'
 | 
			
		||||
        - run: bundle config set without 'development production'
 | 
			
		||||
        - run: bundle config set frozen 'true'
 | 
			
		||||
        - run: bundle install --jobs 16 --retry 3 && bundle clean
 | 
			
		||||
        - run:
 | 
			
		||||
            name: Set bundler settings
 | 
			
		||||
            command: |
 | 
			
		||||
              bundle config clean 'true'
 | 
			
		||||
              bundle config deployment 'true'
 | 
			
		||||
              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:
 | 
			
		||||
            key: v2-ruby-dependencies-{{ checksum "/tmp/.ruby-version" }}-{{ checksum "Gemfile.lock" }}
 | 
			
		||||
            key: v3-ruby-dependencies-{{ checksum "/tmp/.ruby-version" }}-{{ checksum "Gemfile.lock" }}
 | 
			
		||||
            paths:
 | 
			
		||||
              - ./.bundle/
 | 
			
		||||
              - ./vendor/bundle/
 | 
			
		||||
| 
						 | 
				
			
			@ -88,17 +92,26 @@ aliases:
 | 
			
		|||
                - ./mastodon/vendor/bundle/
 | 
			
		||||
 | 
			
		||||
  - &test_steps
 | 
			
		||||
      parallelism: 4
 | 
			
		||||
      steps:
 | 
			
		||||
        - *attach_workspace
 | 
			
		||||
        - *install_system_dependencies
 | 
			
		||||
        - run: sudo apt-get install -y ffmpeg
 | 
			
		||||
        - run:
 | 
			
		||||
            name: Prepare Tests
 | 
			
		||||
            command: ./bin/rails parallel:create parallel:load_schema parallel:prepare
 | 
			
		||||
            name: Install FFMPEG
 | 
			
		||||
            command: sudo apt-get install -y ffmpeg
 | 
			
		||||
        - run:
 | 
			
		||||
            name: Run Tests
 | 
			
		||||
            command: ./bin/retry bundle exec parallel_test ./spec/ --group-by filesize --type rspec
 | 
			
		||||
 | 
			
		||||
            name: Load database schema
 | 
			
		||||
            command: ./bin/rails db:create db:schema:load db:seed
 | 
			
		||||
        - run:
 | 
			
		||||
            name: Run rspec in parallel
 | 
			
		||||
            command: |
 | 
			
		||||
              bundle exec rspec --profile 10 \
 | 
			
		||||
                                --format RspecJunitFormatter \
 | 
			
		||||
                                --out test_results/rspec.xml \
 | 
			
		||||
                                --format progress \
 | 
			
		||||
                                $(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings)
 | 
			
		||||
        - store_test_results:
 | 
			
		||||
            path: test_results
 | 
			
		||||
jobs:
 | 
			
		||||
  install:
 | 
			
		||||
    <<: *defaults
 | 
			
		||||
| 
						 | 
				
			
			@ -115,19 +128,14 @@ jobs:
 | 
			
		|||
        environment: *ruby_environment
 | 
			
		||||
    <<: *install_ruby_dependencies
 | 
			
		||||
 | 
			
		||||
  install-ruby2.5:
 | 
			
		||||
    <<: *defaults
 | 
			
		||||
    docker:
 | 
			
		||||
      - image: circleci/ruby:2.5-buster-node
 | 
			
		||||
        environment: *ruby_environment
 | 
			
		||||
    <<: *install_ruby_dependencies
 | 
			
		||||
 | 
			
		||||
  build:
 | 
			
		||||
    <<: *defaults
 | 
			
		||||
    steps:
 | 
			
		||||
      - *attach_workspace
 | 
			
		||||
      - *install_system_dependencies
 | 
			
		||||
      - run: ./bin/rails assets:precompile
 | 
			
		||||
      - run:
 | 
			
		||||
          name: Precompile assets
 | 
			
		||||
          command: ./bin/rails assets:precompile
 | 
			
		||||
      - persist_to_workspace:
 | 
			
		||||
          root: ~/projects/
 | 
			
		||||
          paths:
 | 
			
		||||
| 
						 | 
				
			
			@ -149,10 +157,10 @@ jobs:
 | 
			
		|||
      - *install_system_dependencies
 | 
			
		||||
      - run:
 | 
			
		||||
          name: Create database
 | 
			
		||||
          command: ./bin/rails parallel:create
 | 
			
		||||
          command: ./bin/rails db:create
 | 
			
		||||
      - run:
 | 
			
		||||
          name: Run migrations
 | 
			
		||||
          command: ./bin/rails parallel:migrate
 | 
			
		||||
          command: ./bin/rails db:migrate
 | 
			
		||||
 | 
			
		||||
  test-ruby2.7:
 | 
			
		||||
    <<: *defaults
 | 
			
		||||
| 
						 | 
				
			
			@ -178,35 +186,33 @@ jobs:
 | 
			
		|||
      - image: circleci/redis:5-alpine
 | 
			
		||||
    <<: *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:
 | 
			
		||||
    <<: *defaults
 | 
			
		||||
    docker:
 | 
			
		||||
      - image: circleci/node:12-buster
 | 
			
		||||
    steps:
 | 
			
		||||
      - *attach_workspace
 | 
			
		||||
      - run: ./bin/retry yarn test:jest
 | 
			
		||||
      - run:
 | 
			
		||||
          name: Run jest
 | 
			
		||||
          command: yarn test:jest
 | 
			
		||||
 | 
			
		||||
  check-i18n:
 | 
			
		||||
    <<: *defaults
 | 
			
		||||
    steps:
 | 
			
		||||
      - *attach_workspace
 | 
			
		||||
      - *install_system_dependencies
 | 
			
		||||
      - run: bundle exec i18n-tasks check-normalized
 | 
			
		||||
      - run: bundle exec i18n-tasks unused -l en
 | 
			
		||||
      - run: bundle exec i18n-tasks check-consistent-interpolations
 | 
			
		||||
      - run: bundle exec rake repo:check_locales_files
 | 
			
		||||
      - run:
 | 
			
		||||
          name: Check locale file normalization
 | 
			
		||||
          command: bundle exec i18n-tasks check-normalized
 | 
			
		||||
      - 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:
 | 
			
		||||
  version: 2
 | 
			
		||||
| 
						 | 
				
			
			@ -220,10 +226,6 @@ workflows:
 | 
			
		|||
          requires:
 | 
			
		||||
            - install
 | 
			
		||||
            - install-ruby2.7
 | 
			
		||||
      - install-ruby2.5:
 | 
			
		||||
          requires:
 | 
			
		||||
            - install
 | 
			
		||||
            - install-ruby2.7
 | 
			
		||||
      - build:
 | 
			
		||||
          requires:
 | 
			
		||||
            - install-ruby2.7
 | 
			
		||||
| 
						 | 
				
			
			@ -238,10 +240,6 @@ workflows:
 | 
			
		|||
          requires:
 | 
			
		||||
            - install-ruby2.6
 | 
			
		||||
            - build
 | 
			
		||||
      - test-ruby2.5:
 | 
			
		||||
          requires:
 | 
			
		||||
            - install-ruby2.5
 | 
			
		||||
            - build
 | 
			
		||||
      - test-webui:
 | 
			
		||||
          requires:
 | 
			
		||||
            - install
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										3
									
								
								Gemfile
								
								
								
								
							
							
						
						
									
										3
									
								
								Gemfile
								
								
								
								
							| 
						 | 
				
			
			@ -20,7 +20,7 @@ gem 'makara', '~> 0.4'
 | 
			
		|||
gem 'pghero', '~> 2.4'
 | 
			
		||||
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-openstack', '~> 0.3', require: false
 | 
			
		||||
gem 'paperclip', '~> 6.0'
 | 
			
		||||
| 
						 | 
				
			
			@ -129,6 +129,7 @@ group :test do
 | 
			
		|||
  gem 'simplecov', '~> 0.18', require: false
 | 
			
		||||
  gem 'webmock', '~> 3.8'
 | 
			
		||||
  gem 'parallel_tests', '~> 2.32'
 | 
			
		||||
  gem 'rspec_junit_formatter', '~> 0.4'
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
group :development do
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										21
									
								
								Gemfile.lock
								
								
								
								
							
							
						
						
									
										21
									
								
								Gemfile.lock
								
								
								
								
							| 
						 | 
				
			
			@ -92,7 +92,7 @@ GEM
 | 
			
		|||
    av (0.9.0)
 | 
			
		||||
      cocaine (~> 0.5.3)
 | 
			
		||||
    aws-eventstream (1.1.0)
 | 
			
		||||
    aws-partitions (1.296.0)
 | 
			
		||||
    aws-partitions (1.303.0)
 | 
			
		||||
    aws-sdk-core (3.94.0)
 | 
			
		||||
      aws-eventstream (~> 1, >= 1.0.2)
 | 
			
		||||
      aws-partitions (~> 1, >= 1.239.0)
 | 
			
		||||
| 
						 | 
				
			
			@ -101,7 +101,7 @@ GEM
 | 
			
		|||
    aws-sdk-kms (1.30.0)
 | 
			
		||||
      aws-sdk-core (~> 3, >= 3.71.0)
 | 
			
		||||
      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-kms (~> 1)
 | 
			
		||||
      aws-sigv4 (~> 1.1)
 | 
			
		||||
| 
						 | 
				
			
			@ -277,7 +277,7 @@ GEM
 | 
			
		|||
      http-parser (~> 1.2.0)
 | 
			
		||||
    http-cookie (1.0.3)
 | 
			
		||||
      domain_name (~> 0.5)
 | 
			
		||||
    http-form_data (2.2.0)
 | 
			
		||||
    http-form_data (2.3.0)
 | 
			
		||||
    http-parser (1.2.1)
 | 
			
		||||
      ffi-compiler (>= 1.0, < 2.0)
 | 
			
		||||
    http_accept_language (2.1.1)
 | 
			
		||||
| 
						 | 
				
			
			@ -303,7 +303,7 @@ GEM
 | 
			
		|||
    jmespath (1.4.0)
 | 
			
		||||
    json (2.3.0)
 | 
			
		||||
    json-canonicalization (0.2.0)
 | 
			
		||||
    json-ld (3.1.2)
 | 
			
		||||
    json-ld (3.1.3)
 | 
			
		||||
      htmlentities (~> 4.3)
 | 
			
		||||
      json-canonicalization (~> 0.2)
 | 
			
		||||
      link_header (~> 0.0, >= 0.0.8)
 | 
			
		||||
| 
						 | 
				
			
			@ -359,7 +359,7 @@ GEM
 | 
			
		|||
      nokogiri (~> 1.10)
 | 
			
		||||
    mime-types (3.3.1)
 | 
			
		||||
      mime-types-data (~> 3.2015)
 | 
			
		||||
    mime-types-data (3.2019.1009)
 | 
			
		||||
    mime-types-data (3.2020.0425)
 | 
			
		||||
    mimemagic (0.3.4)
 | 
			
		||||
    mini_mime (1.0.2)
 | 
			
		||||
    mini_portile2 (2.4.0)
 | 
			
		||||
| 
						 | 
				
			
			@ -382,7 +382,7 @@ GEM
 | 
			
		|||
      concurrent-ruby (~> 1.0, >= 1.0.2)
 | 
			
		||||
      sidekiq (>= 3.5)
 | 
			
		||||
      statsd-ruby (~> 1.4, >= 1.4.0)
 | 
			
		||||
    oj (3.10.5)
 | 
			
		||||
    oj (3.10.6)
 | 
			
		||||
    omniauth (1.9.1)
 | 
			
		||||
      hashie (>= 3.4.6)
 | 
			
		||||
      rack (>= 1.6.2, < 3)
 | 
			
		||||
| 
						 | 
				
			
			@ -409,7 +409,7 @@ GEM
 | 
			
		|||
      parallel
 | 
			
		||||
    parser (2.7.1.1)
 | 
			
		||||
      ast (~> 2.4.0)
 | 
			
		||||
    parslet (1.8.2)
 | 
			
		||||
    parslet (2.0.0)
 | 
			
		||||
    pastel (0.7.3)
 | 
			
		||||
      equatable (~> 0.6)
 | 
			
		||||
      tty-color (~> 0.5)
 | 
			
		||||
| 
						 | 
				
			
			@ -542,6 +542,8 @@ GEM
 | 
			
		|||
      rspec-core (~> 3.0, >= 3.0.0)
 | 
			
		||||
      sidekiq (>= 2.4.0)
 | 
			
		||||
    rspec-support (3.9.2)
 | 
			
		||||
    rspec_junit_formatter (0.4.1)
 | 
			
		||||
      rspec-core (>= 2, < 4, != 2.12.0)
 | 
			
		||||
    rubocop (0.79.0)
 | 
			
		||||
      jaro_winkler (~> 1.5.1)
 | 
			
		||||
      parallel (~> 1.10)
 | 
			
		||||
| 
						 | 
				
			
			@ -554,7 +556,7 @@ GEM
 | 
			
		|||
      rack (>= 1.1)
 | 
			
		||||
      rubocop (>= 0.72.0)
 | 
			
		||||
    ruby-progressbar (1.10.1)
 | 
			
		||||
    ruby-saml (1.9.0)
 | 
			
		||||
    ruby-saml (1.11.0)
 | 
			
		||||
      nokogiri (>= 1.5.10)
 | 
			
		||||
    rufus-scheduler (3.6.0)
 | 
			
		||||
      fugit (~> 1.1, >= 1.1.6)
 | 
			
		||||
| 
						 | 
				
			
			@ -668,7 +670,7 @@ DEPENDENCIES
 | 
			
		|||
  active_record_query_trace (~> 1.7)
 | 
			
		||||
  addressable (~> 2.7)
 | 
			
		||||
  annotate (~> 3.1)
 | 
			
		||||
  aws-sdk-s3 (~> 1.61)
 | 
			
		||||
  aws-sdk-s3 (~> 1.63)
 | 
			
		||||
  better_errors (~> 2.6)
 | 
			
		||||
  binding_of_caller (~> 0.7)
 | 
			
		||||
  blurhash (~> 0.1)
 | 
			
		||||
| 
						 | 
				
			
			@ -765,6 +767,7 @@ DEPENDENCIES
 | 
			
		|||
  rqrcode (~> 1.1)
 | 
			
		||||
  rspec-rails (~> 4.0)
 | 
			
		||||
  rspec-sidekiq (~> 3.0)
 | 
			
		||||
  rspec_junit_formatter (~> 0.4)
 | 
			
		||||
  rubocop (~> 0.79)
 | 
			
		||||
  rubocop-rails (~> 2.5)
 | 
			
		||||
  ruby-progressbar (~> 1.10)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,7 +28,7 @@ class AccountsController < ApplicationController
 | 
			
		|||
        end
 | 
			
		||||
 | 
			
		||||
        @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)
 | 
			
		||||
        @rss_url         = rss_url
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -141,12 +141,12 @@ class AccountsController < ApplicationController
 | 
			
		|||
    request.path.split('.').first.ends_with?(Addressable::URI.parse("/tagged/#{params[:tag]}").normalize)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def filtered_status_page(params)
 | 
			
		||||
    if params[:min_id].present?
 | 
			
		||||
      filtered_statuses.paginate_by_min_id(PAGE_SIZE, params[:min_id]).reverse
 | 
			
		||||
    else
 | 
			
		||||
      filtered_statuses.paginate_by_max_id(PAGE_SIZE, params[:max_id], params[:since_id]).to_a
 | 
			
		||||
    end
 | 
			
		||||
  def filtered_status_page
 | 
			
		||||
    filtered_statuses.paginate_by_id(PAGE_SIZE, params_slice(:max_id, :min_id, :since_id))
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def params_slice(*keys)
 | 
			
		||||
    params.slice(*keys).permit(*keys)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def restrict_fields_to
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,20 +24,23 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController
 | 
			
		|||
  def set_size
 | 
			
		||||
    case params[:id]
 | 
			
		||||
    when 'featured'
 | 
			
		||||
      @account.pinned_statuses.count
 | 
			
		||||
      @size = @account.pinned_statuses.count
 | 
			
		||||
    else
 | 
			
		||||
      raise ActiveRecord::RecordNotFound
 | 
			
		||||
      not_found
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def scope_for_collection
 | 
			
		||||
    case params[:id]
 | 
			
		||||
    when 'featured'
 | 
			
		||||
      return Status.none if @account.blocking?(signed_request_account)
 | 
			
		||||
 | 
			
		||||
      @account.pinned_statuses
 | 
			
		||||
    else
 | 
			
		||||
      raise ActiveRecord::RecordNotFound
 | 
			
		||||
      # 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
 | 
			
		||||
      # would likely be served the cache from the reverse proxy anyway
 | 
			
		||||
      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)))
 | 
			
		||||
        Status.none
 | 
			
		||||
      else
 | 
			
		||||
        @account.pinned_statuses
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,7 +11,7 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
 | 
			
		|||
  before_action :set_cache_headers
 | 
			
		||||
 | 
			
		||||
  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'
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -50,12 +50,12 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
 | 
			
		|||
    return unless page_requested?
 | 
			
		||||
 | 
			
		||||
    @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)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def page_requested?
 | 
			
		||||
    params[:page] == 'true'
 | 
			
		||||
    truthy_param?(:page)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def page_params
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
class ActivityPub::RepliesController < ActivityPub::BaseController
 | 
			
		||||
  include SignatureAuthentication
 | 
			
		||||
  include SignatureVerification
 | 
			
		||||
  include Authorization
 | 
			
		||||
  include AccountOwnedConcern
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -19,15 +19,19 @@ class ActivityPub::RepliesController < ActivityPub::BaseController
 | 
			
		|||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def pundit_user
 | 
			
		||||
    signed_request_account
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def set_status
 | 
			
		||||
    @status = @account.statuses.find(params[:status_id])
 | 
			
		||||
    authorize @status, :show?
 | 
			
		||||
  rescue Mastodon::NotPermittedError
 | 
			
		||||
    raise ActiveRecord::RecordNotFound
 | 
			
		||||
    not_found
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  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.paginate_by_min_id(DESCENDANTS_LIMIT, params[:min_id])
 | 
			
		||||
  end
 | 
			
		||||
| 
						 | 
				
			
			@ -38,7 +42,7 @@ class ActivityPub::RepliesController < ActivityPub::BaseController
 | 
			
		|||
      type: :unordered,
 | 
			
		||||
      part_of: account_status_replies_url(@account, @status),
 | 
			
		||||
      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?
 | 
			
		||||
| 
						 | 
				
			
			@ -51,16 +55,21 @@ class ActivityPub::RepliesController < ActivityPub::BaseController
 | 
			
		|||
  end
 | 
			
		||||
 | 
			
		||||
  def page_requested?
 | 
			
		||||
    params[:page] == 'true'
 | 
			
		||||
    truthy_param?(:page)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def only_other_accounts?
 | 
			
		||||
    truthy_param?(:only_other_accounts)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def next_page
 | 
			
		||||
    only_other_accounts = !(@replies&.last&.account_id == @account.id && @replies.size == DESCENDANTS_LIMIT)
 | 
			
		||||
 | 
			
		||||
    account_status_replies_url(
 | 
			
		||||
      @account,
 | 
			
		||||
      @status,
 | 
			
		||||
      page: true,
 | 
			
		||||
      min_id: only_other_accounts && !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
 | 
			
		||||
    )
 | 
			
		||||
  end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,7 +18,7 @@ class Api::V1::Polls::VotesController < Api::BaseController
 | 
			
		|||
    @poll = Poll.attached.find(params[:poll_id])
 | 
			
		||||
    authorize @poll.status, :show?
 | 
			
		||||
  rescue Mastodon::NotPermittedError
 | 
			
		||||
    raise ActiveRecord::RecordNotFound
 | 
			
		||||
    not_found
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def vote_params
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,7 +17,7 @@ class Api::V1::PollsController < Api::BaseController
 | 
			
		|||
    @poll = Poll.attached.find(params[:id])
 | 
			
		||||
    authorize @poll.status, :show?
 | 
			
		||||
  rescue Mastodon::NotPermittedError
 | 
			
		||||
    raise ActiveRecord::RecordNotFound
 | 
			
		||||
    not_found
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def refresh_poll
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,6 +4,7 @@ class Api::V1::Push::SubscriptionsController < Api::BaseController
 | 
			
		|||
  before_action -> { doorkeeper_authorize! :push }
 | 
			
		||||
  before_action :require_user!
 | 
			
		||||
  before_action :set_web_push_subscription
 | 
			
		||||
  before_action :check_web_push_subscription, only: [:show, :update]
 | 
			
		||||
 | 
			
		||||
  def create
 | 
			
		||||
    @web_subscription&.destroy!
 | 
			
		||||
| 
						 | 
				
			
			@ -21,16 +22,11 @@ class Api::V1::Push::SubscriptionsController < Api::BaseController
 | 
			
		|||
  end
 | 
			
		||||
 | 
			
		||||
  def show
 | 
			
		||||
    raise ActiveRecord::RecordNotFound if @web_subscription.nil?
 | 
			
		||||
 | 
			
		||||
    render json: @web_subscription, serializer: REST::WebPushSubscriptionSerializer
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def update
 | 
			
		||||
    raise ActiveRecord::RecordNotFound if @web_subscription.nil?
 | 
			
		||||
 | 
			
		||||
    @web_subscription.update!(data: data_params)
 | 
			
		||||
 | 
			
		||||
    render json: @web_subscription, serializer: REST::WebPushSubscriptionSerializer
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -45,12 +41,17 @@ class Api::V1::Push::SubscriptionsController < Api::BaseController
 | 
			
		|||
    @web_subscription = ::Web::PushSubscription.find_by(access_token_id: doorkeeper_token.id)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def check_web_push_subscription
 | 
			
		||||
    not_found if @web_subscription.nil?
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def subscription_params
 | 
			
		||||
    params.require(:subscription).permit(:endpoint, keys: [:auth, :p256dh])
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def data_params
 | 
			
		||||
    return {} if params[:data].blank?
 | 
			
		||||
 | 
			
		||||
    params.require(:data).permit(alerts: [:follow, :follow_request, :favourite, :reblog, :mention, :poll])
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,8 +28,7 @@ class Api::V1::Statuses::MutesController < Api::BaseController
 | 
			
		|||
    @status = Status.find(params[:status_id])
 | 
			
		||||
    authorize @status, :show?
 | 
			
		||||
  rescue Mastodon::NotPermittedError
 | 
			
		||||
    # Reraise in order to get a 404 instead of a 403 error code
 | 
			
		||||
    raise ActiveRecord::RecordNotFound
 | 
			
		||||
    not_found
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def set_conversation
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -68,7 +68,7 @@ class Api::V1::StatusesController < Api::BaseController
 | 
			
		|||
    @status = Status.find(params[:id])
 | 
			
		||||
    authorize @status, :show?
 | 
			
		||||
  rescue Mastodon::NotPermittedError
 | 
			
		||||
    raise ActiveRecord::RecordNotFound
 | 
			
		||||
    not_found
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def set_thread
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -33,7 +33,7 @@ class MediaController < ApplicationController
 | 
			
		|||
  def verify_permitted_status!
 | 
			
		||||
    authorize @media_attachment.status, :show?
 | 
			
		||||
  rescue Mastodon::NotPermittedError
 | 
			
		||||
    raise ActiveRecord::RecordNotFound
 | 
			
		||||
    not_found
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def check_playable
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -42,7 +42,7 @@ class RemoteInteractionController < ApplicationController
 | 
			
		|||
    @status = Status.find(params[:id])
 | 
			
		||||
    authorize @status, :show?
 | 
			
		||||
  rescue Mastodon::NotPermittedError
 | 
			
		||||
    raise ActiveRecord::RecordNotFound
 | 
			
		||||
    not_found
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def set_body_classes
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -49,7 +49,7 @@ class StatusesController < ApplicationController
 | 
			
		|||
 | 
			
		||||
  def embed
 | 
			
		||||
    use_pack 'embed'
 | 
			
		||||
    return not_found if @status.hidden?
 | 
			
		||||
    return not_found if @status.hidden? || @status.reblog?
 | 
			
		||||
 | 
			
		||||
    expires_in 180, public: true
 | 
			
		||||
    response.headers['X-Frame-Options'] = 'ALLOWALL'
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,7 +10,7 @@ delegate(document, '#account_display_name', 'input', ({ target }) => {
 | 
			
		|||
    if (target.value) {
 | 
			
		||||
      name.innerHTML = emojify(escapeTextContentForBrowser(target.value));
 | 
			
		||||
    } 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) {
 | 
			
		||||
  return (dispatch, getState) => {
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
    dispatch({
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -46,7 +46,7 @@ class DropdownMenu extends React.PureComponent {
 | 
			
		|||
    document.addEventListener('keydown', this.handleKeyDown, false);
 | 
			
		||||
    document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
 | 
			
		||||
    if (this.focusedItem && this.props.openedViaKeyboard) {
 | 
			
		||||
      this.focusedItem.focus();
 | 
			
		||||
      this.focusedItem.focus({ preventScroll: true });
 | 
			
		||||
    }
 | 
			
		||||
    this.setState({ mounted: true });
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -64,9 +64,9 @@ export default class ComposerOptionsDropdownContent extends React.PureComponent
 | 
			
		|||
    document.addEventListener('click', this.handleDocumentClick, false);
 | 
			
		||||
    document.addEventListener('touchend', this.handleDocumentClick, withPassive);
 | 
			
		||||
    if (this.focusedItem) {
 | 
			
		||||
      this.focusedItem.focus();
 | 
			
		||||
      this.focusedItem.focus({ preventScroll: true });
 | 
			
		||||
    } else {
 | 
			
		||||
      this.node.firstChild.focus();
 | 
			
		||||
      this.node.firstChild.focus({ preventScroll: true });
 | 
			
		||||
    }
 | 
			
		||||
    this.setState({ mounted: true });
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -99,15 +99,13 @@ function main() {
 | 
			
		|||
    delegate(document, '.custom-emoji', 'mouseout', getEmojiAnimationHandler('data-static'));
 | 
			
		||||
 | 
			
		||||
    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') {
 | 
			
		||||
        contentEl.style.display = 'none';
 | 
			
		||||
        this.parentNode.style.marginBottom = 0;
 | 
			
		||||
      if (statusEl.dataset.spoiler === 'expanded') {
 | 
			
		||||
        statusEl.dataset.spoiler = 'folded';
 | 
			
		||||
        this.textContent = (new IntlMessageFormat(messages['status.show_more'] || 'Show more', locale)).format();
 | 
			
		||||
      } else {
 | 
			
		||||
        contentEl.style.display = 'block';
 | 
			
		||||
        this.parentNode.style.marginBottom = null;
 | 
			
		||||
        statusEl.dataset.spoiler = 'expanded';
 | 
			
		||||
        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) => {
 | 
			
		||||
      const contentEl = spoilerLink.parentNode.parentNode.querySelector('.e-content');
 | 
			
		||||
      const message = (contentEl.style.display === 'block') ? (messages['status.show_less'] || 'Show less') : (messages['status.show_more'] || 'Show more');
 | 
			
		||||
      const statusEl = spoilerLink.parentNode.parentNode;
 | 
			
		||||
      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();
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,7 +24,7 @@ const importStatuses = (state, statuses) =>
 | 
			
		|||
 | 
			
		||||
const deleteStatus = (state, id, references) => {
 | 
			
		||||
  references.forEach(ref => {
 | 
			
		||||
    state = deleteStatus(state, ref[0], []);
 | 
			
		||||
    state = deleteStatus(state, ref, []);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  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 => {
 | 
			
		||||
    if (exclude_account === null || (timeline !== `account:${exclude_account}` && !timeline.startsWith(`account:${exclude_account}:`))) {
 | 
			
		||||
      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
 | 
			
		||||
  references.forEach(ref => {
 | 
			
		||||
    state = deleteStatus(state, ref[0], ref[1], [], exclude_account);
 | 
			
		||||
    state = deleteStatus(state, ref, [], exclude_account);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  return state;
 | 
			
		||||
| 
						 | 
				
			
			@ -122,8 +122,8 @@ const filterTimelines = (state, relationship, statuses) => {
 | 
			
		|||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    references = statuses.filter(item => item.get('reblog') === status.get('id')).map(item => [item.get('id'), item.get('account')]);
 | 
			
		||||
    state      = deleteStatus(state, status.get('id'), status.get('account'), references, relationship.id);
 | 
			
		||||
    references = statuses.filter(item => item.get('reblog') === status.get('id')).map(item => item.get('id'));
 | 
			
		||||
    state      = deleteStatus(state, status.get('id'), references, relationship.id);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  return state;
 | 
			
		||||
| 
						 | 
				
			
			@ -155,7 +155,7 @@ export default function timelines(state = initialState, action) {
 | 
			
		|||
  case TIMELINE_UPDATE:
 | 
			
		||||
    return updateTimeline(state, action.timeline, fromJS(action.status), action.usePendingItems, action.filtered);
 | 
			
		||||
  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:
 | 
			
		||||
    return clearTimeline(state, action.timeline);
 | 
			
		||||
  case ACCOUNT_BLOCK_SUCCESS:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -760,8 +760,13 @@ $small-breakpoint: 960px;
 | 
			
		|||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__counters__wrapper {
 | 
			
		||||
      display: flex;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__counter {
 | 
			
		||||
      padding: 10px;
 | 
			
		||||
      width: 50%;
 | 
			
		||||
 | 
			
		||||
      strong {
 | 
			
		||||
        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 {
 | 
			
		||||
  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%;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.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 {
 | 
			
		||||
    @include avatar-radius();
 | 
			
		||||
    @include avatar-size(36px);
 | 
			
		||||
 | 
			
		||||
    img {
 | 
			
		||||
      @include avatar-radius;
 | 
			
		||||
      width: 100%;
 | 
			
		||||
      height: 100%;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  &-overlay {
 | 
			
		||||
| 
						 | 
				
			
			@ -90,6 +96,12 @@
 | 
			
		|||
    bottom: 0;
 | 
			
		||||
    right: 0;
 | 
			
		||||
    z-index: 1;
 | 
			
		||||
 | 
			
		||||
    img {
 | 
			
		||||
      @include avatar-radius;
 | 
			
		||||
      width: 100%;
 | 
			
		||||
      height: 100%;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -133,6 +133,10 @@ code {
 | 
			
		|||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .otp-hint {
 | 
			
		||||
    margin-bottom: 25px;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .card {
 | 
			
		||||
    margin-bottom: 15px;
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -276,6 +280,14 @@ code {
 | 
			
		|||
        margin-bottom: 25px;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .fields-group.invited-by {
 | 
			
		||||
      margin-bottom: 30px;
 | 
			
		||||
 | 
			
		||||
      .hint {
 | 
			
		||||
        text-align: center;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .input.radio_buttons .radio label {
 | 
			
		||||
| 
						 | 
				
			
			@ -626,6 +638,15 @@ code {
 | 
			
		|||
  @media screen and (max-width: 740px) and (min-width: 441px) {
 | 
			
		||||
    margin-top: 40px;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  &.translation-prompt {
 | 
			
		||||
    text-align: unset;
 | 
			
		||||
    color: unset;
 | 
			
		||||
 | 
			
		||||
    a {
 | 
			
		||||
      text-decoration: underline;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.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 {
 | 
			
		||||
    position: relative;
 | 
			
		||||
    display: flex;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -124,6 +124,16 @@
 | 
			
		|||
 | 
			
		||||
.embed,
 | 
			
		||||
.public-layout {
 | 
			
		||||
  .status__content[data-spoiler=folded] {
 | 
			
		||||
    .e-content {
 | 
			
		||||
      display: none;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    p:first-child {
 | 
			
		||||
      margin-bottom: 0;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .detailed-status {
 | 
			
		||||
    padding: 15px;
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -162,6 +172,13 @@
 | 
			
		|||
    .video-player {
 | 
			
		||||
      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) {
 | 
			
		||||
  return (dispatch, getState) => {
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
    dispatch({
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -46,7 +46,7 @@ class DropdownMenu extends React.PureComponent {
 | 
			
		|||
    document.addEventListener('keydown', this.handleKeyDown, false);
 | 
			
		||||
    document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
 | 
			
		||||
    if (this.focusedItem && this.props.openedViaKeyboard) {
 | 
			
		||||
      this.focusedItem.focus();
 | 
			
		||||
      this.focusedItem.focus({ preventScroll: true });
 | 
			
		||||
    }
 | 
			
		||||
    this.setState({ mounted: true });
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -100,7 +100,7 @@ class PrivacyDropdownMenu extends React.PureComponent {
 | 
			
		|||
  componentDidMount () {
 | 
			
		||||
    document.addEventListener('click', this.handleDocumentClick, false);
 | 
			
		||||
    document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
 | 
			
		||||
    if (this.focusedItem) this.focusedItem.focus();
 | 
			
		||||
    if (this.focusedItem) this.focusedItem.focus({ preventScroll: true });
 | 
			
		||||
    this.setState({ mounted: true });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,7 +25,7 @@ const importStatuses = (state, statuses) =>
 | 
			
		|||
 | 
			
		||||
const deleteStatus = (state, id, references) => {
 | 
			
		||||
  references.forEach(ref => {
 | 
			
		||||
    state = deleteStatus(state, ref[0], []);
 | 
			
		||||
    state = deleteStatus(state, ref, []);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  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 => {
 | 
			
		||||
    if (exclude_account === null || (timeline !== `account:${exclude_account}` && !timeline.startsWith(`account:${exclude_account}:`))) {
 | 
			
		||||
      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
 | 
			
		||||
  references.forEach(ref => {
 | 
			
		||||
    state = deleteStatus(state, ref[0], ref[1], [], exclude_account);
 | 
			
		||||
    state = deleteStatus(state, ref, [], exclude_account);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  return state;
 | 
			
		||||
| 
						 | 
				
			
			@ -117,8 +117,8 @@ const filterTimelines = (state, relationship, statuses) => {
 | 
			
		|||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    references = statuses.filter(item => item.get('reblog') === status.get('id')).map(item => [item.get('id'), item.get('account')]);
 | 
			
		||||
    state      = deleteStatus(state, status.get('id'), status.get('account'), references, relationship.id);
 | 
			
		||||
    references = statuses.filter(item => item.get('reblog') === status.get('id')).map(item => item.get('id'));
 | 
			
		||||
    state      = deleteStatus(state, status.get('id'), references, relationship.id);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  return state;
 | 
			
		||||
| 
						 | 
				
			
			@ -150,7 +150,7 @@ export default function timelines(state = initialState, action) {
 | 
			
		|||
  case TIMELINE_UPDATE:
 | 
			
		||||
    return updateTimeline(state, action.timeline, fromJS(action.status), action.usePendingItems);
 | 
			
		||||
  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:
 | 
			
		||||
    return clearTimeline(state, action.timeline);
 | 
			
		||||
  case ACCOUNT_BLOCK_SUCCESS:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -103,15 +103,13 @@ function main() {
 | 
			
		|||
    delegate(document, '.custom-emoji', 'mouseout', getEmojiAnimationHandler('data-static'));
 | 
			
		||||
 | 
			
		||||
    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') {
 | 
			
		||||
        contentEl.style.display = 'none';
 | 
			
		||||
        this.parentNode.style.marginBottom = 0;
 | 
			
		||||
      if (statusEl.dataset.spoiler === 'expanded') {
 | 
			
		||||
        statusEl.dataset.spoiler = 'folded';
 | 
			
		||||
        this.textContent = (new IntlMessageFormat(messages['status.show_more'] || 'Show more', locale)).format();
 | 
			
		||||
      } else {
 | 
			
		||||
        contentEl.style.display = 'block';
 | 
			
		||||
        this.parentNode.style.marginBottom = null;
 | 
			
		||||
        statusEl.dataset.spoiler = 'expanded';
 | 
			
		||||
        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) => {
 | 
			
		||||
      const contentEl = spoilerLink.parentNode.parentNode.querySelector('.e-content');
 | 
			
		||||
      const message = (contentEl.style.display === 'block') ? (messages['status.show_less'] || 'Show less') : (messages['status.show_more'] || 'Show more');
 | 
			
		||||
      const statusEl = spoilerLink.parentNode.parentNode;
 | 
			
		||||
      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();
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -757,8 +757,13 @@ $small-breakpoint: 960px;
 | 
			
		|||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__counters__wrapper {
 | 
			
		||||
      display: flex;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__counter {
 | 
			
		||||
      padding: 10px;
 | 
			
		||||
      width: 50%;
 | 
			
		||||
 | 
			
		||||
      strong {
 | 
			
		||||
        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 {
 | 
			
		||||
  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 {
 | 
			
		||||
    @include avatar-radius;
 | 
			
		||||
    @include avatar-size(36px);
 | 
			
		||||
 | 
			
		||||
    img {
 | 
			
		||||
      @include avatar-radius;
 | 
			
		||||
      width: 100%;
 | 
			
		||||
      height: 100%;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  &-overlay {
 | 
			
		||||
| 
						 | 
				
			
			@ -1372,6 +1378,12 @@ a .account__avatar {
 | 
			
		|||
    bottom: 0;
 | 
			
		||||
    right: 0;
 | 
			
		||||
    z-index: 1;
 | 
			
		||||
 | 
			
		||||
    img {
 | 
			
		||||
      @include avatar-radius;
 | 
			
		||||
      width: 100%;
 | 
			
		||||
      height: 100%;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -142,6 +142,10 @@ code {
 | 
			
		|||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .otp-hint {
 | 
			
		||||
    margin-bottom: 25px;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .card {
 | 
			
		||||
    margin-bottom: 15px;
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -285,6 +289,14 @@ code {
 | 
			
		|||
        margin-bottom: 25px;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .fields-group.invited-by {
 | 
			
		||||
      margin-bottom: 30px;
 | 
			
		||||
 | 
			
		||||
      .hint {
 | 
			
		||||
        text-align: center;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .input.radio_buttons .radio label {
 | 
			
		||||
| 
						 | 
				
			
			@ -635,6 +647,15 @@ code {
 | 
			
		|||
  @media screen and (max-width: 740px) and (min-width: 441px) {
 | 
			
		||||
    margin-top: 40px;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  &.translation-prompt {
 | 
			
		||||
    text-align: unset;
 | 
			
		||||
    color: unset;
 | 
			
		||||
 | 
			
		||||
    a {
 | 
			
		||||
      text-decoration: underline;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.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 {
 | 
			
		||||
    position: relative;
 | 
			
		||||
    display: flex;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -128,6 +128,16 @@
 | 
			
		|||
 | 
			
		||||
.embed,
 | 
			
		||||
.public-layout {
 | 
			
		||||
  .status__content[data-spoiler=folded] {
 | 
			
		||||
    .e-content {
 | 
			
		||||
      display: none;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    p:first-child {
 | 
			
		||||
      margin-bottom: 0;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .detailed-status {
 | 
			
		||||
    padding: 15px;
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -159,5 +169,12 @@
 | 
			
		|||
    .video-player {
 | 
			
		||||
      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
 | 
			
		||||
#
 | 
			
		||||
#  id                      :bigint(8)        not null, primary key
 | 
			
		||||
#  username                :string           default(""), not null
 | 
			
		||||
#  domain                  :string
 | 
			
		||||
#  secret                  :string           default(""), not null
 | 
			
		||||
#  private_key             :text
 | 
			
		||||
#  public_key              :text             default(""), not null
 | 
			
		||||
#  remote_url              :string           default(""), not null
 | 
			
		||||
#  salmon_url              :string           default(""), not null
 | 
			
		||||
#  hub_url                 :string           default(""), not null
 | 
			
		||||
#  created_at              :datetime         not null
 | 
			
		||||
#  updated_at              :datetime         not null
 | 
			
		||||
#  note                    :text             default(""), not null
 | 
			
		||||
#  display_name            :string           default(""), not null
 | 
			
		||||
#  uri                     :string           default(""), not null
 | 
			
		||||
#  url                     :string
 | 
			
		||||
#  avatar_file_name        :string
 | 
			
		||||
#  avatar_content_type     :string
 | 
			
		||||
#  avatar_file_size        :integer
 | 
			
		||||
#  avatar_updated_at       :datetime
 | 
			
		||||
#  header_file_name        :string
 | 
			
		||||
#  header_content_type     :string
 | 
			
		||||
#  header_file_size        :integer
 | 
			
		||||
#  header_updated_at       :datetime
 | 
			
		||||
#  avatar_remote_url       :string
 | 
			
		||||
#  subscription_expires_at :datetime
 | 
			
		||||
#  locked                  :boolean          default(FALSE), not null
 | 
			
		||||
#  header_remote_url       :string           default(""), not null
 | 
			
		||||
#  last_webfingered_at     :datetime
 | 
			
		||||
#  inbox_url               :string           default(""), not null
 | 
			
		||||
#  outbox_url              :string           default(""), not null
 | 
			
		||||
#  shared_inbox_url        :string           default(""), not null
 | 
			
		||||
#  followers_url           :string           default(""), not null
 | 
			
		||||
#  protocol                :integer          default("ostatus"), not null
 | 
			
		||||
#  memorial                :boolean          default(FALSE), not null
 | 
			
		||||
#  moved_to_account_id     :bigint(8)
 | 
			
		||||
#  featured_collection_url :string
 | 
			
		||||
#  fields                  :jsonb
 | 
			
		||||
#  actor_type              :string
 | 
			
		||||
#  discoverable            :boolean
 | 
			
		||||
#  also_known_as           :string           is an Array
 | 
			
		||||
#  silenced_at             :datetime
 | 
			
		||||
#  suspended_at            :datetime
 | 
			
		||||
#  trust_level             :integer
 | 
			
		||||
#  hide_collections        :boolean
 | 
			
		||||
#  id                            :bigint(8)        not null, primary key
 | 
			
		||||
#  username                      :string           default(""), not null
 | 
			
		||||
#  domain                        :string
 | 
			
		||||
#  secret                        :string           default(""), not null
 | 
			
		||||
#  private_key                   :text
 | 
			
		||||
#  public_key                    :text             default(""), not null
 | 
			
		||||
#  remote_url                    :string           default(""), not null
 | 
			
		||||
#  salmon_url                    :string           default(""), not null
 | 
			
		||||
#  hub_url                       :string           default(""), not null
 | 
			
		||||
#  created_at                    :datetime         not null
 | 
			
		||||
#  updated_at                    :datetime         not null
 | 
			
		||||
#  note                          :text             default(""), not null
 | 
			
		||||
#  display_name                  :string           default(""), not null
 | 
			
		||||
#  uri                           :string           default(""), not null
 | 
			
		||||
#  url                           :string
 | 
			
		||||
#  avatar_file_name              :string
 | 
			
		||||
#  avatar_content_type           :string
 | 
			
		||||
#  avatar_file_size              :integer
 | 
			
		||||
#  avatar_updated_at             :datetime
 | 
			
		||||
#  header_file_name              :string
 | 
			
		||||
#  header_content_type           :string
 | 
			
		||||
#  header_file_size              :integer
 | 
			
		||||
#  header_updated_at             :datetime
 | 
			
		||||
#  avatar_remote_url             :string
 | 
			
		||||
#  subscription_expires_at       :datetime
 | 
			
		||||
#  locked                        :boolean          default(FALSE), not null
 | 
			
		||||
#  header_remote_url             :string           default(""), not null
 | 
			
		||||
#  last_webfingered_at           :datetime
 | 
			
		||||
#  inbox_url                     :string           default(""), not null
 | 
			
		||||
#  outbox_url                    :string           default(""), not null
 | 
			
		||||
#  shared_inbox_url              :string           default(""), not null
 | 
			
		||||
#  followers_url                 :string           default(""), not null
 | 
			
		||||
#  protocol                      :integer          default("ostatus"), not null
 | 
			
		||||
#  memorial                      :boolean          default(FALSE), not null
 | 
			
		||||
#  moved_to_account_id           :bigint(8)
 | 
			
		||||
#  featured_collection_url       :string
 | 
			
		||||
#  fields                        :jsonb
 | 
			
		||||
#  actor_type                    :string
 | 
			
		||||
#  discoverable                  :boolean
 | 
			
		||||
#  also_known_as                 :string           is an Array
 | 
			
		||||
#  silenced_at                   :datetime
 | 
			
		||||
#  suspended_at                  :datetime
 | 
			
		||||
#  trust_level                   :integer
 | 
			
		||||
#  hide_collections              :boolean
 | 
			
		||||
#  avatar_storage_schema_version :integer
 | 
			
		||||
#  header_storage_schema_version :integer
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
class Account < ApplicationRecord
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -82,7 +82,7 @@ module Omniauthable
 | 
			
		|||
      username = starting_username
 | 
			
		||||
      i        = 0
 | 
			
		||||
 | 
			
		||||
      while Account.exists?(username: username)
 | 
			
		||||
      while Account.exists?(username: username, domain: nil)
 | 
			
		||||
        i       += 1
 | 
			
		||||
        username = "#{starting_username}_#{i}"
 | 
			
		||||
      end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,20 +3,21 @@
 | 
			
		|||
#
 | 
			
		||||
# Table name: custom_emojis
 | 
			
		||||
#
 | 
			
		||||
#  id                 :bigint(8)        not null, primary key
 | 
			
		||||
#  shortcode          :string           default(""), not null
 | 
			
		||||
#  domain             :string
 | 
			
		||||
#  image_file_name    :string
 | 
			
		||||
#  image_content_type :string
 | 
			
		||||
#  image_file_size    :integer
 | 
			
		||||
#  image_updated_at   :datetime
 | 
			
		||||
#  created_at         :datetime         not null
 | 
			
		||||
#  updated_at         :datetime         not null
 | 
			
		||||
#  disabled           :boolean          default(FALSE), not null
 | 
			
		||||
#  uri                :string
 | 
			
		||||
#  image_remote_url   :string
 | 
			
		||||
#  visible_in_picker  :boolean          default(TRUE), not null
 | 
			
		||||
#  category_id        :bigint(8)
 | 
			
		||||
#  id                           :bigint(8)        not null, primary key
 | 
			
		||||
#  shortcode                    :string           default(""), not null
 | 
			
		||||
#  domain                       :string
 | 
			
		||||
#  image_file_name              :string
 | 
			
		||||
#  image_content_type           :string
 | 
			
		||||
#  image_file_size              :integer
 | 
			
		||||
#  image_updated_at             :datetime
 | 
			
		||||
#  created_at                   :datetime         not null
 | 
			
		||||
#  updated_at                   :datetime         not null
 | 
			
		||||
#  disabled                     :boolean          default(FALSE), not null
 | 
			
		||||
#  uri                          :string
 | 
			
		||||
#  image_remote_url             :string
 | 
			
		||||
#  visible_in_picker            :boolean          default(TRUE), not null
 | 
			
		||||
#  category_id                  :bigint(8)
 | 
			
		||||
#  image_storage_schema_version :integer
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
class CustomEmoji < ApplicationRecord
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,23 +3,24 @@
 | 
			
		|||
#
 | 
			
		||||
# Table name: media_attachments
 | 
			
		||||
#
 | 
			
		||||
#  id                  :bigint(8)        not null, primary key
 | 
			
		||||
#  status_id           :bigint(8)
 | 
			
		||||
#  file_file_name      :string
 | 
			
		||||
#  file_content_type   :string
 | 
			
		||||
#  file_file_size      :integer
 | 
			
		||||
#  file_updated_at     :datetime
 | 
			
		||||
#  remote_url          :string           default(""), not null
 | 
			
		||||
#  created_at          :datetime         not null
 | 
			
		||||
#  updated_at          :datetime         not null
 | 
			
		||||
#  shortcode           :string
 | 
			
		||||
#  type                :integer          default("image"), not null
 | 
			
		||||
#  file_meta           :json
 | 
			
		||||
#  account_id          :bigint(8)
 | 
			
		||||
#  description         :text
 | 
			
		||||
#  scheduled_status_id :bigint(8)
 | 
			
		||||
#  blurhash            :string
 | 
			
		||||
#  processing          :integer
 | 
			
		||||
#  id                          :bigint(8)        not null, primary key
 | 
			
		||||
#  status_id                   :bigint(8)
 | 
			
		||||
#  file_file_name              :string
 | 
			
		||||
#  file_content_type           :string
 | 
			
		||||
#  file_file_size              :integer
 | 
			
		||||
#  file_updated_at             :datetime
 | 
			
		||||
#  remote_url                  :string           default(""), not null
 | 
			
		||||
#  created_at                  :datetime         not null
 | 
			
		||||
#  updated_at                  :datetime         not null
 | 
			
		||||
#  shortcode                   :string
 | 
			
		||||
#  type                        :integer          default("image"), not null
 | 
			
		||||
#  file_meta                   :json
 | 
			
		||||
#  account_id                  :bigint(8)
 | 
			
		||||
#  description                 :text
 | 
			
		||||
#  scheduled_status_id         :bigint(8)
 | 
			
		||||
#  blurhash                    :string
 | 
			
		||||
#  processing                  :integer
 | 
			
		||||
#  file_storage_schema_version :integer
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
class MediaAttachment < ApplicationRecord
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,25 +3,26 @@
 | 
			
		|||
#
 | 
			
		||||
# Table name: preview_cards
 | 
			
		||||
#
 | 
			
		||||
#  id                 :bigint(8)        not null, primary key
 | 
			
		||||
#  url                :string           default(""), not null
 | 
			
		||||
#  title              :string           default(""), not null
 | 
			
		||||
#  description        :string           default(""), not null
 | 
			
		||||
#  image_file_name    :string
 | 
			
		||||
#  image_content_type :string
 | 
			
		||||
#  image_file_size    :integer
 | 
			
		||||
#  image_updated_at   :datetime
 | 
			
		||||
#  type               :integer          default("link"), not null
 | 
			
		||||
#  html               :text             default(""), not null
 | 
			
		||||
#  author_name        :string           default(""), not null
 | 
			
		||||
#  author_url         :string           default(""), not null
 | 
			
		||||
#  provider_name      :string           default(""), not null
 | 
			
		||||
#  provider_url       :string           default(""), not null
 | 
			
		||||
#  width              :integer          default(0), not null
 | 
			
		||||
#  height             :integer          default(0), not null
 | 
			
		||||
#  created_at         :datetime         not null
 | 
			
		||||
#  updated_at         :datetime         not null
 | 
			
		||||
#  embed_url          :string           default(""), not null
 | 
			
		||||
#  id                           :bigint(8)        not null, primary key
 | 
			
		||||
#  url                          :string           default(""), not null
 | 
			
		||||
#  title                        :string           default(""), not null
 | 
			
		||||
#  description                  :string           default(""), not null
 | 
			
		||||
#  image_file_name              :string
 | 
			
		||||
#  image_content_type           :string
 | 
			
		||||
#  image_file_size              :integer
 | 
			
		||||
#  image_updated_at             :datetime
 | 
			
		||||
#  type                         :integer          default("link"), not null
 | 
			
		||||
#  html                         :text             default(""), not null
 | 
			
		||||
#  author_name                  :string           default(""), not null
 | 
			
		||||
#  author_url                   :string           default(""), not null
 | 
			
		||||
#  provider_name                :string           default(""), not null
 | 
			
		||||
#  provider_url                 :string           default(""), not null
 | 
			
		||||
#  width                        :integer          default(0), not null
 | 
			
		||||
#  height                       :integer          default(0), not null
 | 
			
		||||
#  created_at                   :datetime         not null
 | 
			
		||||
#  updated_at                   :datetime         not null
 | 
			
		||||
#  embed_url                    :string           default(""), not null
 | 
			
		||||
#  image_storage_schema_version :integer
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
class PreviewCard < ApplicationRecord
 | 
			
		||||
| 
						 | 
				
			
			@ -47,6 +48,10 @@ class PreviewCard < ApplicationRecord
 | 
			
		|||
 | 
			
		||||
  before_save :extract_dimensions, if: :link?
 | 
			
		||||
 | 
			
		||||
  def local?
 | 
			
		||||
    false
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def missing_image?
 | 
			
		||||
    width.present? && height.present? && image_file_name.blank?
 | 
			
		||||
  end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -206,12 +206,8 @@ class Status < ApplicationRecord
 | 
			
		|||
  def title
 | 
			
		||||
    if destroyed?
 | 
			
		||||
      "#{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
 | 
			
		||||
      preview = sensitive ? '<sensitive>' : text.slice(0, 20).split("\n")[0]
 | 
			
		||||
      "#{account.acct}: #{preview}"
 | 
			
		||||
      reblog? ? "#{account.acct} shared a status by #{reblog.account.acct}" : "New status by #{account.acct}"
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -404,7 +400,7 @@ class Status < ApplicationRecord
 | 
			
		|||
 | 
			
		||||
      if account.nil?
 | 
			
		||||
        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
 | 
			
		||||
      elsif account.id == target_account.id # author can see own stuff
 | 
			
		||||
        all
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,7 +5,7 @@ class REST::InstanceSerializer < ActiveModel::Serializer
 | 
			
		|||
 | 
			
		||||
  attributes :uri, :title, :short_description, :description, :email,
 | 
			
		||||
             :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
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -76,6 +76,10 @@ class REST::InstanceSerializer < ActiveModel::Serializer
 | 
			
		|||
    Setting.registrations_mode == 'approved'
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def invites_enabled
 | 
			
		||||
    Setting.min_invite_role == 'user'
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def instance_presenter
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,7 +25,18 @@ class FetchResourceService < BaseService
 | 
			
		|||
  end
 | 
			
		||||
 | 
			
		||||
  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
 | 
			
		||||
 | 
			
		||||
  def process_response(response, terminal = false)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -68,11 +68,11 @@
 | 
			
		|||
          .hero-widget__footer__column
 | 
			
		||||
            %h4= t 'about.server_stats'
 | 
			
		||||
 | 
			
		||||
            %div{ style: 'display: flex' }
 | 
			
		||||
              .hero-widget__counter{ style: 'width: 50%' }
 | 
			
		||||
            .hero-widget__counters__wrapper
 | 
			
		||||
              .hero-widget__counter
 | 
			
		||||
                %strong= number_to_human @instance_presenter.user_count, strip_insignificant_zeros: true
 | 
			
		||||
                %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
 | 
			
		||||
                %span
 | 
			
		||||
                  = 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
 | 
			
		||||
      .detailed-status__display-avatar
 | 
			
		||||
        .account__avatar-overlay
 | 
			
		||||
          .account__avatar-overlay-base{ style: "background-image: url('#{moved_to_account.avatar.url(:original)}')" }
 | 
			
		||||
          .account__avatar-overlay-overlay{ style: "background-image: url('#{account.avatar.url(:original)}')" }
 | 
			
		||||
          .account__avatar-overlay-base
 | 
			
		||||
            = image_tag moved_to_account.avatar_static_url
 | 
			
		||||
          .account__avatar-overlay-overlay
 | 
			
		||||
            = image_tag account.avatar_static_url
 | 
			
		||||
 | 
			
		||||
      %span.display-name
 | 
			
		||||
        %bdi
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,7 +2,7 @@
 | 
			
		|||
  %td
 | 
			
		||||
    = admin_account_link_to(account)
 | 
			
		||||
  %td
 | 
			
		||||
    %div{ style: 'margin: -2px 0' }= account_badge(account, all: true)
 | 
			
		||||
    %div.account-badges= account_badge(account, all: true)
 | 
			
		||||
  %td
 | 
			
		||||
    - if 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
 | 
			
		||||
        .account__header__content.emojify= Formatter.instance.simplified_format(account, custom_emojify: true)
 | 
			
		||||
 | 
			
		||||
.dashboard__counters{ style: 'margin-top: 10px' }
 | 
			
		||||
.dashboard__counters.admin-account-counters
 | 
			
		||||
  %div
 | 
			
		||||
    = link_to admin_account_statuses_path(@account.id) do
 | 
			
		||||
      .dashboard__counters__num= number_with_delimiter @account.statuses_count
 | 
			
		||||
| 
						 | 
				
			
			@ -178,18 +178,8 @@
 | 
			
		|||
              = @account.shared_inbox_url
 | 
			
		||||
              = fa_icon DeliveryFailureTracker.available?(@account.shared_inbox_url) ? 'check': 'times'
 | 
			
		||||
 | 
			
		||||
  %div{ style: 'overflow: hidden' }
 | 
			
		||||
    %div{ style: 'float: right' }
 | 
			
		||||
      - 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' }
 | 
			
		||||
  %div.action-buttons
 | 
			
		||||
    %div
 | 
			
		||||
      - 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)
 | 
			
		||||
      - if @account.silenced?
 | 
			
		||||
| 
						 | 
				
			
			@ -216,6 +206,16 @@
 | 
			
		|||
        - else
 | 
			
		||||
          = 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/
 | 
			
		||||
 | 
			
		||||
  - unless @warnings.empty?
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,7 +10,7 @@
 | 
			
		|||
      - unless whitelist_mode?
 | 
			
		||||
        %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?
 | 
			
		||||
      = link_to t('admin.domain_allows.add_new'), new_admin_domain_allow_path, class: 'button'
 | 
			
		||||
    - else
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -45,11 +45,11 @@
 | 
			
		|||
 | 
			
		||||
%hr.spacer/
 | 
			
		||||
 | 
			
		||||
%div{ style: 'overflow: hidden' }
 | 
			
		||||
  %div{ style: 'float: left' }
 | 
			
		||||
%div.action-buttons
 | 
			
		||||
  %div
 | 
			
		||||
    = 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
 | 
			
		||||
      = 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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,9 +22,9 @@
 | 
			
		|||
 | 
			
		||||
%hr.spacer/
 | 
			
		||||
 | 
			
		||||
%div{ style: 'overflow: hidden' }
 | 
			
		||||
  %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.action-buttons
 | 
			
		||||
  %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'
 | 
			
		||||
 | 
			
		||||
  %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.remote'), location: 'remote'
 | 
			
		||||
 | 
			
		||||
  .back-link{ style: 'flex: 1 1 auto; text-align: right' }
 | 
			
		||||
  .back-link
 | 
			
		||||
    = link_to admin_account_path(@account.id) do
 | 
			
		||||
      = fa_icon 'chevron-left fw'
 | 
			
		||||
      = t('admin.statuses.back_to_account')
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -65,9 +65,11 @@
 | 
			
		|||
 | 
			
		||||
%hr.spacer
 | 
			
		||||
 | 
			
		||||
%div{ style: 'overflow: hidden; margin-bottom: 20px; clear: both' }
 | 
			
		||||
%div.action-buttons
 | 
			
		||||
  %div
 | 
			
		||||
 | 
			
		||||
  - if @report.unresolved?
 | 
			
		||||
    %div{ style: 'float: right' }
 | 
			
		||||
    %div
 | 
			
		||||
      - 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.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
 | 
			
		||||
      %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'
 | 
			
		||||
  .back-link{ style: 'flex: 1 1 auto; text-align: right' }
 | 
			
		||||
  .back-link
 | 
			
		||||
    = link_to admin_account_path(@account.id) do
 | 
			
		||||
      = fa_icon 'chevron-left fw'
 | 
			
		||||
      = t('admin.statuses.back_to_account')
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,7 @@
 | 
			
		|||
  = "@#{@account.acct}"
 | 
			
		||||
 | 
			
		||||
.filters
 | 
			
		||||
  .back-link{ style: 'flex: 1 1 auto; text-align: right' }
 | 
			
		||||
  .back-link
 | 
			
		||||
    = link_to admin_account_path(@account.id) do
 | 
			
		||||
      %i.fa.fa-chevron-left.fa-fw
 | 
			
		||||
      = t('admin.statuses.back_to_account')
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -68,9 +68,9 @@
 | 
			
		|||
- if params[:pending_review] == '1' || params[:unreviewed] == '1'
 | 
			
		||||
  %hr.spacer/
 | 
			
		||||
 | 
			
		||||
  %div{ style: 'overflow: hidden' }
 | 
			
		||||
    %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.action-buttons
 | 
			
		||||
    %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'
 | 
			
		||||
 | 
			
		||||
    %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'
 | 
			
		||||
 | 
			
		||||
      .display-name
 | 
			
		||||
        %span{ id: "default_account_display_name", style: "display: none" }= account.username
 | 
			
		||||
        %bdi
 | 
			
		||||
          %strong.emojify.p-name= display_name(account, custom_emojify: true)
 | 
			
		||||
        %span
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,8 +8,8 @@
 | 
			
		|||
  = render 'shared/error_messages', object: resource
 | 
			
		||||
 | 
			
		||||
  - if @invite.present? && @invite.autofollow?
 | 
			
		||||
    .fields-group{ style: 'margin-bottom: 30px' }
 | 
			
		||||
      %p.hint{ style: 'text-align: center' }= t('invites.invited_by')
 | 
			
		||||
    .fields-group.invited-by
 | 
			
		||||
      %p.hint= t('invites.invited_by')
 | 
			
		||||
      = render 'application/card', account: @invite.user.account
 | 
			
		||||
 | 
			
		||||
  = f.simple_fields_for :account do |ff|
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,7 +2,7 @@
 | 
			
		|||
  = t('auth.login')
 | 
			
		||||
 | 
			
		||||
= 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
 | 
			
		||||
    = 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'
 | 
			
		||||
 | 
			
		||||
            .display-name
 | 
			
		||||
              %span{ id: "default_account_display_name", style: "display: none" }= account.username
 | 
			
		||||
              %bdi
 | 
			
		||||
                %strong.emojify.p-name= display_name(account, custom_emojify: true)
 | 
			
		||||
              %span= acct(account)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -40,6 +40,6 @@
 | 
			
		|||
  %body{ class: body_classes }
 | 
			
		||||
    = 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_full.svg')
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,5 +23,5 @@
 | 
			
		|||
  %body.embed
 | 
			
		||||
    = yield
 | 
			
		||||
 | 
			
		||||
    %div{ style: 'display: none'}
 | 
			
		||||
    .logo-resources
 | 
			
		||||
      = render file: Rails.root.join('app', 'javascript', 'images', 'logo_transparent.svg')
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,5 +12,5 @@
 | 
			
		|||
  - else
 | 
			
		||||
    %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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
 | 
			
		||||
  - unless I18n.locale == :en
 | 
			
		||||
    .flash-message{ style: "text-align: unset; color: unset" }
 | 
			
		||||
      #{t 'appearance.localization.body'} #{content_tag(:a, t('appearance.localization.guide_link_text'), href: t('appearance.localization.guide_link'), target: "_blank", rel: "noopener", style: "text-decoration: underline")}
 | 
			
		||||
    .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")}
 | 
			
		||||
 | 
			
		||||
  %h4= t 'appearance.advanced_web_interface'
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,7 +9,7 @@
 | 
			
		|||
 | 
			
		||||
  .fields-row
 | 
			
		||||
    .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
 | 
			
		||||
 | 
			
		||||
  .fields-row
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,12 +15,12 @@
 | 
			
		|||
 | 
			
		||||
  = 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?
 | 
			
		||||
      %p{ :style => ('margin-bottom: 0' unless current_account&.user&.setting_expand_spoilers) }<
 | 
			
		||||
      %p<
 | 
			
		||||
        %span.p-summary> #{Formatter.instance.format_spoiler(status, autoplay: autoplay)} 
 | 
			
		||||
        %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)
 | 
			
		||||
      - 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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,13 +10,15 @@
 | 
			
		|||
          - percent = total_votes_count > 0 ? 100 * option.votes_count / total_votes_count : 0
 | 
			
		||||
          %label.poll__option><
 | 
			
		||||
            %span.poll__number><
 | 
			
		||||
              - if own_votes.include?(index)
 | 
			
		||||
                %i.poll__voted__mark.fa.fa-check
 | 
			
		||||
              = "#{percent.round}%"
 | 
			
		||||
            %span.poll__option__text
 | 
			
		||||
              = 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
 | 
			
		||||
          %label.poll__option><
 | 
			
		||||
            %span.poll__input{ class: poll.multiple? ? 'checkbox' : nil}><
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,12 +19,12 @@
 | 
			
		|||
          %span.display-name__account
 | 
			
		||||
            = acct(status.account)
 | 
			
		||||
            = 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?
 | 
			
		||||
      %p{ :style => ('margin-bottom: 0' unless current_account&.user&.setting_expand_spoilers) }<
 | 
			
		||||
      %p<
 | 
			
		||||
        %span.p-summary> #{Formatter.instance.format_spoiler(status, autoplay: autoplay)} 
 | 
			
		||||
        %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)
 | 
			
		||||
      - 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
 | 
			
		||||
| 
						 | 
				
			
			@ -51,18 +51,18 @@
 | 
			
		|||
 | 
			
		||||
  .status__action-bar
 | 
			
		||||
    .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?
 | 
			
		||||
          = fa_icon 'reply fw'
 | 
			
		||||
        - else
 | 
			
		||||
          = fa_icon 'reply-all fw'
 | 
			
		||||
      .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?
 | 
			
		||||
        = fa_icon 'retweet fw'
 | 
			
		||||
      - elsif status.private_visibility? || status.limited_visibility?
 | 
			
		||||
        = fa_icon 'lock fw'
 | 
			
		||||
      - else
 | 
			
		||||
        = 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'
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,9 +10,25 @@ Paperclip.interpolates :filename do |attachment, style|
 | 
			
		|||
  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!(
 | 
			
		||||
  use_timestamp: false,
 | 
			
		||||
  path: ':class/:attachment/:id_partition/:style/:filename',
 | 
			
		||||
  path: ':prefix_url:class/:attachment/:id_partition/:style/:filename',
 | 
			
		||||
  storage: :fog
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -91,7 +107,7 @@ else
 | 
			
		|||
  Paperclip::Attachment.default_options.merge!(
 | 
			
		||||
    storage: :filesystem,
 | 
			
		||||
    use_timestamp: true,
 | 
			
		||||
    path: File.join(ENV.fetch('PAPERCLIP_ROOT_PATH', File.join(':rails_root', 'public', 'system')), ':class', ':attachment', ':id_partition', ':style', ':filename'),
 | 
			
		||||
    url: ENV.fetch('PAPERCLIP_ROOT_URL', '/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') + '/:prefix_url:class/:attachment/:id_partition/:style/:filename',
 | 
			
		||||
  )
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
  enable_extension "plpgsql"
 | 
			
		||||
| 
						 | 
				
			
			@ -172,6 +172,8 @@ ActiveRecord::Schema.define(version: 2020_04_07_202420) do
 | 
			
		|||
    t.datetime "suspended_at"
 | 
			
		||||
    t.integer "trust_level"
 | 
			
		||||
    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 "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"
 | 
			
		||||
| 
						 | 
				
			
			@ -299,6 +301,7 @@ ActiveRecord::Schema.define(version: 2020_04_07_202420) do
 | 
			
		|||
    t.string "image_remote_url"
 | 
			
		||||
    t.boolean "visible_in_picker", default: true, null: false
 | 
			
		||||
    t.bigint "category_id"
 | 
			
		||||
    t.integer "image_storage_schema_version"
 | 
			
		||||
    t.index ["shortcode", "domain"], name: "index_custom_emojis_on_shortcode_and_domain", unique: true
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -465,6 +468,7 @@ ActiveRecord::Schema.define(version: 2020_04_07_202420) do
 | 
			
		|||
    t.bigint "scheduled_status_id"
 | 
			
		||||
    t.string "blurhash"
 | 
			
		||||
    t.integer "processing"
 | 
			
		||||
    t.integer "file_storage_schema_version"
 | 
			
		||||
    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 ["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 "updated_at", 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
 | 
			
		||||
  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/preview_cards_cli'
 | 
			
		||||
require_relative 'mastodon/cache_cli'
 | 
			
		||||
require_relative 'mastodon/upgrade_cli'
 | 
			
		||||
require_relative 'mastodon/version'
 | 
			
		||||
 | 
			
		||||
module Mastodon
 | 
			
		||||
| 
						 | 
				
			
			@ -49,6 +50,9 @@ module Mastodon
 | 
			
		|||
    desc 'cache SUBCOMMAND ...ARGS', 'Manage cache'
 | 
			
		||||
    subcommand 'cache', Mastodon::CacheCLI
 | 
			
		||||
 | 
			
		||||
    desc 'upgrade SUBCOMMAND ...ARGS', 'Various version upgrade utilities'
 | 
			
		||||
    subcommand 'upgrade', Mastodon::UpgradeCLI
 | 
			
		||||
 | 
			
		||||
    option :dry_run, type: :boolean
 | 
			
		||||
    desc 'self-destruct', 'Erase the server from the federation'
 | 
			
		||||
    long_desc <<~LONG_DESC
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,6 +10,10 @@ Paperclip.options[:log]      = false
 | 
			
		|||
 | 
			
		||||
module Mastodon
 | 
			
		||||
  module CLIHelper
 | 
			
		||||
    def dry_run?
 | 
			
		||||
      options[:dry_run]
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def create_progress_bar(total = nil)
 | 
			
		||||
      ProgressBar.create(total: total, format: '%c/%u |%b%i| %e')
 | 
			
		||||
    end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,7 +23,7 @@ module Mastodon
 | 
			
		|||
      Existing emoji will be skipped unless the --overwrite option
 | 
			
		||||
      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.
 | 
			
		||||
 | 
			
		||||
      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))
 | 
			
		||||
    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
 | 
			
		||||
    desc 'purge', 'Remove all custom emoji'
 | 
			
		||||
    long_desc <<-LONG_DESC
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -85,7 +85,9 @@ module Mastodon
 | 
			
		|||
          record_map = preload_records_from_mixed_objects(objects)
 | 
			
		||||
 | 
			
		||||
          objects.each do |object|
 | 
			
		||||
            path_segments   = object.key.split('/')
 | 
			
		||||
            path_segments = object.key.split('/')
 | 
			
		||||
            path_segments.delete('cache')
 | 
			
		||||
 | 
			
		||||
            model_name      = path_segments.first.classify
 | 
			
		||||
            attachment_name = path_segments[1].singularize
 | 
			
		||||
            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|
 | 
			
		||||
          next if File.directory?(path)
 | 
			
		||||
 | 
			
		||||
          key             = path.gsub("#{root_path}#{File::SEPARATOR}", '')
 | 
			
		||||
          path_segments   = key.split(File::SEPARATOR)
 | 
			
		||||
          key = path.gsub("#{root_path}#{File::SEPARATOR}", '')
 | 
			
		||||
 | 
			
		||||
          path_segments = key.split(File::SEPARATOR)
 | 
			
		||||
          path_segments.delete('cache')
 | 
			
		||||
 | 
			
		||||
          model_name      = path_segments.first.classify
 | 
			
		||||
          record_id       = path_segments[2..-2].join.to_i
 | 
			
		||||
          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'
 | 
			
		||||
    def lookup(url)
 | 
			
		||||
      path          = Addressable::URI.parse(url).path
 | 
			
		||||
      path = Addressable::URI.parse(url).path
 | 
			
		||||
 | 
			
		||||
      path_segments = path.split('/')[2..-1]
 | 
			
		||||
      model_name    = path_segments.first.classify
 | 
			
		||||
      record_id     = path_segments[2..-2].join.to_i
 | 
			
		||||
      path_segments.delete('cache')
 | 
			
		||||
 | 
			
		||||
      model_name = path_segments.first.classify
 | 
			
		||||
      record_id  = path_segments[2..-2].join.to_i
 | 
			
		||||
 | 
			
		||||
      unless PRELOAD_MODEL_WHITELIST.include?(model_name)
 | 
			
		||||
        say("Cannot find corresponding model: #{model_name}", :red)
 | 
			
		||||
| 
						 | 
				
			
			@ -276,7 +284,9 @@ module Mastodon
 | 
			
		|||
      preload_map = Hash.new { |hash, key| hash[key] = [] }
 | 
			
		||||
 | 
			
		||||
      objects.map do |object|
 | 
			
		||||
        segments   = object.key.split('/')
 | 
			
		||||
        segments = object.key.split('/')
 | 
			
		||||
        segments.delete('cache')
 | 
			
		||||
 | 
			
		||||
        model_name = segments.first.classify
 | 
			
		||||
        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
 | 
			
		||||
 | 
			
		||||
    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)
 | 
			
		||||
      return true  if original_filename == other_filename
 | 
			
		||||
      return false if original_filename.nil?
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,21 +3,133 @@
 | 
			
		|||
require 'rails_helper'
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
      it 'returns 200 with "application/activity+json"' do
 | 
			
		||||
        post :show, params: { id: 'featured', account_username: account.username }
 | 
			
		||||
  before do
 | 
			
		||||
    allow(controller).to receive(:signed_request_account).and_return(remote_account)
 | 
			
		||||
 | 
			
		||||
        expect(response).to have_http_status(200)
 | 
			
		||||
        expect(response.content_type).to eq 'application/activity+json'
 | 
			
		||||
    Fabricate(:status_pin, account: account)
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
    context 'id is not "featured"' do
 | 
			
		||||
      it 'returns 404' do
 | 
			
		||||
        post :show, params: { id: 'hoge', account_username: account.username }
 | 
			
		||||
    context 'when id is not "featured"' do
 | 
			
		||||
      it 'returns http not found' do
 | 
			
		||||
        get :show, params: { id: 'hoge', account_username: account.username }
 | 
			
		||||
        expect(response).to have_http_status(404)
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,25 +3,31 @@
 | 
			
		|||
require 'rails_helper'
 | 
			
		||||
 | 
			
		||||
RSpec.describe ActivityPub::InboxesController, type: :controller do
 | 
			
		||||
  describe 'POST #create' do
 | 
			
		||||
    context 'with signed_request_account' do
 | 
			
		||||
      it 'returns 202' do
 | 
			
		||||
        allow(controller).to receive(:signed_request_account) do
 | 
			
		||||
          Fabricate(:account)
 | 
			
		||||
        end
 | 
			
		||||
  let(:remote_account) { nil }
 | 
			
		||||
 | 
			
		||||
  before do
 | 
			
		||||
    allow(controller).to receive(:signed_request_account).and_return(remote_account)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe 'POST #create' do
 | 
			
		||||
    context 'with signature' do
 | 
			
		||||
      let(:remote_account) { Fabricate(:account, domain: 'example.com', protocol: :activitypub) }
 | 
			
		||||
 | 
			
		||||
      before do
 | 
			
		||||
        post :create, body: '{}'
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      it 'returns http accepted' do
 | 
			
		||||
        expect(response).to have_http_status(202)
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    context 'without signed_request_account' do
 | 
			
		||||
      it 'returns 401' do
 | 
			
		||||
        allow(controller).to receive(:signed_request_account) do
 | 
			
		||||
          false
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
    context 'without signature' do
 | 
			
		||||
      before do
 | 
			
		||||
        post :create, body: '{}'
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      it 'returns http not authorized' do
 | 
			
		||||
        expect(response).to have_http_status(401)
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,20 +4,174 @@ RSpec.describe ActivityPub::OutboxesController, type: :controller do
 | 
			
		|||
  let!(:account) { Fabricate(:account) }
 | 
			
		||||
 | 
			
		||||
  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
 | 
			
		||||
 | 
			
		||||
  describe 'GET #show' do
 | 
			
		||||
    before do
 | 
			
		||||
      get :show, params: { account_username: account.username }
 | 
			
		||||
    context 'without signature' do
 | 
			
		||||
      let(:remote_account) { nil }
 | 
			
		||||
 | 
			
		||||
      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 public Cache-Control header' do
 | 
			
		||||
          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
 | 
			
		||||
 | 
			
		||||
    it 'returns http success' do
 | 
			
		||||
      expect(response).to have_http_status(200)
 | 
			
		||||
    end
 | 
			
		||||
    context 'with signature' do
 | 
			
		||||
      let(:remote_account) { Fabricate(:account, domain: 'example.com') }
 | 
			
		||||
      let(:page) { 'true' }
 | 
			
		||||
 | 
			
		||||
    it 'returns application/activity+json' do
 | 
			
		||||
      expect(response.content_type).to eq 'application/activity+json'
 | 
			
		||||
      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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
  render_views
 | 
			
		||||
 | 
			
		||||
  describe '#show' do
 | 
			
		||||
    context 'account is suspended' do
 | 
			
		||||
      it 'returns gone' do
 | 
			
		||||
        account = Fabricate(:account, suspended: true)
 | 
			
		||||
        status = Fabricate(:status, account: account)
 | 
			
		||||
  describe 'GET #show' 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 :show, params: { account_username: account.username, id: status.id }
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      it 'returns http gone' do
 | 
			
		||||
        expect(response).to have_http_status(410)
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    context 'status is not permitted' do
 | 
			
		||||
      it 'raises ActiveRecord::RecordNotFound' do
 | 
			
		||||
        user = Fabricate(:user)
 | 
			
		||||
        status = Fabricate(:status)
 | 
			
		||||
        status.account.block!(user.account)
 | 
			
		||||
    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) }
 | 
			
		||||
 | 
			
		||||
        sign_in(user)
 | 
			
		||||
      before do
 | 
			
		||||
        get :show, params: { account_username: status.account.username, id: status.id }
 | 
			
		||||
 | 
			
		||||
        expect(response).to have_http_status(404)
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    context 'status is a reblog' do
 | 
			
		||||
      it 'redirects to the original status' do
 | 
			
		||||
        original_account = Fabricate(:account, domain: 'example.com')
 | 
			
		||||
        original_status = Fabricate(:status, account: original_account, uri: 'tag:example.com,2017:foo', url: 'https://example.com/123')
 | 
			
		||||
        status = Fabricate(:status, reblog: original_status)
 | 
			
		||||
 | 
			
		||||
        get :show, params: { account_username: status.account.username, id: status.id }
 | 
			
		||||
 | 
			
		||||
        expect(response).to redirect_to(original_status.url)
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    context 'account is not suspended and status is permitted' do
 | 
			
		||||
      it 'assigns @account' do
 | 
			
		||||
        status = Fabricate(:status)
 | 
			
		||||
        get :show, params: { account_username: status.account.username, id: status.id }
 | 
			
		||||
        expect(assigns(:account)).to eq status.account
 | 
			
		||||
    context 'when status is public' do
 | 
			
		||||
      before do
 | 
			
		||||
        get :show, params: { account_username: status.account.username, id: status.id, format: format }
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      it 'assigns @status' do
 | 
			
		||||
        status = Fabricate(:status)
 | 
			
		||||
        get :show, params: { account_username: status.account.username, id: status.id }
 | 
			
		||||
        expect(assigns(:status)).to eq status
 | 
			
		||||
      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 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
 | 
			
		||||
 | 
			
		||||
      it 'assigns @ancestors for ancestors of the status if it is a reply' do
 | 
			
		||||
        ancestor = Fabricate(:status)
 | 
			
		||||
        status = Fabricate(:status, in_reply_to_id: ancestor.id)
 | 
			
		||||
      context 'as JSON' do
 | 
			
		||||
        let(:format) { 'json' }
 | 
			
		||||
 | 
			
		||||
        get :show, params: { account_username: status.account.username, id: status.id }
 | 
			
		||||
        it 'returns http success' do
 | 
			
		||||
          expect(response).to have_http_status(200)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        expect(assigns(:ancestors)).to eq [ancestor]
 | 
			
		||||
        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) }
 | 
			
		||||
 | 
			
		||||
      before do
 | 
			
		||||
        get :show, params: { account_username: status.account.username, id: status.id, format: format }
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      it 'assigns @ancestors for [] if it is not a reply' do
 | 
			
		||||
        status = Fabricate(:status)
 | 
			
		||||
        get :show, params: { account_username: status.account.username, id: status.id }
 | 
			
		||||
        expect(assigns(:ancestors)).to eq []
 | 
			
		||||
      context 'as JSON' do
 | 
			
		||||
        let(:format) { 'json' }
 | 
			
		||||
 | 
			
		||||
        it 'returns http not found' do
 | 
			
		||||
          expect(response).to have_http_status(404)
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      it 'assigns @descendant_threads for a thread with several statuses' do
 | 
			
		||||
        status = Fabricate(:status)
 | 
			
		||||
        child = Fabricate(:status, in_reply_to_id: status.id)
 | 
			
		||||
        grandchild = Fabricate(:status, in_reply_to_id: child.id)
 | 
			
		||||
      context 'as HTML' do
 | 
			
		||||
        let(:format) { 'html' }
 | 
			
		||||
 | 
			
		||||
        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
 | 
			
		||||
 | 
			
		||||
        expect(assigns(:descendant_threads)[0][:statuses].pluck(:id)).to eq [child.id, grandchild.id]
 | 
			
		||||
    context 'when status is direct' do
 | 
			
		||||
      let(:status) { Fabricate(:status, account: account, visibility: :direct) }
 | 
			
		||||
 | 
			
		||||
      before do
 | 
			
		||||
        get :show, params: { account_username: status.account.username, id: status.id, format: format }
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      it 'assigns @descendant_threads for several threads sharing the same descendant' do
 | 
			
		||||
        status = Fabricate(:status)
 | 
			
		||||
        child = Fabricate(:status, in_reply_to_id: status.id)
 | 
			
		||||
        grandchildren = 2.times.map { Fabricate(:status, in_reply_to_id: child.id) }
 | 
			
		||||
      context 'as JSON' do
 | 
			
		||||
        let(:format) { 'json' }
 | 
			
		||||
 | 
			
		||||
        get :show, params: { account_username: status.account.username, id: status.id }
 | 
			
		||||
 | 
			
		||||
        expect(assigns(:descendant_threads)[0][:statuses].pluck(:id)).to eq [child.id, grandchildren[0].id]
 | 
			
		||||
        expect(assigns(:descendant_threads)[1][:statuses].pluck(:id)).to eq [grandchildren[1].id]
 | 
			
		||||
        it 'returns http not found' do
 | 
			
		||||
          expect(response).to have_http_status(404)
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      it 'assigns @max_descendant_thread_id for the last thread if it is hitting the status limit' do
 | 
			
		||||
        stub_const 'StatusControllerConcern::DESCENDANTS_LIMIT', 1
 | 
			
		||||
        status = Fabricate(:status)
 | 
			
		||||
        child = Fabricate(:status, in_reply_to_id: status.id)
 | 
			
		||||
      context 'as HTML' do
 | 
			
		||||
        let(:format) { 'html' }
 | 
			
		||||
 | 
			
		||||
        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
 | 
			
		||||
 | 
			
		||||
        expect(assigns(:descendant_threads)).to eq []
 | 
			
		||||
        expect(assigns(:max_descendant_thread_id)).to eq child.id
 | 
			
		||||
    context 'when signed-in' do
 | 
			
		||||
      let(:user) { Fabricate(:user) }
 | 
			
		||||
 | 
			
		||||
      before do
 | 
			
		||||
        sign_in(user)
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      it 'assigns @descendant_threads for threads with :next_status key if they are hitting the depth limit' do
 | 
			
		||||
        stub_const 'StatusControllerConcern::DESCENDANTS_DEPTH_LIMIT', 2
 | 
			
		||||
        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)
 | 
			
		||||
      context 'when account blocks user' do
 | 
			
		||||
        before do
 | 
			
		||||
          account.block!(user.account)
 | 
			
		||||
          get :show, params: { account_username: status.account.username, id: status.id }
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        get :show, params: { account_username: status.account.username, id: status.id }
 | 
			
		||||
 | 
			
		||||
        expect(assigns(:descendant_threads)[0][:statuses].pluck(:id)).not_to include child1.id
 | 
			
		||||
        expect(assigns(:descendant_threads)[1][:statuses].pluck(:id)).not_to include child2.id
 | 
			
		||||
        expect(assigns(:descendant_threads)[0][:next_status].id).to eq child1.id
 | 
			
		||||
        expect(assigns(:descendant_threads)[1][:next_status].id).to eq child2.id
 | 
			
		||||
        it 'returns http not found' do
 | 
			
		||||
          expect(response).to have_http_status(404)
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      it 'returns a success' do
 | 
			
		||||
        status = Fabricate(:status)
 | 
			
		||||
        get :show, params: { account_username: status.account.username, id: status.id }
 | 
			
		||||
      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
 | 
			
		||||
            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
 | 
			
		||||
 | 
			
		||||
            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: 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' }
 | 
			
		||||
 | 
			
		||||
            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
 | 
			
		||||
 | 
			
		||||
    context 'with signature' do
 | 
			
		||||
      let(:remote_account) { Fabricate(:account, domain: 'example.com') }
 | 
			
		||||
 | 
			
		||||
      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)
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      it 'renders statuses/show' do
 | 
			
		||||
        status = Fabricate(:status)
 | 
			
		||||
        get :show, params: { account_username: status.account.username, id: status.id }
 | 
			
		||||
        expect(response).to render_template 'statuses/show'
 | 
			
		||||
      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(: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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
Fabricator(:status_pin) do
 | 
			
		||||
  account
 | 
			
		||||
  status
 | 
			
		||||
  status { |attrs| Fabricate(:status, account: attrs[:account], visibility: :public) }
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -96,20 +96,16 @@ RSpec.describe Status, type: :model do
 | 
			
		|||
 | 
			
		||||
    context 'unless destroyed?' 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
 | 
			
		||||
          preview = subject.text.slice(0, 10).split("\n")[0]
 | 
			
		||||
          expect(subject.title).to(
 | 
			
		||||
            eq "#{account.acct} shared #{reblog.account.acct}'s: #{preview}"
 | 
			
		||||
          )
 | 
			
		||||
          expect(subject.title).to eq "#{account.acct} shared a status by #{reblog.account.acct}"
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      context 'unless reblog?' do
 | 
			
		||||
        it 'returns "#{account.acct}: #{preview}"' do
 | 
			
		||||
        it 'returns "New status by #{account.acct}"' do
 | 
			
		||||
          subject.reblog = nil
 | 
			
		||||
          preview = subject.text.slice(0, 20).split("\n")[0]
 | 
			
		||||
          expect(subject.title).to eq "#{account.acct}: #{preview}"
 | 
			
		||||
          expect(subject.title).to eq "New status by #{account.acct}"
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,7 +21,11 @@ RSpec.describe FetchResourceService, type: :service do
 | 
			
		|||
 | 
			
		||||
    context 'when OpenSSL::SSL::SSLError is raised' 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
 | 
			
		||||
 | 
			
		||||
      it { is_expected.to be_nil }
 | 
			
		||||
| 
						 | 
				
			
			@ -29,7 +33,11 @@ RSpec.describe FetchResourceService, type: :service do
 | 
			
		|||
 | 
			
		||||
    context 'when HTTP::ConnectionError is raised' 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
 | 
			
		||||
 | 
			
		||||
      it { is_expected.to be_nil }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										367
									
								
								yarn.lock
								
								
								
								
							
							
						
						
									
										367
									
								
								yarn.lock
								
								
								
								
							| 
						 | 
				
			
			@ -1327,9 +1327,9 @@
 | 
			
		|||
  integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
 | 
			
		||||
 | 
			
		||||
"@types/q@^1.5.1":
 | 
			
		||||
  version "1.5.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.1.tgz#48fd98c1561fe718b61733daed46ff115b496e18"
 | 
			
		||||
  integrity sha512-eqz8c/0kwNi/OEHQfvIuJVLTst3in0e7uTKeuY+WL/zfKn0xVujOTp42bS/vUUokhK5P2BppLd9JXMOMHcgbjA==
 | 
			
		||||
  version "1.5.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8"
 | 
			
		||||
  integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==
 | 
			
		||||
 | 
			
		||||
"@types/react@16.4.6":
 | 
			
		||||
  version "16.4.6"
 | 
			
		||||
| 
						 | 
				
			
			@ -1632,7 +1632,7 @@ ajv@^4.7.0:
 | 
			
		|||
    co "^4.6.0"
 | 
			
		||||
    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"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52"
 | 
			
		||||
  integrity sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==
 | 
			
		||||
| 
						 | 
				
			
			@ -1652,6 +1652,16 @@ ajv@^6.12.0:
 | 
			
		|||
    json-schema-traverse "^0.4.1"
 | 
			
		||||
    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:
 | 
			
		||||
  version "1.0.2"
 | 
			
		||||
  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=
 | 
			
		||||
 | 
			
		||||
aws4@^1.8.0:
 | 
			
		||||
  version "1.8.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f"
 | 
			
		||||
  integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==
 | 
			
		||||
  version "1.9.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e"
 | 
			
		||||
  integrity sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==
 | 
			
		||||
 | 
			
		||||
axios@^0.18.0:
 | 
			
		||||
  version "0.18.1"
 | 
			
		||||
| 
						 | 
				
			
			@ -2249,9 +2259,9 @@ bindings@^1.5.0:
 | 
			
		|||
    file-uri-to-path "1.0.0"
 | 
			
		||||
 | 
			
		||||
bluebird@^3.5.5:
 | 
			
		||||
  version "3.5.5"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.5.tgz#a8d0afd73251effbbd5fe384a77d73003c17a71f"
 | 
			
		||||
  integrity sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==
 | 
			
		||||
  version "3.7.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
 | 
			
		||||
  integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
 | 
			
		||||
 | 
			
		||||
blurhash@^1.1.3:
 | 
			
		||||
  version "1.1.3"
 | 
			
		||||
| 
						 | 
				
			
			@ -2461,11 +2471,6 @@ buffer@^4.3.0:
 | 
			
		|||
    ieee754 "^1.1.4"
 | 
			
		||||
    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:
 | 
			
		||||
  version "3.0.0"
 | 
			
		||||
  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==
 | 
			
		||||
 | 
			
		||||
cacache@^12.0.2, cacache@^12.0.3:
 | 
			
		||||
  version "12.0.3"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.3.tgz#be99abba4e1bf5df461cd5a2c1071fc432573390"
 | 
			
		||||
  integrity sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==
 | 
			
		||||
  version "12.0.4"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.4.tgz#668bcbd105aeb5f1d92fe25570ec9525c8faa40c"
 | 
			
		||||
  integrity sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    bluebird "^3.5.5"
 | 
			
		||||
    chownr "^1.1.1"
 | 
			
		||||
| 
						 | 
				
			
			@ -2703,16 +2708,11 @@ chokidar@^2.1.8:
 | 
			
		|||
  optionalDependencies:
 | 
			
		||||
    fsevents "^1.2.7"
 | 
			
		||||
 | 
			
		||||
chownr@^1.1.1:
 | 
			
		||||
chownr@^1.1.1, chownr@^1.1.2:
 | 
			
		||||
  version "1.1.4"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b"
 | 
			
		||||
  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:
 | 
			
		||||
  version "1.0.2"
 | 
			
		||||
  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"
 | 
			
		||||
  integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=
 | 
			
		||||
 | 
			
		||||
coa@~2.0.1:
 | 
			
		||||
coa@^2.0.2:
 | 
			
		||||
  version "2.0.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.2.tgz#43f6c21151b4ef2bf57187db0d73de229e3e7ec3"
 | 
			
		||||
  integrity sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==
 | 
			
		||||
| 
						 | 
				
			
			@ -2880,15 +2880,10 @@ color@^3.0.0:
 | 
			
		|||
    color-convert "^1.9.1"
 | 
			
		||||
    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:
 | 
			
		||||
  version "1.0.7"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.7.tgz#2d1d24317afb8abe95d6d2c0b07b57813539d828"
 | 
			
		||||
  integrity sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==
 | 
			
		||||
  version "1.0.8"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
 | 
			
		||||
  integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    delayed-stream "~1.0.0"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -3052,9 +3047,9 @@ core-js-compat@^3.6.2:
 | 
			
		|||
    semver "7.0.0"
 | 
			
		||||
 | 
			
		||||
core-js-pure@^3.0.0:
 | 
			
		||||
  version "3.6.4"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.4.tgz#4bf1ba866e25814f149d4e9aaa08c36173506e3a"
 | 
			
		||||
  integrity sha512-epIhRLkXdgv32xIUFaaAry2wdxZYBi6bgM7cB136dzzXXa+dFyRLTZeLUJxnd8ShrmyVXBub63n2NHo2JAt8Cw==
 | 
			
		||||
  version "3.6.5"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.5.tgz#c79e75f5e38dbc85a662d91eea52b8256d53b813"
 | 
			
		||||
  integrity sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA==
 | 
			
		||||
 | 
			
		||||
core-js@^2.4.0:
 | 
			
		||||
  version "2.6.1"
 | 
			
		||||
| 
						 | 
				
			
			@ -3235,18 +3230,18 @@ css-loader@^3.4.2:
 | 
			
		|||
    postcss-value-parser "^4.0.2"
 | 
			
		||||
    schema-utils "^2.6.0"
 | 
			
		||||
 | 
			
		||||
css-select-base-adapter@~0.1.0:
 | 
			
		||||
css-select-base-adapter@^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"
 | 
			
		||||
  integrity sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==
 | 
			
		||||
 | 
			
		||||
css-select@^2.0.0:
 | 
			
		||||
  version "2.0.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/css-select/-/css-select-2.0.2.tgz#ab4386cec9e1f668855564b17c3733b43b2a5ede"
 | 
			
		||||
  integrity sha512-dSpYaDVoWaELjvZ3mS6IKZM/y2PMPa/XYoEfYNZePL4U/XgyxZNroHEHReDx/d+VgXh9VbCTtFqLkFbmeqeaRQ==
 | 
			
		||||
  version "2.1.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/css-select/-/css-select-2.1.0.tgz#6a34653356635934a81baca68d0255432105dbef"
 | 
			
		||||
  integrity sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    boolbase "^1.0.0"
 | 
			
		||||
    css-what "^2.1.2"
 | 
			
		||||
    css-what "^3.2.1"
 | 
			
		||||
    domutils "^1.7.0"
 | 
			
		||||
    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"
 | 
			
		||||
  integrity sha1-hcbwhquk6zLFcaMIav/ENLhII+0=
 | 
			
		||||
 | 
			
		||||
css-tree@1.0.0-alpha.28:
 | 
			
		||||
  version "1.0.0-alpha.28"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.28.tgz#8e8968190d886c9477bc8d61e96f61af3f7ffa7f"
 | 
			
		||||
  integrity sha512-joNNW1gCp3qFFzj4St6zk+Wh/NBv0vM5YbEreZk0SD4S23S+1xBKb6cLDg2uj4P4k/GUMlIm6cKIDqIG+vdt0w==
 | 
			
		||||
css-tree@1.0.0-alpha.37:
 | 
			
		||||
  version "1.0.0-alpha.37"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.37.tgz#98bebd62c4c1d9f960ec340cf9f7522e30709a22"
 | 
			
		||||
  integrity sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    mdn-data "~1.1.0"
 | 
			
		||||
    source-map "^0.5.3"
 | 
			
		||||
    mdn-data "2.0.4"
 | 
			
		||||
    source-map "^0.6.1"
 | 
			
		||||
 | 
			
		||||
css-tree@1.0.0-alpha.29:
 | 
			
		||||
  version "1.0.0-alpha.29"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.29.tgz#3fa9d4ef3142cbd1c301e7664c1f352bd82f5a39"
 | 
			
		||||
  integrity sha512-sRNb1XydwkW9IOci6iB2xmy8IGCj6r/fr+JWitvJ2JxQRPzN3T4AGGVWCMlVmVwM1gtgALJRmGIlWv5ppnGGkg==
 | 
			
		||||
css-tree@1.0.0-alpha.39:
 | 
			
		||||
  version "1.0.0-alpha.39"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.39.tgz#2bff3ffe1bb3f776cf7eefd91ee5cba77a149eeb"
 | 
			
		||||
  integrity sha512-7UvkEYgBAHRG9Nt980lYxjsTrCyHFN53ky3wVsDkiMdVqylqRt+Zc+jm5qw7/qyOvN2dHSYtX0e4MbCCExSvnA==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    mdn-data "~1.1.0"
 | 
			
		||||
    source-map "^0.5.3"
 | 
			
		||||
    mdn-data "2.0.6"
 | 
			
		||||
    source-map "^0.6.1"
 | 
			
		||||
 | 
			
		||||
css-unit-converter@^1.1.1:
 | 
			
		||||
  version "1.1.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/css-unit-converter/-/css-unit-converter-1.1.1.tgz#d9b9281adcfd8ced935bdbaba83786897f64e996"
 | 
			
		||||
  integrity sha1-2bkoGtz9jO2TW9urqDeGiX9k6ZY=
 | 
			
		||||
 | 
			
		||||
css-url-regex@^1.1.0:
 | 
			
		||||
  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:
 | 
			
		||||
css-what@2.1:
 | 
			
		||||
  version "2.1.3"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2"
 | 
			
		||||
  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:
 | 
			
		||||
  version "2.0.0"
 | 
			
		||||
  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"
 | 
			
		||||
    postcss "^7.0.0"
 | 
			
		||||
 | 
			
		||||
csso@^3.5.0:
 | 
			
		||||
  version "3.5.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/csso/-/csso-3.5.1.tgz#7b9eb8be61628973c1b261e169d2f024008e758b"
 | 
			
		||||
  integrity sha512-vrqULLffYU1Q2tLdJvaCYbONStnfkfimRxXNaGjxMldI0C7JPBC4rB1RyjhfdZ4m1frm8pM9uRPKH3d2knZ8gg==
 | 
			
		||||
csso@^4.0.2:
 | 
			
		||||
  version "4.0.3"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/csso/-/csso-4.0.3.tgz#0d9985dc852c7cc2b2cacfbbe1079014d1a8e903"
 | 
			
		||||
  integrity sha512-NL3spysxUkcrOgnpsT4Xdl2aiEiBG6bXswAABQVHcMrfjjBisFOKwLDOmf4wf32aPdcJws1zds2B0Rg+jqMyHQ==
 | 
			
		||||
  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":
 | 
			
		||||
  version "0.3.8"
 | 
			
		||||
| 
						 | 
				
			
			@ -3961,7 +3956,7 @@ error-stack-parser@^2.0.6:
 | 
			
		|||
  dependencies:
 | 
			
		||||
    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"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.5.tgz#d8c9d1d66c8981fb9200e2251d799eee92774ae9"
 | 
			
		||||
  integrity sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==
 | 
			
		||||
| 
						 | 
				
			
			@ -4524,9 +4519,9 @@ fast-deep-equal@^3.1.1:
 | 
			
		|||
  integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==
 | 
			
		||||
 | 
			
		||||
fast-json-stable-stringify@^2.0.0:
 | 
			
		||||
  version "2.0.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2"
 | 
			
		||||
  integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I=
 | 
			
		||||
  version "2.1.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
 | 
			
		||||
  integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
 | 
			
		||||
 | 
			
		||||
fast-levenshtein@~2.0.6:
 | 
			
		||||
  version "2.0.6"
 | 
			
		||||
| 
						 | 
				
			
			@ -4560,9 +4555,9 @@ fb-watchman@^2.0.0:
 | 
			
		|||
    bser "^2.0.0"
 | 
			
		||||
 | 
			
		||||
figgy-pudding@^3.5.1:
 | 
			
		||||
  version "3.5.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790"
 | 
			
		||||
  integrity sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==
 | 
			
		||||
  version "3.5.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e"
 | 
			
		||||
  integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==
 | 
			
		||||
 | 
			
		||||
figures@^1.3.5:
 | 
			
		||||
  version "1.7.0"
 | 
			
		||||
| 
						 | 
				
			
			@ -4722,9 +4717,9 @@ flat-cache@^2.0.1:
 | 
			
		|||
    write "1.0.3"
 | 
			
		||||
 | 
			
		||||
flatted@^2.0.0:
 | 
			
		||||
  version "2.0.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.0.tgz#55122b6536ea496b4b44893ee2608141d10d9916"
 | 
			
		||||
  integrity sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg==
 | 
			
		||||
  version "2.0.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138"
 | 
			
		||||
  integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==
 | 
			
		||||
 | 
			
		||||
flush-write-stream@^1.0.0:
 | 
			
		||||
  version "1.1.1"
 | 
			
		||||
| 
						 | 
				
			
			@ -4890,9 +4885,9 @@ functional-red-black-tree@^1.0.1:
 | 
			
		|||
  integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
 | 
			
		||||
 | 
			
		||||
functions-have-names@^1.2.0:
 | 
			
		||||
  version "1.2.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.0.tgz#83da7583e4ea0c9ac5ff530f73394b033e0bf77d"
 | 
			
		||||
  integrity sha512-zKXyzksTeaCSw5wIX79iCA40YAa6CJMJgNg9wdkU/ERBrIdPSimPICYiLp65lRbSBqtiHql/HZfS2DyI/AH6tQ==
 | 
			
		||||
  version "1.2.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.1.tgz#a981ac397fa0c9964551402cdc5533d7a4d52f91"
 | 
			
		||||
  integrity sha512-j48B/ZI7VKs3sgeI2cZp7WXWmZXu7Iq5pl5/vptV5N2mq+DGFuS/ulaDjtaoLpYzuD6u8UgrUKHfgo7fDTSiBA==
 | 
			
		||||
 | 
			
		||||
gauge@~2.7.3:
 | 
			
		||||
  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"
 | 
			
		||||
  integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=
 | 
			
		||||
 | 
			
		||||
har-validator@~5.1.0:
 | 
			
		||||
har-validator@~5.1.3:
 | 
			
		||||
  version "5.1.3"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080"
 | 
			
		||||
  integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==
 | 
			
		||||
| 
						 | 
				
			
			@ -5275,9 +5270,9 @@ hoopy@^0.1.4:
 | 
			
		|||
  integrity sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==
 | 
			
		||||
 | 
			
		||||
hosted-git-info@^2.1.4:
 | 
			
		||||
  version "2.7.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047"
 | 
			
		||||
  integrity sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==
 | 
			
		||||
  version "2.8.8"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488"
 | 
			
		||||
  integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==
 | 
			
		||||
 | 
			
		||||
hpack.js@^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"
 | 
			
		||||
  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:
 | 
			
		||||
  version "1.1.5"
 | 
			
		||||
  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"
 | 
			
		||||
  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"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847"
 | 
			
		||||
  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==
 | 
			
		||||
 | 
			
		||||
loglevel@^1.6.6:
 | 
			
		||||
  version "1.6.6"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.6.tgz#0ee6300cc058db6b3551fa1c4bf73b83bb771312"
 | 
			
		||||
  integrity sha512-Sgr5lbboAUBo3eXCSPL4/KoVz3ROKquOjcctxmHIt+vol2DrqTQe3SwkKKuYhEiWB5kYa13YyopJ69deJ1irzQ==
 | 
			
		||||
  version "1.6.8"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.8.tgz#8a25fb75d092230ecd4457270d80b54e28011171"
 | 
			
		||||
  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:
 | 
			
		||||
  version "1.4.0"
 | 
			
		||||
| 
						 | 
				
			
			@ -7013,10 +7001,15 @@ md5.js@^1.3.4:
 | 
			
		|||
    inherits "^2.0.1"
 | 
			
		||||
    safe-buffer "^5.1.2"
 | 
			
		||||
 | 
			
		||||
mdn-data@~1.1.0:
 | 
			
		||||
  version "1.1.4"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-1.1.4.tgz#50b5d4ffc4575276573c4eedb8780812a8419f01"
 | 
			
		||||
  integrity sha512-FSYbp3lyKjyj3E7fMl6rYvUdX0FBXaluGqlFoYESWQlyUTq8R+wp0rkFxoYFqZlHCvsUXGjyJmLQSnXToYhOSA==
 | 
			
		||||
mdn-data@2.0.4:
 | 
			
		||||
  version "2.0.4"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b"
 | 
			
		||||
  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:
 | 
			
		||||
  version "0.3.0"
 | 
			
		||||
| 
						 | 
				
			
			@ -7100,34 +7093,22 @@ miller-rabin@^4.0.0:
 | 
			
		|||
    bn.js "^4.0.0"
 | 
			
		||||
    brorand "^1.0.1"
 | 
			
		||||
 | 
			
		||||
mime-db@1.40.0:
 | 
			
		||||
  version "1.40.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32"
 | 
			
		||||
  integrity sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==
 | 
			
		||||
mime-db@1.44.0:
 | 
			
		||||
  version "1.44.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92"
 | 
			
		||||
  integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==
 | 
			
		||||
 | 
			
		||||
"mime-db@>= 1.40.0 < 2":
 | 
			
		||||
  version "1.42.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.42.0.tgz#3e252907b4c7adb906597b4b65636272cf9e7bac"
 | 
			
		||||
  integrity sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ==
 | 
			
		||||
 | 
			
		||||
mime-db@~1.37.0:
 | 
			
		||||
  version "1.37.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.37.0.tgz#0b6a0ce6fdbe9576e25f1f2d2fde8830dc0ad0d8"
 | 
			
		||||
  integrity sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==
 | 
			
		||||
 | 
			
		||||
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==
 | 
			
		||||
mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24:
 | 
			
		||||
  version "2.1.27"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f"
 | 
			
		||||
  integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    mime-db "~1.37.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-db "1.44.0"
 | 
			
		||||
 | 
			
		||||
mime@1.6.0:
 | 
			
		||||
  version "1.6.0"
 | 
			
		||||
| 
						 | 
				
			
			@ -7253,14 +7234,7 @@ mixin-deep@^1.2.0:
 | 
			
		|||
    for-in "^1.0.2"
 | 
			
		||||
    is-extendable "^1.0.1"
 | 
			
		||||
 | 
			
		||||
mkdirp@^0.5, 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:
 | 
			
		||||
mkdirp@^0.5, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@~0.5.1:
 | 
			
		||||
  version "0.5.5"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
 | 
			
		||||
  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"
 | 
			
		||||
  integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
 | 
			
		||||
 | 
			
		||||
moo@^0.4.3:
 | 
			
		||||
  version "0.4.3"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/moo/-/moo-0.4.3.tgz#3f847a26f31cf625a956a87f2b10fbc013bfd10e"
 | 
			
		||||
  integrity sha512-gFD2xGCl8YFgGHsqJ9NKRVdwlioeW3mI1iqfLNYQOv0+6JRwG58Zk9DIGQgyIaffSYaO1xsKnMaYzzNr1KyIAw==
 | 
			
		||||
moo@^0.5.0:
 | 
			
		||||
  version "0.5.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/moo/-/moo-0.5.1.tgz#7aae7f384b9b09f620b6abf6f74ebbcd1b65dbc4"
 | 
			
		||||
  integrity sha512-I1mnb5xn4fO80BH9BLcF0yLypy2UKl+Cb01Fu0hJRkJjlCRtxZMWkTdAtDd5ZqCOxtCkhmRwyI57vWT+1iZ67w==
 | 
			
		||||
 | 
			
		||||
mousetrap@^1.5.2:
 | 
			
		||||
  version "1.6.5"
 | 
			
		||||
| 
						 | 
				
			
			@ -7360,12 +7334,12 @@ natural-compare@^1.4.0:
 | 
			
		|||
  integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
 | 
			
		||||
 | 
			
		||||
nearley@^2.7.10:
 | 
			
		||||
  version "2.16.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/nearley/-/nearley-2.16.0.tgz#77c297d041941d268290ec84b739d0ee297e83a7"
 | 
			
		||||
  integrity sha512-Tr9XD3Vt/EujXbZBv6UAHYoLUSMQAxSsTnm9K3koXzjzNWY195NqALeyrzLZBKzAkL3gl92BcSogqrHjD8QuUg==
 | 
			
		||||
  version "2.19.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/nearley/-/nearley-2.19.2.tgz#40cafbf235121ae94b1aa1e585890d24fade182d"
 | 
			
		||||
  integrity sha512-h6lygT0BWAGErDvoE2LfI+tDeY2+UUrqG5dcBPdCmjnjud9z1wE0P7ljb85iNbE93YA+xJLpoSYGMuUqhnSSSA==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    commander "^2.19.0"
 | 
			
		||||
    moo "^0.4.3"
 | 
			
		||||
    moo "^0.5.0"
 | 
			
		||||
    railroad-diagrams "^1.0.0"
 | 
			
		||||
    randexp "0.4.6"
 | 
			
		||||
    semver "^5.4.1"
 | 
			
		||||
| 
						 | 
				
			
			@ -7484,12 +7458,12 @@ nopt@^4.0.1:
 | 
			
		|||
    osenv "^0.1.4"
 | 
			
		||||
 | 
			
		||||
normalize-package-data@^2.3.2:
 | 
			
		||||
  version "2.4.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f"
 | 
			
		||||
  integrity sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==
 | 
			
		||||
  version "2.5.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
 | 
			
		||||
  integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    hosted-git-info "^2.1.4"
 | 
			
		||||
    is-builtin-module "^1.0.0"
 | 
			
		||||
    resolve "^1.10.0"
 | 
			
		||||
    semver "2 || 3 || 4 || 5"
 | 
			
		||||
    validate-npm-package-license "^3.0.1"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -7666,13 +7640,13 @@ object.fromentries@^2.0.2:
 | 
			
		|||
    function-bind "^1.1.1"
 | 
			
		||||
    has "^1.0.3"
 | 
			
		||||
 | 
			
		||||
object.getownpropertydescriptors@^2.0.3:
 | 
			
		||||
  version "2.0.3"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16"
 | 
			
		||||
  integrity sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=
 | 
			
		||||
object.getownpropertydescriptors@^2.1.0:
 | 
			
		||||
  version "2.1.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz#369bf1f9592d8ab89d712dced5cb81c7c5352649"
 | 
			
		||||
  integrity sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    define-properties "^1.1.2"
 | 
			
		||||
    es-abstract "^1.5.1"
 | 
			
		||||
    define-properties "^1.1.3"
 | 
			
		||||
    es-abstract "^1.17.0-next.1"
 | 
			
		||||
 | 
			
		||||
object.pick@^1.3.0:
 | 
			
		||||
  version "1.3.0"
 | 
			
		||||
| 
						 | 
				
			
			@ -7681,7 +7655,7 @@ object.pick@^1.3.0:
 | 
			
		|||
  dependencies:
 | 
			
		||||
    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"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.1.tgz#68a99ecde356b7e9295a3c5e0ce31dc8c953de5e"
 | 
			
		||||
  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"
 | 
			
		||||
  integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY=
 | 
			
		||||
 | 
			
		||||
psl@^1.1.24, psl@^1.1.28:
 | 
			
		||||
  version "1.1.31"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.31.tgz#e9aa86d0101b5b105cbe93ac6b784cd547276184"
 | 
			
		||||
  integrity sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==
 | 
			
		||||
psl@^1.1.28:
 | 
			
		||||
  version "1.8.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24"
 | 
			
		||||
  integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==
 | 
			
		||||
 | 
			
		||||
public-encrypt@^4.0.0:
 | 
			
		||||
  version "4.0.3"
 | 
			
		||||
| 
						 | 
				
			
			@ -8792,7 +8766,7 @@ punycode@1.3.2:
 | 
			
		|||
  resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
 | 
			
		||||
  integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=
 | 
			
		||||
 | 
			
		||||
punycode@^1.2.4, punycode@^1.4.1:
 | 
			
		||||
punycode@^1.2.4:
 | 
			
		||||
  version "1.4.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
 | 
			
		||||
  integrity sha1-wNWmOycYgArY4esPpSachN1BhF4=
 | 
			
		||||
| 
						 | 
				
			
			@ -9223,10 +9197,10 @@ read-pkg@^3.0.0:
 | 
			
		|||
    normalize-package-data "^2.3.2"
 | 
			
		||||
    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:
 | 
			
		||||
  version "2.3.6"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf"
 | 
			
		||||
  integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==
 | 
			
		||||
"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.7"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
 | 
			
		||||
  integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    core-util-is "~1.0.0"
 | 
			
		||||
    inherits "~2.0.3"
 | 
			
		||||
| 
						 | 
				
			
			@ -9236,10 +9210,10 @@ read-pkg@^3.0.0:
 | 
			
		|||
    string_decoder "~1.1.1"
 | 
			
		||||
    util-deprecate "~1.0.1"
 | 
			
		||||
 | 
			
		||||
readable-stream@^2.0.2, readable-stream@^2.0.6:
 | 
			
		||||
  version "2.3.7"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
 | 
			
		||||
  integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
 | 
			
		||||
readable-stream@^2.0.1, readable-stream@^2.3.3:
 | 
			
		||||
  version "2.3.6"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf"
 | 
			
		||||
  integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    core-util-is "~1.0.0"
 | 
			
		||||
    inherits "~2.0.3"
 | 
			
		||||
| 
						 | 
				
			
			@ -9493,9 +9467,9 @@ request-promise-native@^1.0.5:
 | 
			
		|||
    tough-cookie ">=2.3.3"
 | 
			
		||||
 | 
			
		||||
request@^2.87.0:
 | 
			
		||||
  version "2.88.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef"
 | 
			
		||||
  integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==
 | 
			
		||||
  version "2.88.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3"
 | 
			
		||||
  integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    aws-sign2 "~0.7.0"
 | 
			
		||||
    aws4 "^1.8.0"
 | 
			
		||||
| 
						 | 
				
			
			@ -9504,7 +9478,7 @@ request@^2.87.0:
 | 
			
		|||
    extend "~3.0.2"
 | 
			
		||||
    forever-agent "~0.6.1"
 | 
			
		||||
    form-data "~2.3.2"
 | 
			
		||||
    har-validator "~5.1.0"
 | 
			
		||||
    har-validator "~5.1.3"
 | 
			
		||||
    http-signature "~1.2.0"
 | 
			
		||||
    is-typedarray "~1.0.0"
 | 
			
		||||
    isstream "~0.1.2"
 | 
			
		||||
| 
						 | 
				
			
			@ -9514,7 +9488,7 @@ request@^2.87.0:
 | 
			
		|||
    performance-now "^2.1.0"
 | 
			
		||||
    qs "~6.5.2"
 | 
			
		||||
    safe-buffer "^5.1.2"
 | 
			
		||||
    tough-cookie "~2.4.3"
 | 
			
		||||
    tough-cookie "~2.5.0"
 | 
			
		||||
    tunnel-agent "^0.6.0"
 | 
			
		||||
    uuid "^3.3.2"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -9621,10 +9595,10 @@ resolve@1.1.7:
 | 
			
		|||
  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
 | 
			
		||||
  integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=
 | 
			
		||||
 | 
			
		||||
resolve@^1.12.0, resolve@^1.15.1, resolve@^1.3.2, resolve@^1.5.0, resolve@^1.8.1:
 | 
			
		||||
  version "1.15.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.15.1.tgz#27bdcdeffeaf2d6244b95bb0f9f4b4653451f3e8"
 | 
			
		||||
  integrity sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==
 | 
			
		||||
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.17.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444"
 | 
			
		||||
  integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    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"
 | 
			
		||||
  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"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
 | 
			
		||||
  integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
 | 
			
		||||
| 
						 | 
				
			
			@ -10187,9 +10161,9 @@ spdx-correct@^3.0.0:
 | 
			
		|||
    spdx-license-ids "^3.0.0"
 | 
			
		||||
 | 
			
		||||
spdx-exceptions@^2.1.0:
 | 
			
		||||
  version "2.2.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz#2ea450aee74f2a89bfb94519c07fcd6f41322977"
 | 
			
		||||
  integrity sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==
 | 
			
		||||
  version "2.3.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d"
 | 
			
		||||
  integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==
 | 
			
		||||
 | 
			
		||||
spdx-expression-parse@^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:
 | 
			
		||||
  version "3.0.3"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.3.tgz#81c0ce8f21474756148bbb5f3bfc0f36bf15d76e"
 | 
			
		||||
  integrity sha512-uBIcIl3Ih6Phe3XHK1NqboJLdGfwr1UN3k6wSD1dZpmPsIkb8AGNbZYJ1fOBk834+Gxy8rpfDxrS6XLEMZMY2g==
 | 
			
		||||
  version "3.0.5"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz#3694b5804567a458d3c8045842a6358632f62654"
 | 
			
		||||
  integrity sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==
 | 
			
		||||
 | 
			
		||||
spdy-transport@^3.0.0:
 | 
			
		||||
  version "3.0.0"
 | 
			
		||||
| 
						 | 
				
			
			@ -10276,7 +10250,7 @@ ssri@^7.0.0:
 | 
			
		|||
    figgy-pudding "^3.5.1"
 | 
			
		||||
    minipass "^3.1.1"
 | 
			
		||||
 | 
			
		||||
stable@~0.1.6:
 | 
			
		||||
stable@^0.1.8:
 | 
			
		||||
  version "0.1.8"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf"
 | 
			
		||||
  integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==
 | 
			
		||||
| 
						 | 
				
			
			@ -10590,22 +10564,21 @@ supports-color@^7.0.0, supports-color@^7.1.0:
 | 
			
		|||
    has-flag "^4.0.0"
 | 
			
		||||
 | 
			
		||||
svgo@^1.0.0:
 | 
			
		||||
  version "1.1.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.1.1.tgz#12384b03335bcecd85cfa5f4e3375fed671cb985"
 | 
			
		||||
  integrity sha512-GBkJbnTuFpM4jFbiERHDWhZc/S/kpHToqmZag3aEBjPYK44JAN2QBjvrGIxLOoCyMZjuFQIfTO2eJd8uwLY/9g==
 | 
			
		||||
  version "1.3.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.3.2.tgz#b6dc511c063346c9e415b81e43401145b96d4167"
 | 
			
		||||
  integrity sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    coa "~2.0.1"
 | 
			
		||||
    colors "~1.1.2"
 | 
			
		||||
    chalk "^2.4.1"
 | 
			
		||||
    coa "^2.0.2"
 | 
			
		||||
    css-select "^2.0.0"
 | 
			
		||||
    css-select-base-adapter "~0.1.0"
 | 
			
		||||
    css-tree "1.0.0-alpha.28"
 | 
			
		||||
    css-url-regex "^1.1.0"
 | 
			
		||||
    csso "^3.5.0"
 | 
			
		||||
    js-yaml "^3.12.0"
 | 
			
		||||
    css-select-base-adapter "^0.1.1"
 | 
			
		||||
    css-tree "1.0.0-alpha.37"
 | 
			
		||||
    csso "^4.0.2"
 | 
			
		||||
    js-yaml "^3.13.1"
 | 
			
		||||
    mkdirp "~0.5.1"
 | 
			
		||||
    object.values "^1.0.4"
 | 
			
		||||
    object.values "^1.1.0"
 | 
			
		||||
    sax "~1.2.4"
 | 
			
		||||
    stable "~0.1.6"
 | 
			
		||||
    stable "^0.1.8"
 | 
			
		||||
    unquote "~1.1.1"
 | 
			
		||||
    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"
 | 
			
		||||
  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"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"
 | 
			
		||||
  integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==
 | 
			
		||||
| 
						 | 
				
			
			@ -10881,14 +10854,6 @@ tough-cookie@>=2.3.3, tough-cookie@^2.3.4:
 | 
			
		|||
    psl "^1.1.28"
 | 
			
		||||
    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:
 | 
			
		||||
  version "1.0.1"
 | 
			
		||||
  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=
 | 
			
		||||
 | 
			
		||||
util.promisify@^1.0.0, util.promisify@~1.0.0:
 | 
			
		||||
  version "1.0.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030"
 | 
			
		||||
  integrity sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==
 | 
			
		||||
  version "1.0.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.1.tgz#6baf7774b80eeb0f7520d8b81d07982a59abbaee"
 | 
			
		||||
  integrity sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    define-properties "^1.1.2"
 | 
			
		||||
    object.getownpropertydescriptors "^2.0.3"
 | 
			
		||||
    define-properties "^1.1.3"
 | 
			
		||||
    es-abstract "^1.17.2"
 | 
			
		||||
    has-symbols "^1.0.1"
 | 
			
		||||
    object.getownpropertydescriptors "^2.1.0"
 | 
			
		||||
 | 
			
		||||
util@0.10.3:
 | 
			
		||||
  version "0.10.3"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue