Merge tag 'v2.7.0rc1' into instance_only_statuses
This commit is contained in:
		@ -3,7 +3,7 @@ version: 2
 | 
			
		||||
aliases:
 | 
			
		||||
  - &defaults
 | 
			
		||||
    docker:
 | 
			
		||||
      - image: circleci/ruby:2.5.1-stretch-node
 | 
			
		||||
      - image: circleci/ruby:2.6.0-stretch-node
 | 
			
		||||
        environment: &ruby_environment
 | 
			
		||||
          BUNDLE_APP_CONFIG: ./.bundle/
 | 
			
		||||
          DB_HOST: localhost
 | 
			
		||||
@ -98,21 +98,21 @@ jobs:
 | 
			
		||||
    <<: *defaults
 | 
			
		||||
    <<: *install_steps
 | 
			
		||||
 | 
			
		||||
  install-ruby2.6:
 | 
			
		||||
    <<: *defaults
 | 
			
		||||
    <<: *install_ruby_dependencies
 | 
			
		||||
 | 
			
		||||
  install-ruby2.5:
 | 
			
		||||
    <<: *defaults
 | 
			
		||||
    docker:
 | 
			
		||||
      - image: circleci/ruby:2.5.3-stretch-node
 | 
			
		||||
        environment: *ruby_environment
 | 
			
		||||
    <<: *install_ruby_dependencies
 | 
			
		||||
 | 
			
		||||
  install-ruby2.4:
 | 
			
		||||
    <<: *defaults
 | 
			
		||||
    docker:
 | 
			
		||||
      - image: circleci/ruby:2.4.4-stretch-node
 | 
			
		||||
        environment: *ruby_environment
 | 
			
		||||
    <<: *install_ruby_dependencies
 | 
			
		||||
 | 
			
		||||
  install-ruby2.3:
 | 
			
		||||
    <<: *defaults
 | 
			
		||||
    docker:
 | 
			
		||||
      - image: circleci/ruby:2.3.7-stretch-node
 | 
			
		||||
      - image: circleci/ruby:2.4.5-stretch-node
 | 
			
		||||
        environment: *ruby_environment
 | 
			
		||||
    <<: *install_ruby_dependencies
 | 
			
		||||
 | 
			
		||||
@ -128,43 +128,43 @@ jobs:
 | 
			
		||||
              - ./mastodon/public/assets
 | 
			
		||||
              - ./mastodon/public/packs-test/
 | 
			
		||||
 | 
			
		||||
  test-ruby2.6:
 | 
			
		||||
    <<: *defaults
 | 
			
		||||
    docker:
 | 
			
		||||
      - image: circleci/ruby:2.6.0-stretch-node
 | 
			
		||||
        environment: *ruby_environment
 | 
			
		||||
      - image: circleci/postgres:10.6-alpine
 | 
			
		||||
        environment:
 | 
			
		||||
          POSTGRES_USER: root
 | 
			
		||||
      - image: circleci/redis:5.0.3-alpine3.8
 | 
			
		||||
    <<: *test_steps
 | 
			
		||||
 | 
			
		||||
  test-ruby2.5:
 | 
			
		||||
    <<: *defaults
 | 
			
		||||
    docker:
 | 
			
		||||
      - image: circleci/ruby:2.5.1-stretch-node
 | 
			
		||||
      - image: circleci/ruby:2.5.3-stretch-node
 | 
			
		||||
        environment: *ruby_environment
 | 
			
		||||
      - image: circleci/postgres:10.3-alpine
 | 
			
		||||
      - image: circleci/postgres:10.6-alpine
 | 
			
		||||
        environment:
 | 
			
		||||
          POSTGRES_USER: root
 | 
			
		||||
      - image: circleci/redis:4.0.9-alpine
 | 
			
		||||
      - image: circleci/redis:4.0.12-alpine
 | 
			
		||||
    <<: *test_steps
 | 
			
		||||
 | 
			
		||||
  test-ruby2.4:
 | 
			
		||||
    <<: *defaults
 | 
			
		||||
    docker:
 | 
			
		||||
      - image: circleci/ruby:2.4.4-stretch-node
 | 
			
		||||
      - image: circleci/ruby:2.4.5-stretch-node
 | 
			
		||||
        environment: *ruby_environment
 | 
			
		||||
      - image: circleci/postgres:10.3-alpine
 | 
			
		||||
      - image: circleci/postgres:10.6-alpine
 | 
			
		||||
        environment:
 | 
			
		||||
          POSTGRES_USER: root
 | 
			
		||||
      - image: circleci/redis:4.0.9-alpine
 | 
			
		||||
    <<: *test_steps
 | 
			
		||||
 | 
			
		||||
  test-ruby2.3:
 | 
			
		||||
    <<: *defaults
 | 
			
		||||
    docker:
 | 
			
		||||
      - image: circleci/ruby:2.3.7-stretch-node
 | 
			
		||||
        environment: *ruby_environment
 | 
			
		||||
      - image: circleci/postgres:10.3-alpine
 | 
			
		||||
        environment:
 | 
			
		||||
          POSTGRES_USER: root
 | 
			
		||||
      - image: circleci/redis:4.0.9-alpine
 | 
			
		||||
      - image: circleci/redis:4.0.12-alpine
 | 
			
		||||
    <<: *test_steps
 | 
			
		||||
 | 
			
		||||
  test-webui:
 | 
			
		||||
    <<: *defaults
 | 
			
		||||
    docker:
 | 
			
		||||
      - image: circleci/node:8.11.1-stretch
 | 
			
		||||
      - image: circleci/node:8.15.0-stretch
 | 
			
		||||
    steps:
 | 
			
		||||
      - *attach_workspace
 | 
			
		||||
      - run: ./bin/retry yarn test:jest
 | 
			
		||||
@ -183,20 +183,24 @@ workflows:
 | 
			
		||||
  build-and-test:
 | 
			
		||||
    jobs:
 | 
			
		||||
      - install
 | 
			
		||||
      - install-ruby2.6:
 | 
			
		||||
          requires:
 | 
			
		||||
            - install
 | 
			
		||||
      - install-ruby2.5:
 | 
			
		||||
          requires:
 | 
			
		||||
            - install
 | 
			
		||||
            - install-ruby2.6
 | 
			
		||||
      - install-ruby2.4:
 | 
			
		||||
          requires:
 | 
			
		||||
            - install
 | 
			
		||||
            - install-ruby2.5
 | 
			
		||||
      - install-ruby2.3:
 | 
			
		||||
          requires:
 | 
			
		||||
            - install
 | 
			
		||||
            - install-ruby2.5
 | 
			
		||||
            - install-ruby2.6
 | 
			
		||||
      - build:
 | 
			
		||||
          requires:
 | 
			
		||||
            - install-ruby2.5
 | 
			
		||||
            - install-ruby2.6
 | 
			
		||||
      - test-ruby2.6:
 | 
			
		||||
          requires:
 | 
			
		||||
            - install-ruby2.6
 | 
			
		||||
            - build
 | 
			
		||||
      - test-ruby2.5:
 | 
			
		||||
          requires:
 | 
			
		||||
            - install-ruby2.5
 | 
			
		||||
@ -205,13 +209,9 @@ workflows:
 | 
			
		||||
          requires:
 | 
			
		||||
            - install-ruby2.4
 | 
			
		||||
            - build
 | 
			
		||||
      - test-ruby2.3:
 | 
			
		||||
          requires:
 | 
			
		||||
            - install-ruby2.3
 | 
			
		||||
            - build
 | 
			
		||||
      - test-webui:
 | 
			
		||||
          requires:
 | 
			
		||||
            - install
 | 
			
		||||
      - check-i18n:
 | 
			
		||||
          requires:
 | 
			
		||||
            - install-ruby2.5
 | 
			
		||||
            - install-ruby2.6
 | 
			
		||||
 | 
			
		||||
@ -27,7 +27,7 @@ plugins:
 | 
			
		||||
    enabled: true
 | 
			
		||||
  eslint:
 | 
			
		||||
    enabled: true
 | 
			
		||||
    channel: eslint-4
 | 
			
		||||
    channel: eslint-5
 | 
			
		||||
  rubocop:
 | 
			
		||||
    enabled: true
 | 
			
		||||
    channel: rubocop-0-54
 | 
			
		||||
 | 
			
		||||
@ -1,30 +1,13 @@
 | 
			
		||||
# See https://help.github.com/articles/ignoring-files for more about ignoring files.
 | 
			
		||||
#
 | 
			
		||||
# If you find yourself ignoring temporary files generated by your text editor
 | 
			
		||||
# or operating system, you probably want to add a global ignore instead:
 | 
			
		||||
#   git config --global core.excludesfile '~/.gitignore_global'
 | 
			
		||||
 | 
			
		||||
# Ignore bundler config.
 | 
			
		||||
/.bundle
 | 
			
		||||
 | 
			
		||||
# Ignore the default SQLite database.
 | 
			
		||||
/db/*.sqlite3
 | 
			
		||||
/db/*.sqlite3-journal
 | 
			
		||||
 | 
			
		||||
# Ignore all logfiles and tempfiles.
 | 
			
		||||
/log/*
 | 
			
		||||
!/log/.keep
 | 
			
		||||
/tmp
 | 
			
		||||
coverage
 | 
			
		||||
public/system
 | 
			
		||||
public/assets
 | 
			
		||||
.env
 | 
			
		||||
.env.production
 | 
			
		||||
node_modules/
 | 
			
		||||
neo4j/
 | 
			
		||||
 | 
			
		||||
# Ignore Vagrant files
 | 
			
		||||
.vagrant/
 | 
			
		||||
 | 
			
		||||
# Ignore Capistrano customizations
 | 
			
		||||
config/deploy/*
 | 
			
		||||
/build/**
 | 
			
		||||
/coverage/**
 | 
			
		||||
/db/**
 | 
			
		||||
/lib/**
 | 
			
		||||
/log/**
 | 
			
		||||
/node_modules/**
 | 
			
		||||
/nonobox/**
 | 
			
		||||
/public/**
 | 
			
		||||
!/public/embed.js
 | 
			
		||||
/spec/**
 | 
			
		||||
/tmp/**
 | 
			
		||||
/vendor/**
 | 
			
		||||
!.eslintrc.js
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										199
									
								
								.eslintrc.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										199
									
								
								.eslintrc.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,199 @@
 | 
			
		||||
module.exports = {
 | 
			
		||||
  root: true,
 | 
			
		||||
 | 
			
		||||
  env: {
 | 
			
		||||
    browser: true,
 | 
			
		||||
    node: true,
 | 
			
		||||
    es6: true,
 | 
			
		||||
    jest: true,
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  globals: {
 | 
			
		||||
    ATTACHMENT_HOST: false,
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  parser: 'babel-eslint',
 | 
			
		||||
 | 
			
		||||
  plugins: [
 | 
			
		||||
    'react',
 | 
			
		||||
    'jsx-a11y',
 | 
			
		||||
    'import',
 | 
			
		||||
    'promise',
 | 
			
		||||
  ],
 | 
			
		||||
 | 
			
		||||
  parserOptions: {
 | 
			
		||||
    sourceType: 'module',
 | 
			
		||||
    ecmaFeatures: {
 | 
			
		||||
      experimentalObjectRestSpread: true,
 | 
			
		||||
      jsx: true,
 | 
			
		||||
    },
 | 
			
		||||
    ecmaVersion: 2018,
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  settings: {
 | 
			
		||||
    react: {
 | 
			
		||||
      version: 'detect',
 | 
			
		||||
    },
 | 
			
		||||
    'import/extensions': [
 | 
			
		||||
      '.js',
 | 
			
		||||
    ],
 | 
			
		||||
    'import/ignore': [
 | 
			
		||||
      'node_modules',
 | 
			
		||||
      '\\.(css|scss|json)$',
 | 
			
		||||
    ],
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  rules: {
 | 
			
		||||
    'brace-style': 'warn',
 | 
			
		||||
    'comma-dangle': ['error', 'always-multiline'],
 | 
			
		||||
    'comma-spacing': [
 | 
			
		||||
      'warn',
 | 
			
		||||
      {
 | 
			
		||||
        before: false,
 | 
			
		||||
        after: true,
 | 
			
		||||
      },
 | 
			
		||||
    ],
 | 
			
		||||
    'comma-style': ['warn', 'last'],
 | 
			
		||||
    'consistent-return': 'error',
 | 
			
		||||
    'dot-notation': 'error',
 | 
			
		||||
    eqeqeq: 'error',
 | 
			
		||||
    indent: ['warn', 2],
 | 
			
		||||
    'jsx-quotes': ['error', 'prefer-single'],
 | 
			
		||||
    'no-catch-shadow': 'error',
 | 
			
		||||
    'no-cond-assign': 'error',
 | 
			
		||||
    'no-console': [
 | 
			
		||||
      'warn',
 | 
			
		||||
      {
 | 
			
		||||
        allow: [
 | 
			
		||||
          'error',
 | 
			
		||||
          'warn',
 | 
			
		||||
        ],
 | 
			
		||||
      },
 | 
			
		||||
    ],
 | 
			
		||||
    'no-fallthrough': 'error',
 | 
			
		||||
    'no-irregular-whitespace': 'error',
 | 
			
		||||
    'no-mixed-spaces-and-tabs': 'warn',
 | 
			
		||||
    'no-nested-ternary': 'warn',
 | 
			
		||||
    'no-trailing-spaces': 'warn',
 | 
			
		||||
    'no-undef': 'error',
 | 
			
		||||
    'no-unreachable': 'error',
 | 
			
		||||
    'no-unused-expressions': 'error',
 | 
			
		||||
    'no-unused-vars': [
 | 
			
		||||
      'error',
 | 
			
		||||
      {
 | 
			
		||||
        vars: 'all',
 | 
			
		||||
        args: 'after-used',
 | 
			
		||||
        ignoreRestSiblings: true,
 | 
			
		||||
      },
 | 
			
		||||
    ],
 | 
			
		||||
    'object-curly-spacing': ['error', 'always'],
 | 
			
		||||
    'padded-blocks': [
 | 
			
		||||
      'error',
 | 
			
		||||
      {
 | 
			
		||||
        classes: 'always',
 | 
			
		||||
      },
 | 
			
		||||
    ],
 | 
			
		||||
    quotes: ['error', 'single'],
 | 
			
		||||
    semi: 'error',
 | 
			
		||||
    strict: 'off',
 | 
			
		||||
    'valid-typeof': 'error',
 | 
			
		||||
 | 
			
		||||
    'react/jsx-boolean-value': 'error',
 | 
			
		||||
    'react/jsx-closing-bracket-location': ['error', 'line-aligned'],
 | 
			
		||||
    'react/jsx-curly-spacing': 'error',
 | 
			
		||||
    'react/jsx-equals-spacing': 'error',
 | 
			
		||||
    'react/jsx-first-prop-new-line': ['error', 'multiline-multiprop'],
 | 
			
		||||
    'react/jsx-indent': ['error', 2],
 | 
			
		||||
    'react/jsx-no-bind': 'error',
 | 
			
		||||
    'react/jsx-no-duplicate-props': 'error',
 | 
			
		||||
    'react/jsx-no-undef': 'error',
 | 
			
		||||
    'react/jsx-tag-spacing': 'error',
 | 
			
		||||
    'react/jsx-uses-react': 'error',
 | 
			
		||||
    'react/jsx-uses-vars': 'error',
 | 
			
		||||
    'react/jsx-wrap-multilines': 'error',
 | 
			
		||||
    'react/no-multi-comp': 'off',
 | 
			
		||||
    'react/no-string-refs': 'error',
 | 
			
		||||
    'react/prop-types': 'error',
 | 
			
		||||
    'react/self-closing-comp': 'error',
 | 
			
		||||
 | 
			
		||||
    'jsx-a11y/accessible-emoji': 'warn',
 | 
			
		||||
    'jsx-a11y/alt-text': 'warn',
 | 
			
		||||
    'jsx-a11y/anchor-has-content': 'warn',
 | 
			
		||||
    'jsx-a11y/anchor-is-valid': [
 | 
			
		||||
      'warn',
 | 
			
		||||
      {
 | 
			
		||||
        components: [
 | 
			
		||||
          'Link',
 | 
			
		||||
          'NavLink',
 | 
			
		||||
        ],
 | 
			
		||||
        specialLink: [
 | 
			
		||||
          'to',
 | 
			
		||||
        ],
 | 
			
		||||
        aspect: [
 | 
			
		||||
          'noHref',
 | 
			
		||||
          'invalidHref',
 | 
			
		||||
          'preferButton',
 | 
			
		||||
        ],
 | 
			
		||||
      },
 | 
			
		||||
    ],
 | 
			
		||||
    'jsx-a11y/aria-activedescendant-has-tabindex': 'warn',
 | 
			
		||||
    'jsx-a11y/aria-props': 'warn',
 | 
			
		||||
    'jsx-a11y/aria-proptypes': 'warn',
 | 
			
		||||
    'jsx-a11y/aria-role': 'warn',
 | 
			
		||||
    'jsx-a11y/aria-unsupported-elements': 'warn',
 | 
			
		||||
    'jsx-a11y/heading-has-content': 'warn',
 | 
			
		||||
    'jsx-a11y/html-has-lang': 'warn',
 | 
			
		||||
    'jsx-a11y/iframe-has-title': 'warn',
 | 
			
		||||
    'jsx-a11y/img-redundant-alt': 'warn',
 | 
			
		||||
    'jsx-a11y/interactive-supports-focus': 'warn',
 | 
			
		||||
    'jsx-a11y/label-has-for': 'off',
 | 
			
		||||
    'jsx-a11y/mouse-events-have-key-events': 'warn',
 | 
			
		||||
    'jsx-a11y/no-access-key': 'warn',
 | 
			
		||||
    'jsx-a11y/no-distracting-elements': 'warn',
 | 
			
		||||
    'jsx-a11y/no-noninteractive-element-interactions': [
 | 
			
		||||
      'warn',
 | 
			
		||||
      {
 | 
			
		||||
        handlers: [
 | 
			
		||||
          'onClick',
 | 
			
		||||
        ],
 | 
			
		||||
      },
 | 
			
		||||
    ],
 | 
			
		||||
    'jsx-a11y/no-onchange': 'warn',
 | 
			
		||||
    'jsx-a11y/no-redundant-roles': 'warn',
 | 
			
		||||
    'jsx-a11y/no-static-element-interactions': [
 | 
			
		||||
      'warn',
 | 
			
		||||
      {
 | 
			
		||||
        handlers: [
 | 
			
		||||
          'onClick',
 | 
			
		||||
        ],
 | 
			
		||||
      },
 | 
			
		||||
    ],
 | 
			
		||||
    'jsx-a11y/role-has-required-aria-props': 'warn',
 | 
			
		||||
    'jsx-a11y/role-supports-aria-props': 'off',
 | 
			
		||||
    'jsx-a11y/scope': 'warn',
 | 
			
		||||
    'jsx-a11y/tabindex-no-positive': 'warn',
 | 
			
		||||
 | 
			
		||||
    'import/extensions': [
 | 
			
		||||
      'error',
 | 
			
		||||
      'always',
 | 
			
		||||
      {
 | 
			
		||||
        js: 'never',
 | 
			
		||||
      },
 | 
			
		||||
    ],
 | 
			
		||||
    'import/newline-after-import': 'error',
 | 
			
		||||
    'import/no-extraneous-dependencies': [
 | 
			
		||||
      'error',
 | 
			
		||||
      {
 | 
			
		||||
        devDependencies: [
 | 
			
		||||
          'config/webpack/**',
 | 
			
		||||
          'app/javascript/mastodon/test_setup.js',
 | 
			
		||||
          'app/javascript/**/__tests__/**',
 | 
			
		||||
        ],
 | 
			
		||||
      },
 | 
			
		||||
    ],
 | 
			
		||||
    'import/no-unresolved': 'error',
 | 
			
		||||
    'import/no-webpack-loader-syntax': 'error',
 | 
			
		||||
 | 
			
		||||
    'promise/catch-or-return': 'error',
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										170
									
								
								.eslintrc.yml
									
									
									
									
									
								
							
							
						
						
									
										170
									
								
								.eslintrc.yml
									
									
									
									
									
								
							@ -1,170 +0,0 @@
 | 
			
		||||
---
 | 
			
		||||
root: true
 | 
			
		||||
 | 
			
		||||
env:
 | 
			
		||||
  browser: true
 | 
			
		||||
  node: true
 | 
			
		||||
  es6: true
 | 
			
		||||
  jest: true
 | 
			
		||||
 | 
			
		||||
globals:
 | 
			
		||||
  ATTACHMENT_HOST: false
 | 
			
		||||
 | 
			
		||||
parser: babel-eslint
 | 
			
		||||
 | 
			
		||||
plugins:
 | 
			
		||||
- react
 | 
			
		||||
- jsx-a11y
 | 
			
		||||
- import
 | 
			
		||||
- promise
 | 
			
		||||
 | 
			
		||||
parserOptions:
 | 
			
		||||
  sourceType: module
 | 
			
		||||
  ecmaFeatures:
 | 
			
		||||
    experimentalObjectRestSpread: true
 | 
			
		||||
    jsx: true
 | 
			
		||||
  ecmaVersion: 2018
 | 
			
		||||
 | 
			
		||||
settings:
 | 
			
		||||
  import/extensions:
 | 
			
		||||
  - .js
 | 
			
		||||
  import/ignore:
 | 
			
		||||
  - node_modules
 | 
			
		||||
  - \\.(css|scss|json)$
 | 
			
		||||
 | 
			
		||||
rules:
 | 
			
		||||
  brace-style: warn
 | 
			
		||||
  comma-dangle:
 | 
			
		||||
  - error
 | 
			
		||||
  - always-multiline
 | 
			
		||||
  comma-spacing:
 | 
			
		||||
  - warn
 | 
			
		||||
  - before: false
 | 
			
		||||
    after: true
 | 
			
		||||
  comma-style:
 | 
			
		||||
  - warn
 | 
			
		||||
  - last
 | 
			
		||||
  consistent-return: error
 | 
			
		||||
  dot-notation: error
 | 
			
		||||
  eqeqeq: error
 | 
			
		||||
  indent:
 | 
			
		||||
  - warn
 | 
			
		||||
  - 2
 | 
			
		||||
  jsx-quotes:
 | 
			
		||||
  - error
 | 
			
		||||
  - prefer-single
 | 
			
		||||
  no-catch-shadow: error
 | 
			
		||||
  no-cond-assign: error
 | 
			
		||||
  no-console:
 | 
			
		||||
  - warn
 | 
			
		||||
  - allow:
 | 
			
		||||
    - error
 | 
			
		||||
    - warn
 | 
			
		||||
  no-fallthrough: error
 | 
			
		||||
  no-irregular-whitespace: error
 | 
			
		||||
  no-mixed-spaces-and-tabs: warn
 | 
			
		||||
  no-nested-ternary: warn
 | 
			
		||||
  no-trailing-spaces: warn
 | 
			
		||||
  no-undef: error
 | 
			
		||||
  no-unreachable: error
 | 
			
		||||
  no-unused-expressions: error
 | 
			
		||||
  no-unused-vars:
 | 
			
		||||
  - error
 | 
			
		||||
  - vars: all
 | 
			
		||||
    args: after-used
 | 
			
		||||
    ignoreRestSiblings: true
 | 
			
		||||
  object-curly-spacing:
 | 
			
		||||
  - error
 | 
			
		||||
  - always
 | 
			
		||||
  padded-blocks:
 | 
			
		||||
  - error
 | 
			
		||||
  - classes: always
 | 
			
		||||
  quotes:
 | 
			
		||||
  - error
 | 
			
		||||
  - single
 | 
			
		||||
  semi: error
 | 
			
		||||
  strict: off
 | 
			
		||||
  valid-typeof: error
 | 
			
		||||
 | 
			
		||||
  react/jsx-boolean-value: error
 | 
			
		||||
  react/jsx-closing-bracket-location:
 | 
			
		||||
  - error
 | 
			
		||||
  - line-aligned
 | 
			
		||||
  react/jsx-curly-spacing: error
 | 
			
		||||
  react/jsx-equals-spacing: error
 | 
			
		||||
  react/jsx-first-prop-new-line:
 | 
			
		||||
  - error
 | 
			
		||||
  - multiline-multiprop
 | 
			
		||||
  react/jsx-indent:
 | 
			
		||||
  - error
 | 
			
		||||
  - 2
 | 
			
		||||
  react/jsx-no-bind: error
 | 
			
		||||
  react/jsx-no-duplicate-props: error
 | 
			
		||||
  react/jsx-no-undef: error
 | 
			
		||||
  react/jsx-tag-spacing: error
 | 
			
		||||
  react/jsx-uses-react: error
 | 
			
		||||
  react/jsx-uses-vars: error
 | 
			
		||||
  react/jsx-wrap-multilines: error
 | 
			
		||||
  react/no-multi-comp: off
 | 
			
		||||
  react/no-string-refs: error
 | 
			
		||||
  react/prop-types: error
 | 
			
		||||
  react/self-closing-comp: error
 | 
			
		||||
 | 
			
		||||
  jsx-a11y/accessible-emoji: warn
 | 
			
		||||
  jsx-a11y/alt-text: warn
 | 
			
		||||
  jsx-a11y/anchor-has-content: warn
 | 
			
		||||
  jsx-a11y/anchor-is-valid:
 | 
			
		||||
  - warn
 | 
			
		||||
  - components:
 | 
			
		||||
    - Link
 | 
			
		||||
    - NavLink
 | 
			
		||||
    specialLink:
 | 
			
		||||
    - to
 | 
			
		||||
    aspect:
 | 
			
		||||
    - noHref
 | 
			
		||||
    - invalidHref
 | 
			
		||||
    - preferButton
 | 
			
		||||
  jsx-a11y/aria-activedescendant-has-tabindex: warn
 | 
			
		||||
  jsx-a11y/aria-props: warn
 | 
			
		||||
  jsx-a11y/aria-proptypes: warn
 | 
			
		||||
  jsx-a11y/aria-role: warn
 | 
			
		||||
  jsx-a11y/aria-unsupported-elements: warn
 | 
			
		||||
  jsx-a11y/heading-has-content: warn
 | 
			
		||||
  jsx-a11y/html-has-lang: warn
 | 
			
		||||
  jsx-a11y/iframe-has-title: warn
 | 
			
		||||
  jsx-a11y/img-redundant-alt: warn
 | 
			
		||||
  jsx-a11y/interactive-supports-focus: warn
 | 
			
		||||
  jsx-a11y/label-has-for: off
 | 
			
		||||
  jsx-a11y/mouse-events-have-key-events: warn
 | 
			
		||||
  jsx-a11y/no-access-key: warn
 | 
			
		||||
  jsx-a11y/no-distracting-elements: warn
 | 
			
		||||
  jsx-a11y/no-noninteractive-element-interactions:
 | 
			
		||||
  - warn
 | 
			
		||||
  - handlers:
 | 
			
		||||
    - onClick
 | 
			
		||||
  jsx-a11y/no-onchange: warn
 | 
			
		||||
  jsx-a11y/no-redundant-roles: warn
 | 
			
		||||
  jsx-a11y/no-static-element-interactions:
 | 
			
		||||
  - warn
 | 
			
		||||
  - handlers:
 | 
			
		||||
    - onClick
 | 
			
		||||
  jsx-a11y/role-has-required-aria-props: warn
 | 
			
		||||
  jsx-a11y/role-supports-aria-props: off
 | 
			
		||||
  jsx-a11y/scope: warn
 | 
			
		||||
  jsx-a11y/tabindex-no-positive: warn
 | 
			
		||||
 | 
			
		||||
  import/extensions:
 | 
			
		||||
  - error
 | 
			
		||||
  - always
 | 
			
		||||
  - js: never
 | 
			
		||||
  import/newline-after-import: error
 | 
			
		||||
  import/no-extraneous-dependencies:
 | 
			
		||||
  - error
 | 
			
		||||
  - devDependencies:
 | 
			
		||||
    - "config/webpack/**"
 | 
			
		||||
    - "app/javascript/mastodon/test_setup.js"
 | 
			
		||||
    - "app/javascript/**/__tests__/**"
 | 
			
		||||
  import/no-unresolved: error
 | 
			
		||||
  import/no-webpack-loader-syntax: error
 | 
			
		||||
 | 
			
		||||
  promise/catch-or-return: error
 | 
			
		||||
@ -1,9 +0,0 @@
 | 
			
		||||
plugins:
 | 
			
		||||
  postcss-smart-import: {}
 | 
			
		||||
  precss: {}
 | 
			
		||||
  autoprefixer:
 | 
			
		||||
    browsers:
 | 
			
		||||
      - last 2 versions
 | 
			
		||||
      - IE >= 11
 | 
			
		||||
      - iOS >= 9
 | 
			
		||||
  postcss-object-fit-images: {}
 | 
			
		||||
@ -1 +1 @@
 | 
			
		||||
2.5.3
 | 
			
		||||
2.6.0
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										89
									
								
								AUTHORS.md
									
									
									
									
									
								
							
							
						
						
									
										89
									
								
								AUTHORS.md
									
									
									
									
									
								
							@ -4,38 +4,38 @@ and provided thanks to the work of the following contributors:
 | 
			
		||||
* [Gargron](https://github.com/Gargron)
 | 
			
		||||
* [ykzts](https://github.com/ykzts)
 | 
			
		||||
* [akihikodaki](https://github.com/akihikodaki)
 | 
			
		||||
* [mjankowski](https://github.com/mjankowski)
 | 
			
		||||
* [ThibG](https://github.com/ThibG)
 | 
			
		||||
* [mjankowski](https://github.com/mjankowski)
 | 
			
		||||
* [unarist](https://github.com/unarist)
 | 
			
		||||
* [m4sk1n](https://github.com/m4sk1n)
 | 
			
		||||
* [dependabot[bot]](https://github.com/apps/dependabot)
 | 
			
		||||
* [yiskah](https://github.com/yiskah)
 | 
			
		||||
* [nolanlawson](https://github.com/nolanlawson)
 | 
			
		||||
* [sorin-davidoi](https://github.com/sorin-davidoi)
 | 
			
		||||
* [ysksn](https://github.com/ysksn)
 | 
			
		||||
* [abcang](https://github.com/abcang)
 | 
			
		||||
* [lynlynlynx](https://github.com/lynlynlynx)
 | 
			
		||||
* [dependabot[bot]](https://github.com/apps/dependabot)
 | 
			
		||||
* [alpaca-tc](https://github.com/alpaca-tc)
 | 
			
		||||
* [mayaeh](https://github.com/mayaeh)
 | 
			
		||||
* [renatolond](https://github.com/renatolond)
 | 
			
		||||
* [nclm](https://github.com/nclm)
 | 
			
		||||
* [ineffyble](https://github.com/ineffyble)
 | 
			
		||||
* [renatolond](https://github.com/renatolond)
 | 
			
		||||
* [jeroenpraat](https://github.com/jeroenpraat)
 | 
			
		||||
* [mayaeh](https://github.com/mayaeh)
 | 
			
		||||
* [blackle](https://github.com/blackle)
 | 
			
		||||
* [Quent-in](https://github.com/Quent-in)
 | 
			
		||||
* [JantsoP](https://github.com/JantsoP)
 | 
			
		||||
* [mabkenar](https://github.com/mabkenar)
 | 
			
		||||
* [nullkal](https://github.com/nullkal)
 | 
			
		||||
* [yookoala](https://github.com/yookoala)
 | 
			
		||||
* [mabkenar](https://github.com/mabkenar)
 | 
			
		||||
* [ysksn](https://github.com/ysksn)
 | 
			
		||||
* [Kjwon15](https://github.com/Kjwon15)
 | 
			
		||||
* [shuheiktgw](https://github.com/shuheiktgw)
 | 
			
		||||
* [ashfurrow](https://github.com/ashfurrow)
 | 
			
		||||
* [Kjwon15](https://github.com/Kjwon15)
 | 
			
		||||
* [Quenty31](https://github.com/Quenty31)
 | 
			
		||||
* [zunda](https://github.com/zunda)
 | 
			
		||||
* [eramdam](https://github.com/eramdam)
 | 
			
		||||
* [masarakki](https://github.com/masarakki)
 | 
			
		||||
* [takayamaki](https://github.com/takayamaki)
 | 
			
		||||
* [masarakki](https://github.com/masarakki)
 | 
			
		||||
* [ticky](https://github.com/ticky)
 | 
			
		||||
* [Quenty31](https://github.com/Quenty31)
 | 
			
		||||
* [danhunsaker](https://github.com/danhunsaker)
 | 
			
		||||
* [ThisIsMissEm](https://github.com/ThisIsMissEm)
 | 
			
		||||
* [hcmiya](https://github.com/hcmiya)
 | 
			
		||||
@ -88,16 +88,19 @@ and provided thanks to the work of the following contributors:
 | 
			
		||||
* [mistydemeo](https://github.com/mistydemeo)
 | 
			
		||||
* [dunn](https://github.com/dunn)
 | 
			
		||||
* [xqus](https://github.com/xqus)
 | 
			
		||||
* [hugogameiro](https://github.com/hugogameiro)
 | 
			
		||||
* [pfm-eyesightjp](https://github.com/pfm-eyesightjp)
 | 
			
		||||
* [fakenine](https://github.com/fakenine)
 | 
			
		||||
* [tsuwatch](https://github.com/tsuwatch)
 | 
			
		||||
* [victorhck](https://github.com/victorhck)
 | 
			
		||||
* [ashleyhull-versent](https://github.com/ashleyhull-versent)
 | 
			
		||||
* [kedamaDQ](https://github.com/kedamaDQ)
 | 
			
		||||
* [puckipedia](https://github.com/puckipedia)
 | 
			
		||||
* [fvh-P](https://github.com/fvh-P)
 | 
			
		||||
* [contraexemplo](https://github.com/contraexemplo)
 | 
			
		||||
* [hugogameiro](https://github.com/hugogameiro)
 | 
			
		||||
* [kazu9su](https://github.com/kazu9su)
 | 
			
		||||
* [Komic](https://github.com/Komic)
 | 
			
		||||
* [lmorchard](https://github.com/lmorchard)
 | 
			
		||||
* [diomed](https://github.com/diomed)
 | 
			
		||||
* [ariasuni](https://github.com/ariasuni)
 | 
			
		||||
* [Neetshin](mailto:neetshin@neetsh.in)
 | 
			
		||||
@ -105,7 +108,6 @@ and provided thanks to the work of the following contributors:
 | 
			
		||||
* [ProgVal](https://github.com/ProgVal)
 | 
			
		||||
* [valentin2105](https://github.com/valentin2105)
 | 
			
		||||
* [yuntan](https://github.com/yuntan)
 | 
			
		||||
* [ashleyhull-versent](https://github.com/ashleyhull-versent)
 | 
			
		||||
* [goofy-bz](mailto:goofy@babelzilla.org)
 | 
			
		||||
* [kadiix](https://github.com/kadiix)
 | 
			
		||||
* [kodacs](https://github.com/kodacs)
 | 
			
		||||
@ -119,35 +121,37 @@ and provided thanks to the work of the following contributors:
 | 
			
		||||
* [northerner](https://github.com/northerner)
 | 
			
		||||
* [fhemberger](https://github.com/fhemberger)
 | 
			
		||||
* [greysteil](https://github.com/greysteil)
 | 
			
		||||
* [hnrysmth](https://github.com/hnrysmth)
 | 
			
		||||
* [hensmith](https://github.com/hensmith)
 | 
			
		||||
* [hinaloe](https://github.com/hinaloe)
 | 
			
		||||
* [d6rkaiz](https://github.com/d6rkaiz)
 | 
			
		||||
* [Reverite](https://github.com/Reverite)
 | 
			
		||||
* [JMendyk](https://github.com/JMendyk)
 | 
			
		||||
* [JohnD28](https://github.com/JohnD28)
 | 
			
		||||
* [znz](https://github.com/znz)
 | 
			
		||||
* [Naouak](https://github.com/Naouak)
 | 
			
		||||
* [pawelngei](https://github.com/pawelngei)
 | 
			
		||||
* [reneklacan](https://github.com/reneklacan)
 | 
			
		||||
* [ekiru](https://github.com/ekiru)
 | 
			
		||||
* [tcitworld](https://github.com/tcitworld)
 | 
			
		||||
* [geta6](https://github.com/geta6)
 | 
			
		||||
* [happycoloredbanana](https://github.com/happycoloredbanana)
 | 
			
		||||
* [kedamaDQ](https://github.com/kedamaDQ)
 | 
			
		||||
* [leopku](https://github.com/leopku)
 | 
			
		||||
* [SansPseudoFix](https://github.com/SansPseudoFix)
 | 
			
		||||
* [tomfhowe](https://github.com/tomfhowe)
 | 
			
		||||
* [noraworld](https://github.com/noraworld)
 | 
			
		||||
* [theboss](https://github.com/theboss)
 | 
			
		||||
* [178inaba](https://github.com/178inaba)
 | 
			
		||||
* [Aditoo17](https://github.com/Aditoo17)
 | 
			
		||||
* [alyssais](https://github.com/alyssais)
 | 
			
		||||
* [kodnaplakal](https://github.com/kodnaplakal)
 | 
			
		||||
* [stalker314314](https://github.com/stalker314314)
 | 
			
		||||
* [huertanix](https://github.com/huertanix)
 | 
			
		||||
* [genesixx](https://github.com/genesixx)
 | 
			
		||||
* [halkeye](https://github.com/halkeye)
 | 
			
		||||
* [hinaloe](https://github.com/hinaloe)
 | 
			
		||||
* [treby](https://github.com/treby)
 | 
			
		||||
* [Reverite](https://github.com/Reverite)
 | 
			
		||||
* [jpdevries](https://github.com/jpdevries)
 | 
			
		||||
* [H-C-F](https://github.com/H-C-F)
 | 
			
		||||
* [gdpelican](https://github.com/gdpelican)
 | 
			
		||||
* [kmichl](https://github.com/kmichl)
 | 
			
		||||
* [Kurtis Rainbolt-Greene](mailto:me@kurtisrainboltgreene.name)
 | 
			
		||||
* [saper](https://github.com/saper)
 | 
			
		||||
* [nevillepark](https://github.com/nevillepark)
 | 
			
		||||
@ -157,6 +161,7 @@ and provided thanks to the work of the following contributors:
 | 
			
		||||
* [Ram Lmn](mailto:ramlmn@users.noreply.github.com)
 | 
			
		||||
* [harukasan](https://github.com/harukasan)
 | 
			
		||||
* [stamak](https://github.com/stamak)
 | 
			
		||||
* [noellabo](https://github.com/noellabo)
 | 
			
		||||
* [Technowix](mailto:technowix@users.noreply.github.com)
 | 
			
		||||
* [Eychics](https://github.com/Eychics)
 | 
			
		||||
* [Thor Harald Johansen](mailto:thj@thj.no)
 | 
			
		||||
@ -165,22 +170,27 @@ and provided thanks to the work of the following contributors:
 | 
			
		||||
* [Valentin_NC](mailto:valentin.ouvrard@nautile.sarl)
 | 
			
		||||
* [R0ckweb](https://github.com/R0ckweb)
 | 
			
		||||
* [caasi](https://github.com/caasi)
 | 
			
		||||
* [chr-1x](https://github.com/chr-1x)
 | 
			
		||||
* [esetomo](https://github.com/esetomo)
 | 
			
		||||
* [foxiehkins](https://github.com/foxiehkins)
 | 
			
		||||
* [hoodie](mailto:hoodiekitten@outlook.com)
 | 
			
		||||
* [luzi82](https://github.com/luzi82)
 | 
			
		||||
* [duxovni](https://github.com/duxovni)
 | 
			
		||||
* [trwnh](https://github.com/trwnh)
 | 
			
		||||
* [unsmell](https://github.com/unsmell)
 | 
			
		||||
* [valerauko](https://github.com/valerauko)
 | 
			
		||||
* [chriswmartin](https://github.com/chriswmartin)
 | 
			
		||||
* [vahnj](https://github.com/vahnj)
 | 
			
		||||
* [ikuradon](https://github.com/ikuradon)
 | 
			
		||||
* [AndreLewin](https://github.com/AndreLewin)
 | 
			
		||||
* [rinsuki](https://github.com/rinsuki)
 | 
			
		||||
* [0xflotus](https://github.com/0xflotus)
 | 
			
		||||
* [redtachyons](https://github.com/redtachyons)
 | 
			
		||||
* [thurloat](https://github.com/thurloat)
 | 
			
		||||
* [aaribaud](https://github.com/aaribaud)
 | 
			
		||||
* [Andrew](mailto:andrewlchronister@gmail.com)
 | 
			
		||||
* [estuans](https://github.com/estuans)
 | 
			
		||||
* [BenLubar](https://github.com/BenLubar)
 | 
			
		||||
* [dissolve](https://github.com/dissolve)
 | 
			
		||||
* [PurpleBooth](https://github.com/PurpleBooth)
 | 
			
		||||
* [bradurani](https://github.com/bradurani)
 | 
			
		||||
@ -192,7 +202,7 @@ and provided thanks to the work of the following contributors:
 | 
			
		||||
* [cdutson](https://github.com/cdutson)
 | 
			
		||||
* [farlistener](https://github.com/farlistener)
 | 
			
		||||
* [DavidLibeau](https://github.com/DavidLibeau)
 | 
			
		||||
* [SirCmpwn](https://github.com/SirCmpwn)
 | 
			
		||||
* [ddevault](https://github.com/ddevault)
 | 
			
		||||
* [Fjoerfoks](https://github.com/Fjoerfoks)
 | 
			
		||||
* [fmauNeko](https://github.com/fmauNeko)
 | 
			
		||||
* [gloaec](https://github.com/gloaec)
 | 
			
		||||
@ -207,6 +217,7 @@ and provided thanks to the work of the following contributors:
 | 
			
		||||
* [jasonrhodes](https://github.com/jasonrhodes)
 | 
			
		||||
* [Jason Snell](mailto:jason@newrelic.com)
 | 
			
		||||
* [jviide](https://github.com/jviide)
 | 
			
		||||
* [YuleZ](https://github.com/YuleZ)
 | 
			
		||||
* [crakaC](https://github.com/crakaC)
 | 
			
		||||
* [tkbky](https://github.com/tkbky)
 | 
			
		||||
* [Kaylee](mailto:kaylee@codethat.sucks)
 | 
			
		||||
@ -223,10 +234,12 @@ and provided thanks to the work of the following contributors:
 | 
			
		||||
* [petzah](https://github.com/petzah)
 | 
			
		||||
* [ignisf](https://github.com/ignisf)
 | 
			
		||||
* [raymestalez](https://github.com/raymestalez)
 | 
			
		||||
* [remram44](https://github.com/remram44)
 | 
			
		||||
* [sascha-sl](https://github.com/sascha-sl)
 | 
			
		||||
* [u1-liquid](https://github.com/u1-liquid)
 | 
			
		||||
* [sim6](https://github.com/sim6)
 | 
			
		||||
* [stemid](https://github.com/stemid)
 | 
			
		||||
* [sumdog](https://github.com/sumdog)
 | 
			
		||||
* [ThomasLeister](https://github.com/ThomasLeister)
 | 
			
		||||
* [mcat-ee](https://github.com/mcat-ee)
 | 
			
		||||
* [tototoshi](https://github.com/tototoshi)
 | 
			
		||||
@ -243,7 +256,6 @@ and provided thanks to the work of the following contributors:
 | 
			
		||||
* [aus-social](https://github.com/aus-social)
 | 
			
		||||
* [imbsky](https://github.com/imbsky)
 | 
			
		||||
* [bsky](mailto:me@imbsky.net)
 | 
			
		||||
* [chr-1x](https://github.com/chr-1x)
 | 
			
		||||
* [codl](https://github.com/codl)
 | 
			
		||||
* [cpsdqs](https://github.com/cpsdqs)
 | 
			
		||||
* [barzamin](https://github.com/barzamin)
 | 
			
		||||
@ -252,6 +264,7 @@ and provided thanks to the work of the following contributors:
 | 
			
		||||
* [ik11235](https://github.com/ik11235)
 | 
			
		||||
* [kawax](https://github.com/kawax)
 | 
			
		||||
* [007lva](https://github.com/007lva)
 | 
			
		||||
* [mbajur](https://github.com/mbajur)
 | 
			
		||||
* [matsurai25](https://github.com/matsurai25)
 | 
			
		||||
* [mecab](https://github.com/mecab)
 | 
			
		||||
* [nicobz25](https://github.com/nicobz25)
 | 
			
		||||
@ -259,7 +272,6 @@ and provided thanks to the work of the following contributors:
 | 
			
		||||
* [pinfort](https://github.com/pinfort)
 | 
			
		||||
* [rbaumert](https://github.com/rbaumert)
 | 
			
		||||
* [rhoio](https://github.com/rhoio)
 | 
			
		||||
* [trwnh](https://github.com/trwnh)
 | 
			
		||||
* [usagi-f](https://github.com/usagi-f)
 | 
			
		||||
* [vidarlee](https://github.com/vidarlee)
 | 
			
		||||
* [vjackson725](https://github.com/vjackson725)
 | 
			
		||||
@ -269,11 +281,11 @@ and provided thanks to the work of the following contributors:
 | 
			
		||||
* [Awea](https://github.com/Awea)
 | 
			
		||||
* [halcy](https://github.com/halcy)
 | 
			
		||||
* [naaaaaaaaaaaf](https://github.com/naaaaaaaaaaaf)
 | 
			
		||||
* [NecroTechno](https://github.com/NecroTechno)
 | 
			
		||||
* [8398a7](https://github.com/8398a7)
 | 
			
		||||
* [857b](https://github.com/857b)
 | 
			
		||||
* [insom](https://github.com/insom)
 | 
			
		||||
* [Aditoo17](https://github.com/Aditoo17)
 | 
			
		||||
* [tachyons](https://github.com/tachyons)
 | 
			
		||||
* [Esteth](https://github.com/Esteth)
 | 
			
		||||
* [unascribed](https://github.com/unascribed)
 | 
			
		||||
* [Aguay-val](https://github.com/Aguay-val)
 | 
			
		||||
* [Akihiko Odaki](mailto:nekomanma@pixiv.co.jp)
 | 
			
		||||
@ -283,6 +295,7 @@ and provided thanks to the work of the following contributors:
 | 
			
		||||
* [alxrcs](https://github.com/alxrcs)
 | 
			
		||||
* [console-cowboy](https://github.com/console-cowboy)
 | 
			
		||||
* [pointlessone](https://github.com/pointlessone)
 | 
			
		||||
* [Alkarex](https://github.com/Alkarex)
 | 
			
		||||
* [a2](https://github.com/a2)
 | 
			
		||||
* [0xa](https://github.com/0xa)
 | 
			
		||||
* [palindromordnilap](https://github.com/palindromordnilap)
 | 
			
		||||
@ -299,7 +312,6 @@ and provided thanks to the work of the following contributors:
 | 
			
		||||
* [ayumin](https://github.com/ayumin)
 | 
			
		||||
* [BaptisteGelez](https://github.com/BaptisteGelez)
 | 
			
		||||
* [bzg](https://github.com/bzg)
 | 
			
		||||
* [BenLubar](https://github.com/BenLubar)
 | 
			
		||||
* [benediktg](https://github.com/benediktg)
 | 
			
		||||
* [blakebarnett](https://github.com/blakebarnett)
 | 
			
		||||
* [bradj](https://github.com/bradj)
 | 
			
		||||
@ -341,6 +353,7 @@ and provided thanks to the work of the following contributors:
 | 
			
		||||
* [espenronnevik](https://github.com/espenronnevik)
 | 
			
		||||
* [Finariel](https://github.com/Finariel)
 | 
			
		||||
* [siuying](https://github.com/siuying)
 | 
			
		||||
* [fwenzel](https://github.com/fwenzel)
 | 
			
		||||
* [GenbuHase](https://github.com/GenbuHase)
 | 
			
		||||
* [hattori6789](https://github.com/hattori6789)
 | 
			
		||||
* [algernon](https://github.com/algernon)
 | 
			
		||||
@ -375,10 +388,9 @@ and provided thanks to the work of the following contributors:
 | 
			
		||||
* [jguerder](https://github.com/jguerder)
 | 
			
		||||
* [Jehops](https://github.com/Jehops)
 | 
			
		||||
* [joshuap](https://github.com/joshuap)
 | 
			
		||||
* [YuleZ](https://github.com/YuleZ)
 | 
			
		||||
* [Tiwy57](https://github.com/Tiwy57)
 | 
			
		||||
* [xuv](https://github.com/xuv)
 | 
			
		||||
* [Jnsll](https://github.com/Jnsll)
 | 
			
		||||
* [June Sallou](mailto:jnsll@users.noreply.github.com)
 | 
			
		||||
* [j0k3r](https://github.com/j0k3r)
 | 
			
		||||
* [KEINOS](https://github.com/KEINOS)
 | 
			
		||||
* [futoase](https://github.com/futoase)
 | 
			
		||||
@ -389,7 +401,6 @@ and provided thanks to the work of the following contributors:
 | 
			
		||||
* [k0ta0uchi](https://github.com/k0ta0uchi)
 | 
			
		||||
* [KrzysiekJ](https://github.com/KrzysiekJ)
 | 
			
		||||
* [leowzukw](https://github.com/leowzukw)
 | 
			
		||||
* [lmorchard](https://github.com/lmorchard)
 | 
			
		||||
* [Tak](https://github.com/Tak)
 | 
			
		||||
* [cacheflow](https://github.com/cacheflow)
 | 
			
		||||
* [ldidry](https://github.com/ldidry)
 | 
			
		||||
@ -426,6 +437,7 @@ and provided thanks to the work of the following contributors:
 | 
			
		||||
* [lae](https://github.com/lae)
 | 
			
		||||
* [Nanamachi](https://github.com/Nanamachi)
 | 
			
		||||
* [orinthe](https://github.com/orinthe)
 | 
			
		||||
* [NecroTechno](https://github.com/NecroTechno)
 | 
			
		||||
* [Dar13](https://github.com/Dar13)
 | 
			
		||||
* [ngerakines](https://github.com/ngerakines)
 | 
			
		||||
* [vonneudeck](https://github.com/vonneudeck)
 | 
			
		||||
@ -443,7 +455,6 @@ and provided thanks to the work of the following contributors:
 | 
			
		||||
* [Pangoraw](https://github.com/Pangoraw)
 | 
			
		||||
* [peterkeen](https://github.com/peterkeen)
 | 
			
		||||
* [pgate](https://github.com/pgate)
 | 
			
		||||
* [remram44](https://github.com/remram44)
 | 
			
		||||
* [retokromer](https://github.com/retokromer)
 | 
			
		||||
* [rfwatson](https://github.com/rfwatson)
 | 
			
		||||
* [rfreebern](https://github.com/rfreebern)
 | 
			
		||||
@ -455,19 +466,22 @@ and provided thanks to the work of the following contributors:
 | 
			
		||||
* [sts10](https://github.com/sts10)
 | 
			
		||||
* [skoji](https://github.com/skoji)
 | 
			
		||||
* [ScienJus](https://github.com/ScienJus)
 | 
			
		||||
* [larkinscott](https://github.com/larkinscott)
 | 
			
		||||
* [imolein](https://github.com/imolein)
 | 
			
		||||
* [blinry](https://github.com/blinry)
 | 
			
		||||
* [Noiwex](https://github.com/Noiwex)
 | 
			
		||||
* [yuki764](https://github.com/yuki764)
 | 
			
		||||
* [shnjp](https://github.com/shnjp)
 | 
			
		||||
* [ernix](https://github.com/ernix)
 | 
			
		||||
* [rosylilly](https://github.com/rosylilly)
 | 
			
		||||
* [shouko](https://github.com/shouko)
 | 
			
		||||
* [Scott Larkin](mailto:scott@codeclimate.com)
 | 
			
		||||
* [Sebastian Hübner](mailto:imolein@users.noreply.github.com)
 | 
			
		||||
* [Sebastian Morr](mailto:sebastian@morr.cc)
 | 
			
		||||
* [Sergei Č](mailto:noiwex1911@gmail.com)
 | 
			
		||||
* [Setuu](mailto:yuki764setuu@gmail.com)
 | 
			
		||||
* [Shaun Gillies](mailto:me@shaungillies.net)
 | 
			
		||||
* [Shin Adachi](mailto:shn@glucose.jp)
 | 
			
		||||
* [Shin Kojima](mailto:shin@kojima.org)
 | 
			
		||||
* [Sho Kusano](mailto:rosylilly@aduca.org)
 | 
			
		||||
* [Shouko Yu](mailto:imshouko@gmail.com)
 | 
			
		||||
* [Sina Mashek](mailto:sina@mashek.xyz)
 | 
			
		||||
* [sossii](https://github.com/sossii)
 | 
			
		||||
* [Sir-Boops](mailto:admin@boops.me)
 | 
			
		||||
* [Soshi Kato](mailto:mail@sossii.com)
 | 
			
		||||
* [Spanky](mailto:2788886+spankyworks@users.noreply.github.com)
 | 
			
		||||
* [StefOfficiel](mailto:pichard.stephane@free.fr)
 | 
			
		||||
* [Steven Tappert](mailto:admin@dark-it.net)
 | 
			
		||||
* [Svetlozar Todorov](mailto:svetlik@users.noreply.github.com)
 | 
			
		||||
* [Sébastien Santoro](mailto:dereckson@espace-win.org)
 | 
			
		||||
* [Tad Thorley](mailto:phaedryx@users.noreply.github.com)
 | 
			
		||||
@ -521,11 +535,13 @@ and provided thanks to the work of the following contributors:
 | 
			
		||||
* [jacob](mailto:jacobherringtondeveloper@gmail.com)
 | 
			
		||||
* [jenn kaplan](mailto:me@jkap.io)
 | 
			
		||||
* [jirayudech](mailto:jirayudech@gmail.com)
 | 
			
		||||
* [jomo](mailto:github@jomo.tv)
 | 
			
		||||
* [jooops](mailto:joops@autistici.org)
 | 
			
		||||
* [jukper](mailto:jukkaperanto@gmail.com)
 | 
			
		||||
* [jumoru](mailto:jumoru@mailbox.org)
 | 
			
		||||
* [karlyeurl](mailto:karl.yeurl@gmail.com)
 | 
			
		||||
* [kedama](mailto:32974885+kedamadq@users.noreply.github.com)
 | 
			
		||||
* [kodai](mailto:shirafuta.kodai@gmail.com)
 | 
			
		||||
* [kuro5hin](mailto:rusty@kuro5hin.org)
 | 
			
		||||
* [luzpaz](mailto:luzpaz@users.noreply.github.com)
 | 
			
		||||
* [maxypy](mailto:maxime@mpigou.fr)
 | 
			
		||||
@ -533,6 +549,7 @@ and provided thanks to the work of the following contributors:
 | 
			
		||||
* [mimikun](mailto:dzdzble_effort_311@outlook.jp)
 | 
			
		||||
* [mshrtkch](mailto:mshrtkch@users.noreply.github.com)
 | 
			
		||||
* [muan](mailto:muan@github.com)
 | 
			
		||||
* [namelessGonbai](mailto:43787036+namelessgonbai@users.noreply.github.com)
 | 
			
		||||
* [neetshin](mailto:neetshin@neetsh.in)
 | 
			
		||||
* [nightpool](mailto:nightpool@users.noreply.github.com)
 | 
			
		||||
* [rch850](mailto:rich850@gmail.com)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										105
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										105
									
								
								CHANGELOG.md
									
									
									
									
									
								
							@ -3,6 +3,111 @@ Changelog
 | 
			
		||||
 | 
			
		||||
All notable changes to this project will be documented in this file.
 | 
			
		||||
 | 
			
		||||
## [Unreleased]
 | 
			
		||||
### Added
 | 
			
		||||
 | 
			
		||||
- Add link for adding a user to a list from their profile (#9062)
 | 
			
		||||
- Add joining several hashtags in a single column (#8904)
 | 
			
		||||
- Add volume sliders for videos (#9366)
 | 
			
		||||
- Add a tooltip explaining what a locked account is (#9403)
 | 
			
		||||
- Add preloaded cache for common JSON-LD contexts (#9412)
 | 
			
		||||
- Add profile directory (#9427)
 | 
			
		||||
- Add setting to not group reblogs in home feed (#9248)
 | 
			
		||||
- Add admin ability to remove a user's header image (#9495)
 | 
			
		||||
- Add account hashtags to ActivityPub actor JSON (#9450)
 | 
			
		||||
- Add error message for avatar image that's too large (#9518)
 | 
			
		||||
- Add notification quick-filter bar (#9399)
 | 
			
		||||
- Add new first-time tutorial (#9531)
 | 
			
		||||
- Add moderation warnings (#9519)
 | 
			
		||||
- Add emoji codepoint mappings for v11.0 (#9618)
 | 
			
		||||
- Add REST API for creating an account (#9572)
 | 
			
		||||
- Add support for Malayalam in language filter (#9624)
 | 
			
		||||
- Add exclude_reblogs option to account statuses API (#9640)
 | 
			
		||||
- Add local followers page to admin account UI (#9610)
 | 
			
		||||
- Add healthcheck commands to docker-compose.yml (#9143)
 | 
			
		||||
- Add handler for Move activity to migrate followers (#9629)
 | 
			
		||||
- Add CSV export for lists and domain blocks (#9677)
 | 
			
		||||
- Add `tootctl accounts follow ACCT` (#9414)
 | 
			
		||||
- Add scheduled statuses (#9706)
 | 
			
		||||
- Add immutable caching for S3 objects (#9722)
 | 
			
		||||
- Add cache to custom emojis API (#9732)
 | 
			
		||||
- Add preview cards to non-detailed statuses on public pages (#9714)
 | 
			
		||||
- Add `mod` and `moderator` to list of default reserved usernames (#9713)
 | 
			
		||||
- Add quick links to the admin interface in the web UI (#8545)
 | 
			
		||||
 | 
			
		||||
### Changed
 | 
			
		||||
 | 
			
		||||
- Temporarily pause timeline if mouse moved recently (#9200)
 | 
			
		||||
- Change the password form order (#9267)
 | 
			
		||||
- Redesign admin UI for accounts (#9340, #9643)
 | 
			
		||||
- Redesign admin UI for instances/domain blocks (#9645)
 | 
			
		||||
- Swap avatar and header input fields in profile page (#9271)
 | 
			
		||||
- When posting in mobile mode, go back to previous history location (#9502)
 | 
			
		||||
- Split out is_changing_upload from is_submitting (#9536)
 | 
			
		||||
- Back to the getting-started when pins the timeline. (#9561)
 | 
			
		||||
- Allow unauthenticated REST API access to GET /api/v1/accounts/:id/statuses (#9573)
 | 
			
		||||
- Limit maximum visibility of local silenced users to unlisted (#9583)
 | 
			
		||||
- Change API error message for unconfirmed accounts (#9625)
 | 
			
		||||
- Change the icon to "reply-all" when it's a reply to other accounts (#9378)
 | 
			
		||||
- Do not ignore federated reports targetting already-reported accounts (#9534)
 | 
			
		||||
- Upgrade default Ruby version to 2.6.0 (#9688)
 | 
			
		||||
- Change e-mail digest frequency (#9689)
 | 
			
		||||
- Change Docker images for Tor support in docker-compose.yml (#9438)
 | 
			
		||||
- Display fallback link card thumbnail when none is given (#9715)
 | 
			
		||||
- Change account bio length validation to ignore mention domains and URLs (#9717)
 | 
			
		||||
- Use configured contact user for "anonymous" federation activities (#9661)
 | 
			
		||||
- Change remote interaction dialog to use specific actions instead of generic "interact" (#9743)
 | 
			
		||||
- Always re-fetch public key when signature verification fails to support blind key rotation (#9667)
 | 
			
		||||
- Make replies to boosts impossible, connect reply to original status instead (#9129)
 | 
			
		||||
- Change e-mail MX validation to check both A and MX records against blacklist (#9489)
 | 
			
		||||
 | 
			
		||||
### Removed
 | 
			
		||||
 | 
			
		||||
- Remove links to bridge.joinmastodon.org (non-functional) (#9608)
 | 
			
		||||
- Remove LD-Signatures from activities that do not need them (#9659)
 | 
			
		||||
 | 
			
		||||
### Fixed
 | 
			
		||||
 | 
			
		||||
- Remove unused computation of reblog references from updateTimeline (#9244)
 | 
			
		||||
- Fix loaded embeds resetting if a status arrives from API again (#9270)
 | 
			
		||||
- Fix race condition causing shallow status with only a "favourited" attribute (#9272)
 | 
			
		||||
- Remove intermediary arrays when creating hash maps from results (#9291)
 | 
			
		||||
- Extract counters from accounts table to account_stats table to improve performance (#9295)
 | 
			
		||||
- Change identities id column to a bigint (#9371)
 | 
			
		||||
- Fix conversations API pagination (#9407)
 | 
			
		||||
- Improve account suspension speed and completeness (#9290)
 | 
			
		||||
- Fix thread depth computation in statuses_controller (#9426)
 | 
			
		||||
- Fix database deadlocks by moving account stats update outside transaction (#9437)
 | 
			
		||||
- Escape HTML in profile name preview in profile settings (#9446)
 | 
			
		||||
- Use same CORS policy for /@:username and /users/:username (#9485)
 | 
			
		||||
- Make custom emoji domains case insensitive (#9474)
 | 
			
		||||
- Various fixes to scrollable lists and media gallery (#9501)
 | 
			
		||||
- Fix bootsnap cache directory being declared relatively (#9511)
 | 
			
		||||
- Fix timeline pagination in the web UI (#9516)
 | 
			
		||||
- Fix padding on dropdown elements in preferences (#9517)
 | 
			
		||||
- Make avatar and headers respect GIF autoplay settings (#9515)
 | 
			
		||||
- Do no retry Web Push workers if the server returns a 4xx response (#9434)
 | 
			
		||||
- Minor scrollable list fixes (#9551)
 | 
			
		||||
- Ignore low-confidence CharlockHolmes guesses when parsing link cards (#9510)
 | 
			
		||||
- Fix `tootctl accounts rotate` not updating public keys (#9556)
 | 
			
		||||
- Fix CSP / X-Frame-Options for media players (#9558)
 | 
			
		||||
- Fix unnecessary loadMore calls when the end of a timeline has been reached (#9581)
 | 
			
		||||
- Skip mailer job retries when a record no longer exists (#9590)
 | 
			
		||||
- Fix composer not getting focus after reply confirmation dialog (#9602)
 | 
			
		||||
- Fix signature verification stoplight triggering on non-timeout errors (#9617)
 | 
			
		||||
- Fix ThreadResolveWorker getting queued with invalid URLs (#9628)
 | 
			
		||||
- Fix crash when clearing uninitialized timeline (#9662)
 | 
			
		||||
- Avoid duplicate work by merging ReplyDistributionWorker into DistributionWorker (#9660)
 | 
			
		||||
- Skip full text search if it fails, instead of erroring out completely (#9654)
 | 
			
		||||
- Fix profile metadata links not verifying correctly sometimes (#9673)
 | 
			
		||||
- Ensure blocked user unfollows blocker if Block/Undo-Block activities are processed out of order (#9687)
 | 
			
		||||
- Fix unreadable text color in report modal for some statuses (#9716)
 | 
			
		||||
- Stop GIFV timeline preview explicitly when it's opened in modal (#9749)
 | 
			
		||||
 | 
			
		||||
### Security
 | 
			
		||||
 | 
			
		||||
- Sanitize and sandbox toot embeds in web UI (#9552)
 | 
			
		||||
 | 
			
		||||
## [2.6.5] - 2018-12-01
 | 
			
		||||
### Changed
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
FROM node:8.12.0-alpine as node
 | 
			
		||||
FROM node:8.14.0-alpine as node
 | 
			
		||||
FROM ruby:2.4.5-alpine3.8
 | 
			
		||||
 | 
			
		||||
LABEL maintainer="https://github.com/tootsuite/mastodon" \
 | 
			
		||||
@ -31,6 +31,8 @@ RUN apk -U upgrade \
 | 
			
		||||
    libidn-dev \
 | 
			
		||||
    libressl \
 | 
			
		||||
    libtool \
 | 
			
		||||
    libxml2-dev \
 | 
			
		||||
    libxslt-dev \
 | 
			
		||||
    postgresql-dev \
 | 
			
		||||
    protobuf-dev \
 | 
			
		||||
    python \
 | 
			
		||||
@ -43,6 +45,8 @@ RUN apk -U upgrade \
 | 
			
		||||
    imagemagick \
 | 
			
		||||
    libidn \
 | 
			
		||||
    libpq \
 | 
			
		||||
    libxml2 \
 | 
			
		||||
    libxslt \
 | 
			
		||||
    protobuf \
 | 
			
		||||
    tini \
 | 
			
		||||
    tzdata \
 | 
			
		||||
@ -64,7 +68,7 @@ RUN apk -U upgrade \
 | 
			
		||||
 | 
			
		||||
COPY Gemfile Gemfile.lock package.json yarn.lock .yarnclean /mastodon/
 | 
			
		||||
 | 
			
		||||
RUN bundle config build.nokogiri --with-iconv-lib=/usr/local/lib --with-iconv-include=/usr/local/include \
 | 
			
		||||
RUN bundle config build.nokogiri --use-system-libraries --with-iconv-lib=/usr/local/lib --with-iconv-include=/usr/local/include \
 | 
			
		||||
 && bundle install -j$(getconf _NPROCESSORS_ONLN) --deployment --without test development \
 | 
			
		||||
 && yarn install --pure-lockfile --ignore-engines \
 | 
			
		||||
 && yarn cache clean
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										36
									
								
								Gemfile
									
									
									
									
									
								
							
							
						
						
									
										36
									
								
								Gemfile
									
									
									
									
									
								
							@ -1,12 +1,12 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
source 'https://rubygems.org'
 | 
			
		||||
ruby '>= 2.3.0', '< 2.6.0'
 | 
			
		||||
ruby '>= 2.4.0', '< 2.7.0'
 | 
			
		||||
 | 
			
		||||
gem 'pkg-config', '~> 1.3'
 | 
			
		||||
 | 
			
		||||
gem 'puma', '~> 3.12'
 | 
			
		||||
gem 'rails', '~> 5.2.1'
 | 
			
		||||
gem 'rails', '~> 5.2.2'
 | 
			
		||||
gem 'thor', '~> 0.20'
 | 
			
		||||
 | 
			
		||||
gem 'hamlit-rails', '~> 0.2'
 | 
			
		||||
@ -15,7 +15,7 @@ gem 'makara', '~> 0.4'
 | 
			
		||||
gem 'pghero', '~> 2.2'
 | 
			
		||||
gem 'dotenv-rails', '~> 2.5'
 | 
			
		||||
 | 
			
		||||
gem 'aws-sdk-s3', '~> 1.23', require: false
 | 
			
		||||
gem 'aws-sdk-s3', '~> 1.30', require: false
 | 
			
		||||
gem 'fog-core', '<= 2.1.0'
 | 
			
		||||
gem 'fog-openstack', '~> 0.3', require: false
 | 
			
		||||
gem 'paperclip', '~> 6.0'
 | 
			
		||||
@ -29,7 +29,7 @@ gem 'browser'
 | 
			
		||||
gem 'charlock_holmes', '~> 0.7.6'
 | 
			
		||||
gem 'iso-639'
 | 
			
		||||
gem 'chewy', '~> 5.0'
 | 
			
		||||
gem 'cld3', '~> 3.2.0'
 | 
			
		||||
gem 'cld3', '~> 3.2.3'
 | 
			
		||||
gem 'devise', '~> 4.5'
 | 
			
		||||
gem 'devise-two-factor', '~> 3.0'
 | 
			
		||||
 | 
			
		||||
@ -40,7 +40,7 @@ end
 | 
			
		||||
gem 'net-ldap', '~> 0.10'
 | 
			
		||||
gem 'omniauth-cas', '~> 1.1'
 | 
			
		||||
gem 'omniauth-saml', '~> 1.10'
 | 
			
		||||
gem 'omniauth', '~> 1.2'
 | 
			
		||||
gem 'omniauth', '~> 1.9'
 | 
			
		||||
 | 
			
		||||
gem 'doorkeeper', '~> 5.0'
 | 
			
		||||
gem 'fast_blank', '~> 1.0'
 | 
			
		||||
@ -52,12 +52,12 @@ gem 'htmlentities', '~> 4.3'
 | 
			
		||||
gem 'http', '~> 3.3'
 | 
			
		||||
gem 'http_accept_language', '~> 2.1'
 | 
			
		||||
gem 'http_parser.rb', '~> 0.6', git: 'https://github.com/tmm1/http_parser.rb', ref: '54b17ba8c7d8d20a16dfc65d1775241833219cf2'
 | 
			
		||||
gem 'httplog', '~> 1.1'
 | 
			
		||||
gem 'httplog', '~> 1.2'
 | 
			
		||||
gem 'idn-ruby', require: 'idn'
 | 
			
		||||
gem 'kaminari', '~> 1.1'
 | 
			
		||||
gem 'link_header', '~> 0.0'
 | 
			
		||||
gem 'mime-types', '~> 3.2', require: 'mime/types/columnar'
 | 
			
		||||
gem 'nokogiri', '~> 1.8'
 | 
			
		||||
gem 'nokogiri', '~> 1.10'
 | 
			
		||||
gem 'nsa', '~> 0.2'
 | 
			
		||||
gem 'oj', '~> 3.7'
 | 
			
		||||
gem 'ostatus2', '~> 2.0'
 | 
			
		||||
@ -69,28 +69,28 @@ gem 'rack-attack', '~> 5.4'
 | 
			
		||||
gem 'rack-cors', '~> 1.0', require: 'rack/cors'
 | 
			
		||||
gem 'rails-i18n', '~> 5.1'
 | 
			
		||||
gem 'rails-settings-cached', '~> 0.6'
 | 
			
		||||
gem 'redis', '~> 4.0', require: ['redis', 'redis/connection/hiredis']
 | 
			
		||||
gem 'redis', '~> 4.1', require: ['redis', 'redis/connection/hiredis']
 | 
			
		||||
gem 'mario-redis-lock', '~> 1.2', require: 'redis_lock'
 | 
			
		||||
gem 'rqrcode', '~> 0.10'
 | 
			
		||||
gem 'sanitize', '~> 5.0'
 | 
			
		||||
gem 'sidekiq', '~> 5.2'
 | 
			
		||||
gem 'sidekiq-scheduler', '~> 3.0'
 | 
			
		||||
gem 'sidekiq-unique-jobs', '~> 5.0'
 | 
			
		||||
gem 'sidekiq-bulk', '~>0.1.1'
 | 
			
		||||
gem 'sidekiq-bulk', '~>0.2.0'
 | 
			
		||||
gem 'simple-navigation', '~> 4.0'
 | 
			
		||||
gem 'simple_form', '~> 4.0'
 | 
			
		||||
gem 'simple_form', '~> 4.1'
 | 
			
		||||
gem 'sprockets-rails', '~> 3.2', require: 'sprockets/railtie'
 | 
			
		||||
gem 'stoplight', '~> 2.1.3'
 | 
			
		||||
gem 'strong_migrations', '~> 0.3'
 | 
			
		||||
gem 'tty-command', '~> 0.8', require: false
 | 
			
		||||
gem 'tty-prompt', '~> 0.17', require: false
 | 
			
		||||
gem 'tty-prompt', '~> 0.18', require: false
 | 
			
		||||
gem 'twitter-text', '~> 1.14'
 | 
			
		||||
gem 'tzinfo-data', '~> 1.2018'
 | 
			
		||||
gem 'webpacker', '~> 3.5'
 | 
			
		||||
gem 'webpush'
 | 
			
		||||
 | 
			
		||||
gem 'json-ld', '~> 2.2'
 | 
			
		||||
gem 'json-ld-preloaded', '~> 2.2'
 | 
			
		||||
gem 'json-ld-preloaded', '~> 3.0'
 | 
			
		||||
gem 'rdf-normalize', '~> 0.3'
 | 
			
		||||
 | 
			
		||||
group :development, :test do
 | 
			
		||||
@ -107,15 +107,15 @@ group :production, :test do
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
group :test do
 | 
			
		||||
  gem 'capybara', '~> 3.10'
 | 
			
		||||
  gem 'capybara', '~> 3.12'
 | 
			
		||||
  gem 'climate_control', '~> 0.2'
 | 
			
		||||
  gem 'faker', '~> 1.9'
 | 
			
		||||
  gem 'microformats', '~> 4.0'
 | 
			
		||||
  gem 'rails-controller-testing', '~> 1.0'
 | 
			
		||||
  gem 'rspec-sidekiq', '~> 3.0'
 | 
			
		||||
  gem 'simplecov', '~> 0.16', require: false
 | 
			
		||||
  gem 'webmock', '~> 3.4'
 | 
			
		||||
  gem 'parallel_tests', '~> 2.26'
 | 
			
		||||
  gem 'webmock', '~> 3.5'
 | 
			
		||||
  gem 'parallel_tests', '~> 2.27'
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
group :development do
 | 
			
		||||
@ -123,11 +123,11 @@ group :development do
 | 
			
		||||
  gem 'annotate', '~> 2.7'
 | 
			
		||||
  gem 'better_errors', '~> 2.5'
 | 
			
		||||
  gem 'binding_of_caller', '~> 0.7'
 | 
			
		||||
  gem 'bullet', '~> 5.7'
 | 
			
		||||
  gem 'letter_opener', '~> 1.4'
 | 
			
		||||
  gem 'bullet', '~> 5.9'
 | 
			
		||||
  gem 'letter_opener', '~> 1.7'
 | 
			
		||||
  gem 'letter_opener_web', '~> 1.3'
 | 
			
		||||
  gem 'memory_profiler'
 | 
			
		||||
  gem 'rubocop', '~> 0.60', require: false
 | 
			
		||||
  gem 'rubocop', '~> 0.62', require: false
 | 
			
		||||
  gem 'brakeman', '~> 4.3', require: false
 | 
			
		||||
  gem 'bundler-audit', '~> 0.6', require: false
 | 
			
		||||
  gem 'scss_lint', '~> 0.57', require: false
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										217
									
								
								Gemfile.lock
									
									
									
									
									
								
							
							
						
						
									
										217
									
								
								Gemfile.lock
									
									
									
									
									
								
							@ -15,49 +15,49 @@ GIT
 | 
			
		||||
GEM
 | 
			
		||||
  remote: https://rubygems.org/
 | 
			
		||||
  specs:
 | 
			
		||||
    actioncable (5.2.1)
 | 
			
		||||
      actionpack (= 5.2.1)
 | 
			
		||||
    actioncable (5.2.2)
 | 
			
		||||
      actionpack (= 5.2.2)
 | 
			
		||||
      nio4r (~> 2.0)
 | 
			
		||||
      websocket-driver (>= 0.6.1)
 | 
			
		||||
    actionmailer (5.2.1)
 | 
			
		||||
      actionpack (= 5.2.1)
 | 
			
		||||
      actionview (= 5.2.1)
 | 
			
		||||
      activejob (= 5.2.1)
 | 
			
		||||
    actionmailer (5.2.2)
 | 
			
		||||
      actionpack (= 5.2.2)
 | 
			
		||||
      actionview (= 5.2.2)
 | 
			
		||||
      activejob (= 5.2.2)
 | 
			
		||||
      mail (~> 2.5, >= 2.5.4)
 | 
			
		||||
      rails-dom-testing (~> 2.0)
 | 
			
		||||
    actionpack (5.2.1)
 | 
			
		||||
      actionview (= 5.2.1)
 | 
			
		||||
      activesupport (= 5.2.1)
 | 
			
		||||
    actionpack (5.2.2)
 | 
			
		||||
      actionview (= 5.2.2)
 | 
			
		||||
      activesupport (= 5.2.2)
 | 
			
		||||
      rack (~> 2.0)
 | 
			
		||||
      rack-test (>= 0.6.3)
 | 
			
		||||
      rails-dom-testing (~> 2.0)
 | 
			
		||||
      rails-html-sanitizer (~> 1.0, >= 1.0.2)
 | 
			
		||||
    actionview (5.2.1)
 | 
			
		||||
      activesupport (= 5.2.1)
 | 
			
		||||
    actionview (5.2.2)
 | 
			
		||||
      activesupport (= 5.2.2)
 | 
			
		||||
      builder (~> 3.1)
 | 
			
		||||
      erubi (~> 1.4)
 | 
			
		||||
      rails-dom-testing (~> 2.0)
 | 
			
		||||
      rails-html-sanitizer (~> 1.0, >= 1.0.3)
 | 
			
		||||
    active_model_serializers (0.10.7)
 | 
			
		||||
    active_model_serializers (0.10.8)
 | 
			
		||||
      actionpack (>= 4.1, < 6)
 | 
			
		||||
      activemodel (>= 4.1, < 6)
 | 
			
		||||
      case_transform (>= 0.2)
 | 
			
		||||
      jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
 | 
			
		||||
    active_record_query_trace (1.5.4)
 | 
			
		||||
    activejob (5.2.1)
 | 
			
		||||
      activesupport (= 5.2.1)
 | 
			
		||||
    activejob (5.2.2)
 | 
			
		||||
      activesupport (= 5.2.2)
 | 
			
		||||
      globalid (>= 0.3.6)
 | 
			
		||||
    activemodel (5.2.1)
 | 
			
		||||
      activesupport (= 5.2.1)
 | 
			
		||||
    activerecord (5.2.1)
 | 
			
		||||
      activemodel (= 5.2.1)
 | 
			
		||||
      activesupport (= 5.2.1)
 | 
			
		||||
    activemodel (5.2.2)
 | 
			
		||||
      activesupport (= 5.2.2)
 | 
			
		||||
    activerecord (5.2.2)
 | 
			
		||||
      activemodel (= 5.2.2)
 | 
			
		||||
      activesupport (= 5.2.2)
 | 
			
		||||
      arel (>= 9.0)
 | 
			
		||||
    activestorage (5.2.1)
 | 
			
		||||
      actionpack (= 5.2.1)
 | 
			
		||||
      activerecord (= 5.2.1)
 | 
			
		||||
    activestorage (5.2.2)
 | 
			
		||||
      actionpack (= 5.2.2)
 | 
			
		||||
      activerecord (= 5.2.2)
 | 
			
		||||
      marcel (~> 0.3.1)
 | 
			
		||||
    activesupport (5.2.1)
 | 
			
		||||
    activesupport (5.2.2)
 | 
			
		||||
      concurrent-ruby (~> 1.0, >= 1.0.2)
 | 
			
		||||
      i18n (>= 0.7, < 2)
 | 
			
		||||
      minitest (~> 5.1)
 | 
			
		||||
@ -76,17 +76,17 @@ GEM
 | 
			
		||||
    av (0.9.0)
 | 
			
		||||
      cocaine (~> 0.5.3)
 | 
			
		||||
    aws-eventstream (1.0.1)
 | 
			
		||||
    aws-partitions (1.106.0)
 | 
			
		||||
    aws-sdk-core (3.35.0)
 | 
			
		||||
    aws-partitions (1.122.0)
 | 
			
		||||
    aws-sdk-core (3.43.0)
 | 
			
		||||
      aws-eventstream (~> 1.0)
 | 
			
		||||
      aws-partitions (~> 1.0)
 | 
			
		||||
      aws-sigv4 (~> 1.0)
 | 
			
		||||
      jmespath (~> 1.0)
 | 
			
		||||
    aws-sdk-kms (1.11.0)
 | 
			
		||||
      aws-sdk-core (~> 3, >= 3.26.0)
 | 
			
		||||
    aws-sdk-kms (1.13.0)
 | 
			
		||||
      aws-sdk-core (~> 3, >= 3.39.0)
 | 
			
		||||
      aws-sigv4 (~> 1.0)
 | 
			
		||||
    aws-sdk-s3 (1.23.0)
 | 
			
		||||
      aws-sdk-core (~> 3, >= 3.26.0)
 | 
			
		||||
    aws-sdk-s3 (1.30.0)
 | 
			
		||||
      aws-sdk-core (~> 3, >= 3.39.0)
 | 
			
		||||
      aws-sdk-kms (~> 1)
 | 
			
		||||
      aws-sigv4 (~> 1.0)
 | 
			
		||||
    aws-sigv4 (1.0.3)
 | 
			
		||||
@ -103,9 +103,9 @@ GEM
 | 
			
		||||
    brakeman (4.3.1)
 | 
			
		||||
    browser (2.5.3)
 | 
			
		||||
    builder (3.2.3)
 | 
			
		||||
    bullet (5.7.6)
 | 
			
		||||
    bullet (5.9.0)
 | 
			
		||||
      activesupport (>= 3.0.0)
 | 
			
		||||
      uniform_notifier (~> 1.11.0)
 | 
			
		||||
      uniform_notifier (~> 1.11)
 | 
			
		||||
    bundler-audit (0.6.0)
 | 
			
		||||
      bundler (~> 1.2)
 | 
			
		||||
      thor (~> 0.18)
 | 
			
		||||
@ -126,7 +126,7 @@ GEM
 | 
			
		||||
      sshkit (~> 1.3)
 | 
			
		||||
    capistrano-yarn (2.0.2)
 | 
			
		||||
      capistrano (~> 3.0)
 | 
			
		||||
    capybara (3.10.0)
 | 
			
		||||
    capybara (3.12.0)
 | 
			
		||||
      addressable
 | 
			
		||||
      mini_mime (>= 0.1.3)
 | 
			
		||||
      nokogiri (~> 1.8)
 | 
			
		||||
@ -142,7 +142,7 @@ GEM
 | 
			
		||||
      elasticsearch (>= 2.0.0)
 | 
			
		||||
      elasticsearch-dsl
 | 
			
		||||
    chunky_png (1.3.10)
 | 
			
		||||
    cld3 (3.2.2)
 | 
			
		||||
    cld3 (3.2.3)
 | 
			
		||||
      ffi (>= 1.1.0, < 1.10.0)
 | 
			
		||||
    climate_control (0.2.0)
 | 
			
		||||
    cocaine (0.5.8)
 | 
			
		||||
@ -210,7 +210,7 @@ GEM
 | 
			
		||||
    faraday (0.15.0)
 | 
			
		||||
      multipart-post (>= 1.2, < 3)
 | 
			
		||||
    fast_blank (1.0.0)
 | 
			
		||||
    fastimage (2.1.4)
 | 
			
		||||
    fastimage (2.1.5)
 | 
			
		||||
    ffi (1.9.25)
 | 
			
		||||
    fog-core (2.1.0)
 | 
			
		||||
      builder
 | 
			
		||||
@ -251,11 +251,10 @@ GEM
 | 
			
		||||
    hamster (3.0.0)
 | 
			
		||||
      concurrent-ruby (~> 1.0)
 | 
			
		||||
    hashdiff (0.3.7)
 | 
			
		||||
    hashie (3.5.7)
 | 
			
		||||
    hashie (3.6.0)
 | 
			
		||||
    heapy (0.1.4)
 | 
			
		||||
    highline (2.0.0)
 | 
			
		||||
    hiredis (0.6.1)
 | 
			
		||||
    hitimes (1.3.0)
 | 
			
		||||
    hiredis (0.6.3)
 | 
			
		||||
    hkdf (0.3.0)
 | 
			
		||||
    htmlentities (4.3.4)
 | 
			
		||||
    http (3.3.0)
 | 
			
		||||
@ -267,7 +266,7 @@ GEM
 | 
			
		||||
      domain_name (~> 0.5)
 | 
			
		||||
    http-form_data (2.1.1)
 | 
			
		||||
    http_accept_language (2.1.1)
 | 
			
		||||
    httplog (1.1.1)
 | 
			
		||||
    httplog (1.2.0)
 | 
			
		||||
      rack (>= 1.0)
 | 
			
		||||
      rainbow (>= 2.0.0)
 | 
			
		||||
    i18n (1.1.1)
 | 
			
		||||
@ -291,10 +290,10 @@ GEM
 | 
			
		||||
    json-ld (2.2.1)
 | 
			
		||||
      multi_json (~> 1.12)
 | 
			
		||||
      rdf (>= 2.2.8, < 4.0)
 | 
			
		||||
    json-ld-preloaded (2.2.3)
 | 
			
		||||
    json-ld-preloaded (3.0.0)
 | 
			
		||||
      json-ld (>= 2.2, < 4.0)
 | 
			
		||||
      multi_json (~> 1.12)
 | 
			
		||||
      rdf (>= 2.2, < 4.0)
 | 
			
		||||
      rdf (~> 3.0)
 | 
			
		||||
    jsonapi-renderer (0.2.0)
 | 
			
		||||
    jwt (2.1.0)
 | 
			
		||||
    kaminari (1.1.1)
 | 
			
		||||
@ -311,7 +310,7 @@ GEM
 | 
			
		||||
    kaminari-core (1.1.1)
 | 
			
		||||
    launchy (2.4.3)
 | 
			
		||||
      addressable (~> 2.3)
 | 
			
		||||
    letter_opener (1.6.0)
 | 
			
		||||
    letter_opener (1.7.0)
 | 
			
		||||
      launchy (~> 2.2)
 | 
			
		||||
    letter_opener_web (1.3.4)
 | 
			
		||||
      actionmailer (>= 3.2)
 | 
			
		||||
@ -326,16 +325,16 @@ GEM
 | 
			
		||||
    loofah (2.2.3)
 | 
			
		||||
      crass (~> 1.0.2)
 | 
			
		||||
      nokogiri (>= 1.5.9)
 | 
			
		||||
    mail (2.7.0)
 | 
			
		||||
    mail (2.7.1)
 | 
			
		||||
      mini_mime (>= 0.1.1)
 | 
			
		||||
    makara (0.4.0)
 | 
			
		||||
      activerecord (>= 3.0.0)
 | 
			
		||||
    marcel (0.3.2)
 | 
			
		||||
    marcel (0.3.3)
 | 
			
		||||
      mimemagic (~> 0.3.2)
 | 
			
		||||
    mario-redis-lock (1.2.1)
 | 
			
		||||
      redis (>= 3.0.5)
 | 
			
		||||
    memory_profiler (0.9.12)
 | 
			
		||||
    method_source (0.9.0)
 | 
			
		||||
    method_source (0.9.2)
 | 
			
		||||
    microformats (4.0.7)
 | 
			
		||||
      json
 | 
			
		||||
      nokogiri
 | 
			
		||||
@ -344,7 +343,7 @@ GEM
 | 
			
		||||
    mime-types-data (3.2018.0812)
 | 
			
		||||
    mimemagic (0.3.2)
 | 
			
		||||
    mini_mime (1.0.1)
 | 
			
		||||
    mini_portile2 (2.3.0)
 | 
			
		||||
    mini_portile2 (2.4.0)
 | 
			
		||||
    minitest (5.11.3)
 | 
			
		||||
    msgpack (1.2.4)
 | 
			
		||||
    multi_json (1.13.1)
 | 
			
		||||
@ -355,8 +354,8 @@ GEM
 | 
			
		||||
      net-ssh (>= 2.6.5)
 | 
			
		||||
    net-ssh (5.0.2)
 | 
			
		||||
    nio4r (2.3.1)
 | 
			
		||||
    nokogiri (1.8.5)
 | 
			
		||||
      mini_portile2 (~> 2.3.0)
 | 
			
		||||
    nokogiri (1.10.0)
 | 
			
		||||
      mini_portile2 (~> 2.4.0)
 | 
			
		||||
    nokogumbo (2.0.0)
 | 
			
		||||
      nokogiri (~> 1.8, >= 1.8.4)
 | 
			
		||||
    nsa (0.2.4)
 | 
			
		||||
@ -364,9 +363,9 @@ GEM
 | 
			
		||||
      concurrent-ruby (~> 1.0.0)
 | 
			
		||||
      sidekiq (>= 3.5.0)
 | 
			
		||||
      statsd-ruby (~> 1.2.0)
 | 
			
		||||
    oj (3.7.0)
 | 
			
		||||
    omniauth (1.8.1)
 | 
			
		||||
      hashie (>= 3.4.6, < 3.6.0)
 | 
			
		||||
    oj (3.7.6)
 | 
			
		||||
    omniauth (1.9.0)
 | 
			
		||||
      hashie (>= 3.4.6, < 3.7.0)
 | 
			
		||||
      rack (>= 1.6.2, < 3)
 | 
			
		||||
    omniauth-cas (1.1.1)
 | 
			
		||||
      addressable (~> 2.3)
 | 
			
		||||
@ -391,7 +390,7 @@ GEM
 | 
			
		||||
      av (~> 0.9.0)
 | 
			
		||||
      paperclip (>= 2.5.2)
 | 
			
		||||
    parallel (1.12.1)
 | 
			
		||||
    parallel_tests (2.26.0)
 | 
			
		||||
    parallel_tests (2.27.1)
 | 
			
		||||
      parallel
 | 
			
		||||
    parser (2.5.3.0)
 | 
			
		||||
      ast (~> 2.4.0)
 | 
			
		||||
@ -401,7 +400,7 @@ GEM
 | 
			
		||||
    pg (1.1.3)
 | 
			
		||||
    pghero (2.2.0)
 | 
			
		||||
      activerecord
 | 
			
		||||
    pkg-config (1.3.1)
 | 
			
		||||
    pkg-config (1.3.2)
 | 
			
		||||
    powerpack (0.1.2)
 | 
			
		||||
    premailer (1.11.1)
 | 
			
		||||
      addressable
 | 
			
		||||
@ -411,21 +410,21 @@ GEM
 | 
			
		||||
      actionmailer (>= 3, < 6)
 | 
			
		||||
      premailer (~> 1.7, >= 1.7.9)
 | 
			
		||||
    private_address_check (0.5.0)
 | 
			
		||||
    pry (0.11.3)
 | 
			
		||||
    pry (0.12.2)
 | 
			
		||||
      coderay (~> 1.1.0)
 | 
			
		||||
      method_source (~> 0.9.0)
 | 
			
		||||
    pry-byebug (3.6.0)
 | 
			
		||||
      byebug (~> 10.0)
 | 
			
		||||
      pry (~> 0.10)
 | 
			
		||||
    pry-rails (0.3.6)
 | 
			
		||||
    pry-rails (0.3.9)
 | 
			
		||||
      pry (>= 0.10.4)
 | 
			
		||||
    public_suffix (3.0.3)
 | 
			
		||||
    puma (3.12.0)
 | 
			
		||||
    pundit (2.0.0)
 | 
			
		||||
      activesupport (>= 3.0.0)
 | 
			
		||||
    raabro (1.1.6)
 | 
			
		||||
    rack (2.0.5)
 | 
			
		||||
    rack-attack (5.4.1)
 | 
			
		||||
    rack (2.0.6)
 | 
			
		||||
    rack-attack (5.4.2)
 | 
			
		||||
      rack (>= 1.0, < 3)
 | 
			
		||||
    rack-cors (1.0.2)
 | 
			
		||||
    rack-protection (2.0.4)
 | 
			
		||||
@ -434,23 +433,23 @@ GEM
 | 
			
		||||
      rack
 | 
			
		||||
    rack-test (1.1.0)
 | 
			
		||||
      rack (>= 1.0, < 3)
 | 
			
		||||
    rails (5.2.1)
 | 
			
		||||
      actioncable (= 5.2.1)
 | 
			
		||||
      actionmailer (= 5.2.1)
 | 
			
		||||
      actionpack (= 5.2.1)
 | 
			
		||||
      actionview (= 5.2.1)
 | 
			
		||||
      activejob (= 5.2.1)
 | 
			
		||||
      activemodel (= 5.2.1)
 | 
			
		||||
      activerecord (= 5.2.1)
 | 
			
		||||
      activestorage (= 5.2.1)
 | 
			
		||||
      activesupport (= 5.2.1)
 | 
			
		||||
    rails (5.2.2)
 | 
			
		||||
      actioncable (= 5.2.2)
 | 
			
		||||
      actionmailer (= 5.2.2)
 | 
			
		||||
      actionpack (= 5.2.2)
 | 
			
		||||
      actionview (= 5.2.2)
 | 
			
		||||
      activejob (= 5.2.2)
 | 
			
		||||
      activemodel (= 5.2.2)
 | 
			
		||||
      activerecord (= 5.2.2)
 | 
			
		||||
      activestorage (= 5.2.2)
 | 
			
		||||
      activesupport (= 5.2.2)
 | 
			
		||||
      bundler (>= 1.3.0)
 | 
			
		||||
      railties (= 5.2.1)
 | 
			
		||||
      railties (= 5.2.2)
 | 
			
		||||
      sprockets-rails (>= 2.0.0)
 | 
			
		||||
    rails-controller-testing (1.0.2)
 | 
			
		||||
      actionpack (~> 5.x, >= 5.0.1)
 | 
			
		||||
      actionview (~> 5.x, >= 5.0.1)
 | 
			
		||||
      activesupport (~> 5.x)
 | 
			
		||||
    rails-controller-testing (1.0.4)
 | 
			
		||||
      actionpack (>= 5.0.1.x)
 | 
			
		||||
      actionview (>= 5.0.1.x)
 | 
			
		||||
      activesupport (>= 5.0.1.x)
 | 
			
		||||
    rails-dom-testing (2.0.3)
 | 
			
		||||
      activesupport (>= 4.2.0)
 | 
			
		||||
      nokogiri (>= 1.6)
 | 
			
		||||
@ -461,9 +460,9 @@ GEM
 | 
			
		||||
      railties (>= 5.0, < 6)
 | 
			
		||||
    rails-settings-cached (0.6.6)
 | 
			
		||||
      rails (>= 4.2.0)
 | 
			
		||||
    railties (5.2.1)
 | 
			
		||||
      actionpack (= 5.2.1)
 | 
			
		||||
      activesupport (= 5.2.1)
 | 
			
		||||
    railties (5.2.2)
 | 
			
		||||
      actionpack (= 5.2.2)
 | 
			
		||||
      activesupport (= 5.2.2)
 | 
			
		||||
      method_source
 | 
			
		||||
      rake (>= 0.8.7)
 | 
			
		||||
      thor (>= 0.19.0, < 2.0)
 | 
			
		||||
@ -472,12 +471,12 @@ GEM
 | 
			
		||||
    rb-fsevent (0.10.3)
 | 
			
		||||
    rb-inotify (0.9.10)
 | 
			
		||||
      ffi (>= 0.5.0, < 2)
 | 
			
		||||
    rdf (3.0.2)
 | 
			
		||||
    rdf (3.0.7)
 | 
			
		||||
      hamster (~> 3.0)
 | 
			
		||||
      link_header (~> 0.0, >= 0.0.8)
 | 
			
		||||
    rdf-normalize (0.3.3)
 | 
			
		||||
      rdf (>= 2.2, < 4.0)
 | 
			
		||||
    redis (4.0.2)
 | 
			
		||||
    redis (4.1.0)
 | 
			
		||||
    redis-actionpack (5.0.2)
 | 
			
		||||
      actionpack (>= 4.0, < 6)
 | 
			
		||||
      redis-rack (>= 1, < 3)
 | 
			
		||||
@ -496,7 +495,7 @@ GEM
 | 
			
		||||
      redis-store (>= 1.2, < 2)
 | 
			
		||||
    redis-store (1.5.0)
 | 
			
		||||
      redis (>= 2.2, < 5)
 | 
			
		||||
    regexp_parser (1.2.0)
 | 
			
		||||
    regexp_parser (1.3.0)
 | 
			
		||||
    request_store (1.4.1)
 | 
			
		||||
      rack (>= 1.4)
 | 
			
		||||
    responders (2.4.0)
 | 
			
		||||
@ -526,7 +525,7 @@ GEM
 | 
			
		||||
      rspec-core (~> 3.0, >= 3.0.0)
 | 
			
		||||
      sidekiq (>= 2.4.0)
 | 
			
		||||
    rspec-support (3.8.0)
 | 
			
		||||
    rubocop (0.60.0)
 | 
			
		||||
    rubocop (0.62.0)
 | 
			
		||||
      jaro_winkler (~> 1.5.1)
 | 
			
		||||
      parallel (~> 1.10)
 | 
			
		||||
      parser (>= 2.5, != 2.5.1.1)
 | 
			
		||||
@ -552,12 +551,11 @@ GEM
 | 
			
		||||
    scss_lint (0.57.1)
 | 
			
		||||
      rake (>= 0.9, < 13)
 | 
			
		||||
      sass (~> 3.5, >= 3.5.5)
 | 
			
		||||
    sidekiq (5.2.2)
 | 
			
		||||
    sidekiq (5.2.3)
 | 
			
		||||
      connection_pool (~> 2.2, >= 2.2.2)
 | 
			
		||||
      rack-protection (>= 1.5.0)
 | 
			
		||||
      redis (>= 3.3.5, < 5)
 | 
			
		||||
    sidekiq-bulk (0.1.1)
 | 
			
		||||
      activesupport
 | 
			
		||||
    sidekiq-bulk (0.2.0)
 | 
			
		||||
      sidekiq
 | 
			
		||||
    sidekiq-scheduler (3.0.0)
 | 
			
		||||
      redis (>= 3, < 5)
 | 
			
		||||
@ -569,7 +567,7 @@ GEM
 | 
			
		||||
      thor (~> 0)
 | 
			
		||||
    simple-navigation (4.0.5)
 | 
			
		||||
      activesupport (>= 2.3.2)
 | 
			
		||||
    simple_form (4.0.1)
 | 
			
		||||
    simple_form (4.1.0)
 | 
			
		||||
      actionpack (>= 5.0)
 | 
			
		||||
      activemodel (>= 5.0)
 | 
			
		||||
    simplecov (0.16.1)
 | 
			
		||||
@ -599,22 +597,21 @@ GEM
 | 
			
		||||
      unicode-display_width (~> 1.1, >= 1.1.1)
 | 
			
		||||
    terrapin (0.6.0)
 | 
			
		||||
      climate_control (>= 0.0.3, < 1.0)
 | 
			
		||||
    thor (0.20.0)
 | 
			
		||||
    thor (0.20.3)
 | 
			
		||||
    thread_safe (0.3.6)
 | 
			
		||||
    tilt (2.0.8)
 | 
			
		||||
    timers (4.1.2)
 | 
			
		||||
      hitimes
 | 
			
		||||
    timers (4.2.0)
 | 
			
		||||
    tty-color (0.4.3)
 | 
			
		||||
    tty-command (0.8.2)
 | 
			
		||||
      pastel (~> 0.7.0)
 | 
			
		||||
    tty-cursor (0.6.0)
 | 
			
		||||
    tty-prompt (0.17.1)
 | 
			
		||||
    tty-prompt (0.18.1)
 | 
			
		||||
      necromancer (~> 0.4.0)
 | 
			
		||||
      pastel (~> 0.7.0)
 | 
			
		||||
      timers (~> 4.0)
 | 
			
		||||
      tty-cursor (~> 0.6.0)
 | 
			
		||||
      tty-reader (~> 0.4.0)
 | 
			
		||||
    tty-reader (0.4.0)
 | 
			
		||||
      tty-reader (~> 0.5.0)
 | 
			
		||||
    tty-reader (0.5.0)
 | 
			
		||||
      tty-cursor (~> 0.6.0)
 | 
			
		||||
      tty-screen (~> 0.6.4)
 | 
			
		||||
      wisper (~> 2.0.0)
 | 
			
		||||
@ -623,16 +620,16 @@ GEM
 | 
			
		||||
      unf (~> 0.1.0)
 | 
			
		||||
    tzinfo (1.2.5)
 | 
			
		||||
      thread_safe (~> 0.1)
 | 
			
		||||
    tzinfo-data (1.2018.7)
 | 
			
		||||
    tzinfo-data (1.2018.9)
 | 
			
		||||
      tzinfo (>= 1.0.0)
 | 
			
		||||
    unf (0.1.4)
 | 
			
		||||
      unf_ext
 | 
			
		||||
    unf_ext (0.0.7.5)
 | 
			
		||||
    unicode-display_width (1.4.0)
 | 
			
		||||
    uniform_notifier (1.11.0)
 | 
			
		||||
    unicode-display_width (1.4.1)
 | 
			
		||||
    uniform_notifier (1.12.1)
 | 
			
		||||
    warden (1.2.7)
 | 
			
		||||
      rack (>= 1.0)
 | 
			
		||||
    webmock (3.4.2)
 | 
			
		||||
    webmock (3.5.1)
 | 
			
		||||
      addressable (>= 2.3.6)
 | 
			
		||||
      crack (>= 0.3.2)
 | 
			
		||||
      hashdiff
 | 
			
		||||
@ -640,7 +637,7 @@ GEM
 | 
			
		||||
      activesupport (>= 4.2)
 | 
			
		||||
      rack-proxy (>= 0.6.1)
 | 
			
		||||
      railties (>= 4.2)
 | 
			
		||||
    webpush (0.3.4)
 | 
			
		||||
    webpush (0.3.5)
 | 
			
		||||
      hkdf (~> 0.2)
 | 
			
		||||
      jwt (~> 2.0)
 | 
			
		||||
    websocket-driver (0.7.0)
 | 
			
		||||
@ -658,22 +655,22 @@ DEPENDENCIES
 | 
			
		||||
  active_record_query_trace (~> 1.5)
 | 
			
		||||
  addressable (~> 2.5)
 | 
			
		||||
  annotate (~> 2.7)
 | 
			
		||||
  aws-sdk-s3 (~> 1.23)
 | 
			
		||||
  aws-sdk-s3 (~> 1.30)
 | 
			
		||||
  better_errors (~> 2.5)
 | 
			
		||||
  binding_of_caller (~> 0.7)
 | 
			
		||||
  bootsnap (~> 1.3)
 | 
			
		||||
  brakeman (~> 4.3)
 | 
			
		||||
  browser
 | 
			
		||||
  bullet (~> 5.7)
 | 
			
		||||
  bullet (~> 5.9)
 | 
			
		||||
  bundler-audit (~> 0.6)
 | 
			
		||||
  capistrano (~> 3.11)
 | 
			
		||||
  capistrano-rails (~> 1.4)
 | 
			
		||||
  capistrano-rbenv (~> 2.1)
 | 
			
		||||
  capistrano-yarn (~> 2.0)
 | 
			
		||||
  capybara (~> 3.10)
 | 
			
		||||
  capybara (~> 3.12)
 | 
			
		||||
  charlock_holmes (~> 0.7.6)
 | 
			
		||||
  chewy (~> 5.0)
 | 
			
		||||
  cld3 (~> 3.2.0)
 | 
			
		||||
  cld3 (~> 3.2.3)
 | 
			
		||||
  climate_control (~> 0.2)
 | 
			
		||||
  derailed_benchmarks
 | 
			
		||||
  devise (~> 4.5)
 | 
			
		||||
@ -695,14 +692,14 @@ DEPENDENCIES
 | 
			
		||||
  http (~> 3.3)
 | 
			
		||||
  http_accept_language (~> 2.1)
 | 
			
		||||
  http_parser.rb (~> 0.6)!
 | 
			
		||||
  httplog (~> 1.1)
 | 
			
		||||
  httplog (~> 1.2)
 | 
			
		||||
  i18n-tasks (~> 0.9)
 | 
			
		||||
  idn-ruby
 | 
			
		||||
  iso-639
 | 
			
		||||
  json-ld (~> 2.2)
 | 
			
		||||
  json-ld-preloaded (~> 2.2)
 | 
			
		||||
  json-ld-preloaded (~> 3.0)
 | 
			
		||||
  kaminari (~> 1.1)
 | 
			
		||||
  letter_opener (~> 1.4)
 | 
			
		||||
  letter_opener (~> 1.7)
 | 
			
		||||
  letter_opener_web (~> 1.3)
 | 
			
		||||
  link_header (~> 0.0)
 | 
			
		||||
  lograge (~> 0.10)
 | 
			
		||||
@ -712,17 +709,17 @@ DEPENDENCIES
 | 
			
		||||
  microformats (~> 4.0)
 | 
			
		||||
  mime-types (~> 3.2)
 | 
			
		||||
  net-ldap (~> 0.10)
 | 
			
		||||
  nokogiri (~> 1.8)
 | 
			
		||||
  nokogiri (~> 1.10)
 | 
			
		||||
  nsa (~> 0.2)
 | 
			
		||||
  oj (~> 3.7)
 | 
			
		||||
  omniauth (~> 1.2)
 | 
			
		||||
  omniauth (~> 1.9)
 | 
			
		||||
  omniauth-cas (~> 1.1)
 | 
			
		||||
  omniauth-saml (~> 1.10)
 | 
			
		||||
  ostatus2 (~> 2.0)
 | 
			
		||||
  ox (~> 2.10)
 | 
			
		||||
  paperclip (~> 6.0)
 | 
			
		||||
  paperclip-av-transcoder (~> 0.6)
 | 
			
		||||
  parallel_tests (~> 2.26)
 | 
			
		||||
  parallel_tests (~> 2.27)
 | 
			
		||||
  pg (~> 1.1)
 | 
			
		||||
  pghero (~> 2.2)
 | 
			
		||||
  pkg-config (~> 1.3)
 | 
			
		||||
@ -735,26 +732,26 @@ DEPENDENCIES
 | 
			
		||||
  pundit (~> 2.0)
 | 
			
		||||
  rack-attack (~> 5.4)
 | 
			
		||||
  rack-cors (~> 1.0)
 | 
			
		||||
  rails (~> 5.2.1)
 | 
			
		||||
  rails (~> 5.2.2)
 | 
			
		||||
  rails-controller-testing (~> 1.0)
 | 
			
		||||
  rails-i18n (~> 5.1)
 | 
			
		||||
  rails-settings-cached (~> 0.6)
 | 
			
		||||
  rdf-normalize (~> 0.3)
 | 
			
		||||
  redis (~> 4.0)
 | 
			
		||||
  redis (~> 4.1)
 | 
			
		||||
  redis-namespace (~> 1.5)
 | 
			
		||||
  redis-rails (~> 5.0)
 | 
			
		||||
  rqrcode (~> 0.10)
 | 
			
		||||
  rspec-rails (~> 3.8)
 | 
			
		||||
  rspec-sidekiq (~> 3.0)
 | 
			
		||||
  rubocop (~> 0.60)
 | 
			
		||||
  rubocop (~> 0.62)
 | 
			
		||||
  sanitize (~> 5.0)
 | 
			
		||||
  scss_lint (~> 0.57)
 | 
			
		||||
  sidekiq (~> 5.2)
 | 
			
		||||
  sidekiq-bulk (~> 0.1.1)
 | 
			
		||||
  sidekiq-bulk (~> 0.2.0)
 | 
			
		||||
  sidekiq-scheduler (~> 3.0)
 | 
			
		||||
  sidekiq-unique-jobs (~> 5.0)
 | 
			
		||||
  simple-navigation (~> 4.0)
 | 
			
		||||
  simple_form (~> 4.0)
 | 
			
		||||
  simple_form (~> 4.1)
 | 
			
		||||
  simplecov (~> 0.16)
 | 
			
		||||
  sprockets-rails (~> 3.2)
 | 
			
		||||
  stackprof
 | 
			
		||||
@ -763,10 +760,10 @@ DEPENDENCIES
 | 
			
		||||
  strong_migrations (~> 0.3)
 | 
			
		||||
  thor (~> 0.20)
 | 
			
		||||
  tty-command (~> 0.8)
 | 
			
		||||
  tty-prompt (~> 0.17)
 | 
			
		||||
  tty-prompt (~> 0.18)
 | 
			
		||||
  twitter-text (~> 1.14)
 | 
			
		||||
  tzinfo-data (~> 1.2018)
 | 
			
		||||
  webmock (~> 3.4)
 | 
			
		||||
  webmock (~> 3.5)
 | 
			
		||||
  webpacker (~> 3.5)
 | 
			
		||||
  webpush
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										2
									
								
								Vagrantfile
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								Vagrantfile
									
									
									
									
										vendored
									
									
								
							@ -44,7 +44,7 @@ sudo apt-get install \
 | 
			
		||||
 | 
			
		||||
# Install rvm
 | 
			
		||||
read RUBY_VERSION < .ruby-version
 | 
			
		||||
gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
 | 
			
		||||
gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
 | 
			
		||||
curl -sSL https://raw.githubusercontent.com/rvm/rvm/stable/binscripts/rvm-installer | bash -s stable --ruby=$RUBY_VERSION
 | 
			
		||||
source /home/vagrant/.rvm/scripts/rvm
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -31,7 +31,7 @@ class ActivityPub::CollectionsController < Api::BaseController
 | 
			
		||||
    when 'featured'
 | 
			
		||||
      @account.pinned_statuses.count
 | 
			
		||||
    else
 | 
			
		||||
      raise ActiveRecord::NotFound
 | 
			
		||||
      raise ActiveRecord::RecordNotFound
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@ -42,7 +42,7 @@ class ActivityPub::CollectionsController < Api::BaseController
 | 
			
		||||
        scope.merge!(@account.pinned_statuses)
 | 
			
		||||
      end
 | 
			
		||||
    else
 | 
			
		||||
      raise ActiveRecord::NotFound
 | 
			
		||||
      raise ActiveRecord::RecordNotFound
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										36
									
								
								app/controllers/admin/account_actions_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								app/controllers/admin/account_actions_controller.rb
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,36 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
module Admin
 | 
			
		||||
  class AccountActionsController < BaseController
 | 
			
		||||
    before_action :set_account
 | 
			
		||||
 | 
			
		||||
    def new
 | 
			
		||||
      @account_action  = Admin::AccountAction.new(type: params[:type], report_id: params[:report_id], send_email_notification: true)
 | 
			
		||||
      @warning_presets = AccountWarningPreset.all
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def create
 | 
			
		||||
      account_action                 = Admin::AccountAction.new(resource_params)
 | 
			
		||||
      account_action.target_account  = @account
 | 
			
		||||
      account_action.current_account = current_account
 | 
			
		||||
 | 
			
		||||
      account_action.save!
 | 
			
		||||
 | 
			
		||||
      if account_action.with_report?
 | 
			
		||||
        redirect_to admin_reports_path
 | 
			
		||||
      else
 | 
			
		||||
        redirect_to admin_account_path(@account.id)
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    private
 | 
			
		||||
 | 
			
		||||
    def set_account
 | 
			
		||||
      @account = Account.find(params[:account_id])
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def resource_params
 | 
			
		||||
      params.require(:admin_account_action).permit(:type, :report_id, :warning_preset_id, :text, :send_email_notification)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@ -14,6 +14,7 @@ module Admin
 | 
			
		||||
      else
 | 
			
		||||
        @account          = @account_moderation_note.target_account
 | 
			
		||||
        @moderation_notes = @account.targeted_moderation_notes.latest
 | 
			
		||||
        @warnings         = @account.targeted_account_warnings.latest.custom
 | 
			
		||||
 | 
			
		||||
        render template: 'admin/accounts/show'
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
@ -2,9 +2,9 @@
 | 
			
		||||
 | 
			
		||||
module Admin
 | 
			
		||||
  class AccountsController < BaseController
 | 
			
		||||
    before_action :set_account, only: [:show, :subscribe, :unsubscribe, :redownload, :remove_avatar, :enable, :disable, :memorialize]
 | 
			
		||||
    before_action :set_account, only: [:show, :subscribe, :unsubscribe, :redownload, :remove_avatar, :remove_header, :enable, :unsilence, :unsuspend, :memorialize]
 | 
			
		||||
    before_action :require_remote_account!, only: [:subscribe, :unsubscribe, :redownload]
 | 
			
		||||
    before_action :require_local_account!, only: [:enable, :disable, :memorialize]
 | 
			
		||||
    before_action :require_local_account!, only: [:enable, :memorialize]
 | 
			
		||||
 | 
			
		||||
    def index
 | 
			
		||||
      authorize :account, :index?
 | 
			
		||||
@ -13,8 +13,10 @@ module Admin
 | 
			
		||||
 | 
			
		||||
    def show
 | 
			
		||||
      authorize @account, :show?
 | 
			
		||||
 | 
			
		||||
      @account_moderation_note = current_account.account_moderation_notes.new(target_account: @account)
 | 
			
		||||
      @moderation_notes = @account.targeted_moderation_notes.latest
 | 
			
		||||
      @moderation_notes        = @account.targeted_moderation_notes.latest
 | 
			
		||||
      @warnings                = @account.targeted_account_warnings.latest.custom
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def subscribe
 | 
			
		||||
@ -43,19 +45,25 @@ module Admin
 | 
			
		||||
      redirect_to admin_account_path(@account.id)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def disable
 | 
			
		||||
      authorize @account.user, :disable?
 | 
			
		||||
      @account.user.disable!
 | 
			
		||||
      log_action :disable, @account.user
 | 
			
		||||
    def unsilence
 | 
			
		||||
      authorize @account, :unsilence?
 | 
			
		||||
      @account.unsilence!
 | 
			
		||||
      log_action :unsilence, @account
 | 
			
		||||
      redirect_to admin_account_path(@account.id)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def unsuspend
 | 
			
		||||
      authorize @account, :unsuspend?
 | 
			
		||||
      @account.unsuspend!
 | 
			
		||||
      log_action :unsuspend, @account
 | 
			
		||||
      redirect_to admin_account_path(@account.id)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def redownload
 | 
			
		||||
      authorize @account, :redownload?
 | 
			
		||||
 | 
			
		||||
      @account.reset_avatar!
 | 
			
		||||
      @account.reset_header!
 | 
			
		||||
      @account.save!
 | 
			
		||||
      @account.update!(last_webfingered_at: nil)
 | 
			
		||||
      ResolveAccountService.new.call(@account)
 | 
			
		||||
 | 
			
		||||
      redirect_to admin_account_path(@account.id)
 | 
			
		||||
    end
 | 
			
		||||
@ -71,6 +79,17 @@ module Admin
 | 
			
		||||
      redirect_to admin_account_path(@account.id)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def remove_header
 | 
			
		||||
      authorize @account, :remove_header?
 | 
			
		||||
 | 
			
		||||
      @account.header = nil
 | 
			
		||||
      @account.save!
 | 
			
		||||
 | 
			
		||||
      log_action :remove_header, @account.user
 | 
			
		||||
 | 
			
		||||
      redirect_to admin_account_path(@account.id)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    private
 | 
			
		||||
 | 
			
		||||
    def set_account
 | 
			
		||||
@ -94,8 +113,8 @@ module Admin
 | 
			
		||||
        :local,
 | 
			
		||||
        :remote,
 | 
			
		||||
        :by_domain,
 | 
			
		||||
        :active,
 | 
			
		||||
        :silenced,
 | 
			
		||||
        :alphabetic,
 | 
			
		||||
        :suspended,
 | 
			
		||||
        :username,
 | 
			
		||||
        :display_name,
 | 
			
		||||
 | 
			
		||||
@ -15,5 +15,9 @@ module Admin
 | 
			
		||||
    def set_body_classes
 | 
			
		||||
      @body_classes = 'admin'
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def set_user
 | 
			
		||||
      @user = Account.find(params[:account_id]).user || raise(ActiveRecord::RecordNotFound)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -25,10 +25,6 @@ module Admin
 | 
			
		||||
 | 
			
		||||
    private
 | 
			
		||||
 | 
			
		||||
    def set_user
 | 
			
		||||
      @user = Account.find(params[:account_id]).user || raise(ActiveRecord::RecordNotFound)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def check_confirmation
 | 
			
		||||
      if @user.confirmed?
 | 
			
		||||
        flash[:error] = I18n.t('admin.accounts.resend_confirmation.already_confirmed')
 | 
			
		||||
 | 
			
		||||
@ -28,6 +28,7 @@ module Admin
 | 
			
		||||
      @pam_enabled           = ENV['PAM_ENABLED'] == 'true'
 | 
			
		||||
      @hidden_service        = ENV['ALLOW_ACCESS_TO_HIDDEN_SERVICE'] == 'true'
 | 
			
		||||
      @trending_hashtags     = TrendingTags.get(7)
 | 
			
		||||
      @profile_directory     = Setting.profile_directory
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    private
 | 
			
		||||
 | 
			
		||||
@ -4,14 +4,9 @@ module Admin
 | 
			
		||||
  class DomainBlocksController < BaseController
 | 
			
		||||
    before_action :set_domain_block, only: [:show, :destroy]
 | 
			
		||||
 | 
			
		||||
    def index
 | 
			
		||||
      authorize :domain_block, :index?
 | 
			
		||||
      @domain_blocks = DomainBlock.page(params[:page])
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def new
 | 
			
		||||
      authorize :domain_block, :create?
 | 
			
		||||
      @domain_block = DomainBlock.new
 | 
			
		||||
      @domain_block = DomainBlock.new(domain: params[:_domain])
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def create
 | 
			
		||||
@ -22,7 +17,7 @@ module Admin
 | 
			
		||||
      if @domain_block.save
 | 
			
		||||
        DomainBlockWorker.perform_async(@domain_block.id)
 | 
			
		||||
        log_action :create, @domain_block
 | 
			
		||||
        redirect_to admin_domain_blocks_path, notice: I18n.t('admin.domain_blocks.created_msg')
 | 
			
		||||
        redirect_to admin_instances_path(limited: '1'), notice: I18n.t('admin.domain_blocks.created_msg')
 | 
			
		||||
      else
 | 
			
		||||
        render :new
 | 
			
		||||
      end
 | 
			
		||||
@ -36,7 +31,7 @@ module Admin
 | 
			
		||||
      authorize @domain_block, :destroy?
 | 
			
		||||
      UnblockDomainService.new.call(@domain_block, retroactive_unblock?)
 | 
			
		||||
      log_action :destroy, @domain_block
 | 
			
		||||
      redirect_to admin_domain_blocks_path, notice: I18n.t('admin.domain_blocks.destroyed_msg')
 | 
			
		||||
      redirect_to admin_instances_path(limited: '1'), notice: I18n.t('admin.domain_blocks.destroyed_msg')
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    private
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										18
									
								
								app/controllers/admin/followers_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								app/controllers/admin/followers_controller.rb
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,18 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
module Admin
 | 
			
		||||
  class FollowersController < BaseController
 | 
			
		||||
    before_action :set_account
 | 
			
		||||
 | 
			
		||||
    PER_PAGE = 40
 | 
			
		||||
 | 
			
		||||
    def index
 | 
			
		||||
      authorize :account, :index?
 | 
			
		||||
      @followers = @account.followers.local.recent.page(params[:page]).per(PER_PAGE)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def set_account
 | 
			
		||||
      @account = Account.find(params[:account_id])
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@ -4,14 +4,21 @@ module Admin
 | 
			
		||||
  class InstancesController < BaseController
 | 
			
		||||
    def index
 | 
			
		||||
      authorize :instance, :index?
 | 
			
		||||
 | 
			
		||||
      @instances = ordered_instances
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def resubscribe
 | 
			
		||||
      authorize :instance, :resubscribe?
 | 
			
		||||
      params.require(:by_domain)
 | 
			
		||||
      Pubsubhubbub::SubscribeWorker.push_bulk(subscribeable_accounts.pluck(:id))
 | 
			
		||||
      redirect_to admin_instances_path
 | 
			
		||||
    def show
 | 
			
		||||
      authorize :instance, :show?
 | 
			
		||||
 | 
			
		||||
      @instance        = Instance.new(Account.by_domain_accounts.find_by(domain: params[:id]) || DomainBlock.find_by!(domain: params[:id]))
 | 
			
		||||
      @following_count = Follow.where(account: Account.where(domain: params[:id])).count
 | 
			
		||||
      @followers_count = Follow.where(target_account: Account.where(domain: params[:id])).count
 | 
			
		||||
      @reports_count   = Report.where(target_account: Account.where(domain: params[:id])).count
 | 
			
		||||
      @blocks_count    = Block.where(target_account: Account.where(domain: params[:id])).count
 | 
			
		||||
      @available       = DeliveryFailureTracker.available?(Account.select(:shared_inbox_url).where(domain: params[:id]).first&.shared_inbox_url)
 | 
			
		||||
      @media_storage   = MediaAttachment.where(account: Account.where(domain: params[:id])).sum(:file_file_size)
 | 
			
		||||
      @domain_block    = DomainBlock.find_by(domain: params[:id])
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    private
 | 
			
		||||
@ -27,17 +34,11 @@ module Admin
 | 
			
		||||
    helper_method :paginated_instances
 | 
			
		||||
 | 
			
		||||
    def ordered_instances
 | 
			
		||||
      paginated_instances.map { |account| Instance.new(account) }
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def subscribeable_accounts
 | 
			
		||||
      Account.with_followers.remote.where(domain: params[:by_domain])
 | 
			
		||||
      paginated_instances.map { |resource| Instance.new(resource) }
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def filter_params
 | 
			
		||||
      params.permit(
 | 
			
		||||
        :domain_name
 | 
			
		||||
      )
 | 
			
		||||
      params.permit(:limited)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -13,75 +13,42 @@ module Admin
 | 
			
		||||
      authorize @report, :show?
 | 
			
		||||
 | 
			
		||||
      @report_note  = @report.notes.new
 | 
			
		||||
      @report_notes = (@report.notes.latest + @report.history).sort_by(&:created_at)
 | 
			
		||||
      @report_notes = (@report.notes.latest + @report.history + @report.target_account.targeted_account_warnings.latest.custom).sort_by(&:created_at)
 | 
			
		||||
      @form         = Form::StatusBatch.new
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def update
 | 
			
		||||
    def assign_to_self
 | 
			
		||||
      authorize @report, :update?
 | 
			
		||||
      process_report
 | 
			
		||||
      @report.update!(assigned_account_id: current_account.id)
 | 
			
		||||
      log_action :assigned_to_self, @report
 | 
			
		||||
      redirect_to admin_report_path(@report)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
      if @report.action_taken?
 | 
			
		||||
        redirect_to admin_reports_path, notice: I18n.t('admin.reports.resolved_msg')
 | 
			
		||||
      else
 | 
			
		||||
        redirect_to admin_report_path(@report)
 | 
			
		||||
      end
 | 
			
		||||
    def unassign
 | 
			
		||||
      authorize @report, :update?
 | 
			
		||||
      @report.update!(assigned_account_id: nil)
 | 
			
		||||
      log_action :unassigned, @report
 | 
			
		||||
      redirect_to admin_report_path(@report)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def reopen
 | 
			
		||||
      authorize @report, :update?
 | 
			
		||||
      @report.unresolve!
 | 
			
		||||
      log_action :reopen, @report
 | 
			
		||||
      redirect_to admin_report_path(@report)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def resolve
 | 
			
		||||
      authorize @report, :update?
 | 
			
		||||
      @report.resolve!(current_account)
 | 
			
		||||
      log_action :resolve, @report
 | 
			
		||||
      redirect_to admin_reports_path, notice: I18n.t('admin.reports.resolved_msg')
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    private
 | 
			
		||||
 | 
			
		||||
    def process_report
 | 
			
		||||
      case params[:outcome].to_s
 | 
			
		||||
      when 'assign_to_self'
 | 
			
		||||
        @report.update!(assigned_account_id: current_account.id)
 | 
			
		||||
        log_action :assigned_to_self, @report
 | 
			
		||||
      when 'unassign'
 | 
			
		||||
        @report.update!(assigned_account_id: nil)
 | 
			
		||||
        log_action :unassigned, @report
 | 
			
		||||
      when 'reopen'
 | 
			
		||||
        @report.unresolve!
 | 
			
		||||
        log_action :reopen, @report
 | 
			
		||||
      when 'resolve'
 | 
			
		||||
        @report.resolve!(current_account)
 | 
			
		||||
        log_action :resolve, @report
 | 
			
		||||
      when 'disable'
 | 
			
		||||
        @report.resolve!(current_account)
 | 
			
		||||
        @report.target_account.user.disable!
 | 
			
		||||
 | 
			
		||||
        log_action :resolve, @report
 | 
			
		||||
        log_action :disable, @report.target_account.user
 | 
			
		||||
 | 
			
		||||
        resolve_all_target_account_reports
 | 
			
		||||
      when 'silence'
 | 
			
		||||
        @report.resolve!(current_account)
 | 
			
		||||
        @report.target_account.update!(silenced: true)
 | 
			
		||||
 | 
			
		||||
        log_action :resolve, @report
 | 
			
		||||
        log_action :silence, @report.target_account
 | 
			
		||||
 | 
			
		||||
        resolve_all_target_account_reports
 | 
			
		||||
      else
 | 
			
		||||
        raise ActiveRecord::RecordNotFound
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      @report.reload
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def resolve_all_target_account_reports
 | 
			
		||||
      unresolved_reports_for_target_account.update_all(action_taken: true, action_taken_by_account_id: current_account.id)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def unresolved_reports_for_target_account
 | 
			
		||||
      Report.where(
 | 
			
		||||
        target_account: @report.target_account
 | 
			
		||||
      ).unresolved
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def filtered_reports
 | 
			
		||||
      ReportFilter.new(filter_params).results.order(id: :desc).includes(
 | 
			
		||||
        :account,
 | 
			
		||||
        :target_account
 | 
			
		||||
      )
 | 
			
		||||
      ReportFilter.new(filter_params).results.order(id: :desc).includes(:account, :target_account)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def filter_params
 | 
			
		||||
 | 
			
		||||
@ -10,11 +10,5 @@ module Admin
 | 
			
		||||
      log_action :reset_password, @user
 | 
			
		||||
      redirect_to admin_accounts_path
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    private
 | 
			
		||||
 | 
			
		||||
    def set_user
 | 
			
		||||
      @user = Account.find(params[:account_id]).user || raise(ActiveRecord::RecordNotFound)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -17,11 +17,5 @@ module Admin
 | 
			
		||||
      log_action :demote, @user
 | 
			
		||||
      redirect_to admin_account_path(@user.account_id)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    private
 | 
			
		||||
 | 
			
		||||
    def set_user
 | 
			
		||||
      @user = Account.find(params[:account_id]).user || raise(ActiveRecord::RecordNotFound)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -26,6 +26,7 @@ module Admin
 | 
			
		||||
      show_known_fediverse_at_about_page
 | 
			
		||||
      preview_sensitive_media
 | 
			
		||||
      custom_css
 | 
			
		||||
      profile_directory
 | 
			
		||||
    ).freeze
 | 
			
		||||
 | 
			
		||||
    BOOLEAN_SETTINGS = %w(
 | 
			
		||||
@ -37,6 +38,7 @@ module Admin
 | 
			
		||||
      peers_api_enabled
 | 
			
		||||
      show_known_fediverse_at_about_page
 | 
			
		||||
      preview_sensitive_media
 | 
			
		||||
      profile_directory
 | 
			
		||||
    ).freeze
 | 
			
		||||
 | 
			
		||||
    UPLOAD_SETTINGS = %w(
 | 
			
		||||
 | 
			
		||||
@ -1,27 +0,0 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
module Admin
 | 
			
		||||
  class SilencesController < BaseController
 | 
			
		||||
    before_action :set_account
 | 
			
		||||
 | 
			
		||||
    def create
 | 
			
		||||
      authorize @account, :silence?
 | 
			
		||||
      @account.update!(silenced: true)
 | 
			
		||||
      log_action :silence, @account
 | 
			
		||||
      redirect_to admin_accounts_path
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def destroy
 | 
			
		||||
      authorize @account, :unsilence?
 | 
			
		||||
      @account.update!(silenced: false)
 | 
			
		||||
      log_action :unsilence, @account
 | 
			
		||||
      redirect_to admin_accounts_path
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    private
 | 
			
		||||
 | 
			
		||||
    def set_account
 | 
			
		||||
      @account = Account.find(params[:account_id])
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@ -22,6 +22,15 @@ module Admin
 | 
			
		||||
      @form     = Form::StatusBatch.new
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def show
 | 
			
		||||
      authorize :status, :index?
 | 
			
		||||
 | 
			
		||||
      @statuses = @account.statuses.where(id: params[:id])
 | 
			
		||||
      authorize @statuses.first, :show?
 | 
			
		||||
 | 
			
		||||
      @form = Form::StatusBatch.new
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def create
 | 
			
		||||
      authorize :status, :update?
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,60 +0,0 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
module Admin
 | 
			
		||||
  class SuspensionsController < BaseController
 | 
			
		||||
    before_action :set_account
 | 
			
		||||
 | 
			
		||||
    def new
 | 
			
		||||
      @suspension = Form::AdminSuspensionConfirmation.new(report_id: params[:report_id])
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def create
 | 
			
		||||
      authorize @account, :suspend?
 | 
			
		||||
 | 
			
		||||
      @suspension = Form::AdminSuspensionConfirmation.new(suspension_params)
 | 
			
		||||
 | 
			
		||||
      if suspension_params[:acct] == @account.acct
 | 
			
		||||
        resolve_report! if suspension_params[:report_id].present?
 | 
			
		||||
        perform_suspend!
 | 
			
		||||
        mark_reports_resolved!
 | 
			
		||||
        redirect_to admin_accounts_path
 | 
			
		||||
      else
 | 
			
		||||
        flash.now[:alert] = I18n.t('admin.suspensions.bad_acct_msg')
 | 
			
		||||
        render :new
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def destroy
 | 
			
		||||
      authorize @account, :unsuspend?
 | 
			
		||||
      @account.unsuspend!
 | 
			
		||||
      log_action :unsuspend, @account
 | 
			
		||||
      redirect_to admin_accounts_path
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    private
 | 
			
		||||
 | 
			
		||||
    def set_account
 | 
			
		||||
      @account = Account.find(params[:account_id])
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def suspension_params
 | 
			
		||||
      params.require(:form_admin_suspension_confirmation).permit(:acct, :report_id)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def resolve_report!
 | 
			
		||||
      report = Report.find(suspension_params[:report_id])
 | 
			
		||||
      report.resolve!(current_account)
 | 
			
		||||
      log_action :resolve, report
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def perform_suspend!
 | 
			
		||||
      @account.suspend!
 | 
			
		||||
      Admin::SuspensionWorker.perform_async(@account.id)
 | 
			
		||||
      log_action :suspend, @account
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def mark_reports_resolved!
 | 
			
		||||
      Report.where(target_account: @account).unresolved.update_all(action_taken: true, action_taken_by_account_id: current_account.id)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										44
									
								
								app/controllers/admin/tags_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								app/controllers/admin/tags_controller.rb
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,44 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
module Admin
 | 
			
		||||
  class TagsController < BaseController
 | 
			
		||||
    before_action :set_tags, only: :index
 | 
			
		||||
    before_action :set_tag, except: :index
 | 
			
		||||
    before_action :set_filter_params
 | 
			
		||||
 | 
			
		||||
    def index
 | 
			
		||||
      authorize :tag, :index?
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def hide
 | 
			
		||||
      authorize @tag, :hide?
 | 
			
		||||
      @tag.account_tag_stat.update!(hidden: true)
 | 
			
		||||
      redirect_to admin_tags_path(@filter_params)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def unhide
 | 
			
		||||
      authorize @tag, :unhide?
 | 
			
		||||
      @tag.account_tag_stat.update!(hidden: false)
 | 
			
		||||
      redirect_to admin_tags_path(@filter_params)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    private
 | 
			
		||||
 | 
			
		||||
    def set_tags
 | 
			
		||||
      @tags = Tag.discoverable
 | 
			
		||||
      @tags.merge!(Tag.hidden) if filter_params[:hidden]
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def set_tag
 | 
			
		||||
      @tag = Tag.find(params[:id])
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def set_filter_params
 | 
			
		||||
      @filter_params = filter_params.to_hash.symbolize_keys
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def filter_params
 | 
			
		||||
      params.permit(:hidden)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@ -2,7 +2,7 @@
 | 
			
		||||
 | 
			
		||||
module Admin
 | 
			
		||||
  class TwoFactorAuthenticationsController < BaseController
 | 
			
		||||
    before_action :set_user
 | 
			
		||||
    before_action :set_target_user
 | 
			
		||||
 | 
			
		||||
    def destroy
 | 
			
		||||
      authorize @user, :disable_2fa?
 | 
			
		||||
@ -13,7 +13,7 @@ module Admin
 | 
			
		||||
 | 
			
		||||
    private
 | 
			
		||||
 | 
			
		||||
    def set_user
 | 
			
		||||
    def set_target_user
 | 
			
		||||
      @user = User.find(params[:user_id])
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										58
									
								
								app/controllers/admin/warning_presets_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								app/controllers/admin/warning_presets_controller.rb
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,58 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
module Admin
 | 
			
		||||
  class WarningPresetsController < BaseController
 | 
			
		||||
    before_action :set_warning_preset, except: [:index, :create]
 | 
			
		||||
 | 
			
		||||
    def index
 | 
			
		||||
      authorize :account_warning_preset, :index?
 | 
			
		||||
 | 
			
		||||
      @warning_presets = AccountWarningPreset.all
 | 
			
		||||
      @warning_preset  = AccountWarningPreset.new
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def create
 | 
			
		||||
      authorize :account_warning_preset, :create?
 | 
			
		||||
 | 
			
		||||
      @warning_preset = AccountWarningPreset.new(warning_preset_params)
 | 
			
		||||
 | 
			
		||||
      if @warning_preset.save
 | 
			
		||||
        redirect_to admin_warning_presets_path
 | 
			
		||||
      else
 | 
			
		||||
        @warning_presets = AccountWarningPreset.all
 | 
			
		||||
        render :index
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def edit
 | 
			
		||||
      authorize @warning_preset, :update?
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def update
 | 
			
		||||
      authorize @warning_preset, :update?
 | 
			
		||||
 | 
			
		||||
      if @warning_preset.update(warning_preset_params)
 | 
			
		||||
        redirect_to admin_warning_presets_path
 | 
			
		||||
      else
 | 
			
		||||
        render :edit
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def destroy
 | 
			
		||||
      authorize @warning_preset, :destroy?
 | 
			
		||||
 | 
			
		||||
      @warning_preset.destroy!
 | 
			
		||||
      redirect_to admin_warning_presets_path
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    private
 | 
			
		||||
 | 
			
		||||
    def set_warning_preset
 | 
			
		||||
      @warning_preset = AccountWarningPreset.find(params[:id])
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def warning_preset_params
 | 
			
		||||
      params.require(:account_warning_preset).permit(:text)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@ -68,12 +68,14 @@ class Api::BaseController < ApplicationController
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def require_user!
 | 
			
		||||
    if current_user && !current_user.disabled?
 | 
			
		||||
      set_user_activity
 | 
			
		||||
    elsif current_user
 | 
			
		||||
      render json: { error: 'Your login is currently disabled' }, status: 403
 | 
			
		||||
    else
 | 
			
		||||
    if !current_user
 | 
			
		||||
      render json: { error: 'This method requires an authenticated user' }, status: 422
 | 
			
		||||
    elsif current_user.disabled?
 | 
			
		||||
      render json: { error: 'Your login is currently disabled' }, status: 403
 | 
			
		||||
    elsif !current_user.confirmed?
 | 
			
		||||
      render json: { error: 'Email confirmation is not completed' }, status: 403
 | 
			
		||||
    else
 | 
			
		||||
      set_user_activity
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -21,7 +21,7 @@ class Api::V1::Accounts::CredentialsController < Api::BaseController
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def account_params
 | 
			
		||||
    params.permit(:display_name, :note, :avatar, :header, :locked, :bot, fields_attributes: [:name, :value])
 | 
			
		||||
    params.permit(:display_name, :note, :avatar, :header, :locked, :bot, :discoverable, fields_attributes: [:name, :value])
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def user_settings_params
 | 
			
		||||
 | 
			
		||||
@ -25,7 +25,7 @@ class Api::V1::Accounts::FollowerAccountsController < Api::BaseController
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def default_accounts
 | 
			
		||||
    Account.includes(:active_relationships).references(:active_relationships)
 | 
			
		||||
    Account.includes(:active_relationships, :account_stat).references(:active_relationships)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def paginated_follows
 | 
			
		||||
 | 
			
		||||
@ -25,7 +25,7 @@ class Api::V1::Accounts::FollowingAccountsController < Api::BaseController
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def default_accounts
 | 
			
		||||
    Account.includes(:passive_relationships).references(:passive_relationships)
 | 
			
		||||
    Account.includes(:passive_relationships, :account_stat).references(:passive_relationships)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def paginated_follows
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
class Api::V1::Accounts::StatusesController < Api::BaseController
 | 
			
		||||
  before_action -> { doorkeeper_authorize! :read, :'read:statuses' }
 | 
			
		||||
  before_action -> { authorize_if_got_token! :read, :'read:statuses' }
 | 
			
		||||
  before_action :set_account
 | 
			
		||||
  after_action :insert_pagination_headers
 | 
			
		||||
 | 
			
		||||
@ -28,13 +28,11 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
 | 
			
		||||
 | 
			
		||||
  def account_statuses
 | 
			
		||||
    statuses = truthy_param?(:pinned) ? pinned_scope : permitted_account_statuses
 | 
			
		||||
    statuses = statuses.paginate_by_id(
 | 
			
		||||
      limit_param(DEFAULT_STATUSES_LIMIT),
 | 
			
		||||
      params_slice(:max_id, :since_id, :min_id)
 | 
			
		||||
    )
 | 
			
		||||
    statuses = statuses.paginate_by_id(limit_param(DEFAULT_STATUSES_LIMIT), params_slice(:max_id, :since_id, :min_id))
 | 
			
		||||
 | 
			
		||||
    statuses.merge!(only_media_scope) if truthy_param?(:only_media)
 | 
			
		||||
    statuses.merge!(no_replies_scope) if truthy_param?(:exclude_replies)
 | 
			
		||||
    statuses.merge!(no_reblogs_scope) if truthy_param?(:exclude_reblogs)
 | 
			
		||||
 | 
			
		||||
    statuses
 | 
			
		||||
  end
 | 
			
		||||
@ -65,6 +63,10 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
 | 
			
		||||
    Status.without_replies
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def no_reblogs_scope
 | 
			
		||||
    Status.without_reblogs
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def pagination_params(core_params)
 | 
			
		||||
    params.slice(:limit, :only_media, :exclude_replies).permit(:limit, :only_media, :exclude_replies).merge(core_params)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@ -1,14 +1,16 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
class Api::V1::AccountsController < Api::BaseController
 | 
			
		||||
  before_action -> { authorize_if_got_token! :read, :'read:accounts' }, except: [:follow, :unfollow, :block, :unblock, :mute, :unmute]
 | 
			
		||||
  before_action -> { authorize_if_got_token! :read, :'read:accounts' }, except: [:create, :follow, :unfollow, :block, :unblock, :mute, :unmute]
 | 
			
		||||
  before_action -> { doorkeeper_authorize! :follow, :'write:follows' }, only: [:follow, :unfollow]
 | 
			
		||||
  before_action -> { doorkeeper_authorize! :follow, :'write:mutes' }, only: [:mute, :unmute]
 | 
			
		||||
  before_action -> { doorkeeper_authorize! :follow, :'write:blocks' }, only: [:block, :unblock]
 | 
			
		||||
  before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, only: [:create]
 | 
			
		||||
 | 
			
		||||
  before_action :require_user!, except: [:show]
 | 
			
		||||
  before_action :set_account
 | 
			
		||||
  before_action :require_user!, except: [:show, :create]
 | 
			
		||||
  before_action :set_account, except: [:create]
 | 
			
		||||
  before_action :check_account_suspension, only: [:show]
 | 
			
		||||
  before_action :check_enabled_registrations, only: [:create]
 | 
			
		||||
 | 
			
		||||
  respond_to :json
 | 
			
		||||
 | 
			
		||||
@ -16,6 +18,16 @@ class Api::V1::AccountsController < Api::BaseController
 | 
			
		||||
    render json: @account, serializer: REST::AccountSerializer
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def create
 | 
			
		||||
    token    = AppSignUpService.new.call(doorkeeper_token.application, account_params)
 | 
			
		||||
    response = Doorkeeper::OAuth::TokenResponse.new(token)
 | 
			
		||||
 | 
			
		||||
    headers.merge!(response.headers)
 | 
			
		||||
 | 
			
		||||
    self.response_body = Oj.dump(response.body)
 | 
			
		||||
    self.status        = response.status
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def follow
 | 
			
		||||
    FollowService.new.call(current_user.account, @account, reblogs: truthy_param?(:reblogs))
 | 
			
		||||
 | 
			
		||||
@ -62,4 +74,12 @@ class Api::V1::AccountsController < Api::BaseController
 | 
			
		||||
  def check_account_suspension
 | 
			
		||||
    gone if @account.suspended?
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def account_params
 | 
			
		||||
    params.permit(:username, :email, :password, :agreement, :locale)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def check_enabled_registrations
 | 
			
		||||
    forbidden if single_user_mode? || !Setting.open_registrations
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -19,7 +19,7 @@ class Api::V1::BlocksController < Api::BaseController
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def paginated_blocks
 | 
			
		||||
    @paginated_blocks ||= Block.eager_load(:target_account)
 | 
			
		||||
    @paginated_blocks ||= Block.eager_load(target_account: :account_stat)
 | 
			
		||||
                               .where(account: current_account)
 | 
			
		||||
                               .paginate_by_max_id(
 | 
			
		||||
                                 limit_param(DEFAULT_ACCOUNTS_LIMIT),
 | 
			
		||||
 | 
			
		||||
@ -4,6 +4,8 @@ class Api::V1::CustomEmojisController < Api::BaseController
 | 
			
		||||
  respond_to :json
 | 
			
		||||
 | 
			
		||||
  def index
 | 
			
		||||
    render json: CustomEmoji.local.where(disabled: false), each_serializer: REST::CustomEmojiSerializer
 | 
			
		||||
    render_cached_json('api:v1:custom_emojis', expires_in: 1.minute) do
 | 
			
		||||
      ActiveModelSerializers::SerializableResource.new(CustomEmoji.local.where(disabled: false), each_serializer: REST::CustomEmojiSerializer)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -27,7 +27,7 @@ class Api::V1::EndorsementsController < Api::BaseController
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def endorsed_accounts
 | 
			
		||||
    current_account.endorsed_accounts
 | 
			
		||||
    current_account.endorsed_accounts.includes(:account_stat)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def insert_pagination_headers
 | 
			
		||||
 | 
			
		||||
@ -33,7 +33,7 @@ class Api::V1::FollowRequestsController < Api::BaseController
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def default_accounts
 | 
			
		||||
    Account.includes(:follow_requests).references(:follow_requests)
 | 
			
		||||
    Account.includes(:follow_requests, :account_stat).references(:follow_requests)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def paginated_follow_requests
 | 
			
		||||
 | 
			
		||||
@ -37,9 +37,9 @@ class Api::V1::Lists::AccountsController < Api::BaseController
 | 
			
		||||
 | 
			
		||||
  def load_accounts
 | 
			
		||||
    if unlimited?
 | 
			
		||||
      @list.accounts.all
 | 
			
		||||
      @list.accounts.includes(:account_stat).all
 | 
			
		||||
    else
 | 
			
		||||
      @list.accounts.paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id])
 | 
			
		||||
      @list.accounts.includes(:account_stat).paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id])
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										77
									
								
								app/controllers/api/v1/scheduled_statuses_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								app/controllers/api/v1/scheduled_statuses_controller.rb
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,77 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
class Api::V1::ScheduledStatusesController < Api::BaseController
 | 
			
		||||
  include Authorization
 | 
			
		||||
 | 
			
		||||
  before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, except: [:update, :destroy]
 | 
			
		||||
  before_action -> { doorkeeper_authorize! :write, :'write:statuses' }, only: [:update, :destroy]
 | 
			
		||||
 | 
			
		||||
  before_action :set_statuses, only: :index
 | 
			
		||||
  before_action :set_status, except: :index
 | 
			
		||||
 | 
			
		||||
  after_action :insert_pagination_headers, only: :index
 | 
			
		||||
 | 
			
		||||
  def index
 | 
			
		||||
    render json: @statuses, each_serializer: REST::ScheduledStatusSerializer
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def show
 | 
			
		||||
    render json: @status, serializer: REST::ScheduledStatusSerializer
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def update
 | 
			
		||||
    @status.update!(scheduled_status_params)
 | 
			
		||||
    render json: @status, serializer: REST::ScheduledStatusSerializer
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def destroy
 | 
			
		||||
    @status.destroy!
 | 
			
		||||
    render_empty
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def set_statuses
 | 
			
		||||
    @statuses = current_account.scheduled_statuses.paginate_by_id(limit_param(DEFAULT_STATUSES_LIMIT), params_slice(:max_id, :since_id, :min_id))
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def set_status
 | 
			
		||||
    @status = current_account.scheduled_statuses.find(params[:id])
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def scheduled_status_params
 | 
			
		||||
    params.permit(:scheduled_at)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def pagination_params(core_params)
 | 
			
		||||
    params.slice(:limit).permit(:limit).merge(core_params)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def insert_pagination_headers
 | 
			
		||||
    set_pagination_headers(next_path, prev_path)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def next_path
 | 
			
		||||
    if records_continue?
 | 
			
		||||
      api_v1_scheduled_statuses_url pagination_params(max_id: pagination_max_id)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def prev_path
 | 
			
		||||
    unless @statuses.empty?
 | 
			
		||||
      api_v1_scheduled_statuses_url pagination_params(min_id: pagination_since_id)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def records_continue?
 | 
			
		||||
    @statuses.size == limit_param(DEFAULT_STATUSES_LIMIT)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def pagination_max_id
 | 
			
		||||
    @statuses.last.id
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def pagination_since_id
 | 
			
		||||
    @statuses.first.id
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@ -22,7 +22,7 @@ class Api::V1::Statuses::FavouritedByAccountsController < Api::BaseController
 | 
			
		||||
 | 
			
		||||
  def default_accounts
 | 
			
		||||
    Account
 | 
			
		||||
      .includes(:favourites)
 | 
			
		||||
      .includes(:favourites, :account_stat)
 | 
			
		||||
      .references(:favourites)
 | 
			
		||||
      .where(favourites: { status_id: @status.id })
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@ -21,7 +21,7 @@ class Api::V1::Statuses::RebloggedByAccountsController < Api::BaseController
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def default_accounts
 | 
			
		||||
    Account.includes(:statuses).references(:statuses)
 | 
			
		||||
    Account.includes(:statuses, :account_stat).references(:statuses)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def paginated_statuses
 | 
			
		||||
 | 
			
		||||
@ -45,17 +45,18 @@ class Api::V1::StatusesController < Api::BaseController
 | 
			
		||||
 | 
			
		||||
  def create
 | 
			
		||||
    @status = PostStatusService.new.call(current_user.account,
 | 
			
		||||
                                         status_params[:status],
 | 
			
		||||
                                         status_params[:in_reply_to_id].blank? ? nil : Status.find(status_params[:in_reply_to_id]),
 | 
			
		||||
                                         text: status_params[:status],
 | 
			
		||||
                                         thread: status_params[:in_reply_to_id].blank? ? nil : Status.find(status_params[:in_reply_to_id]),
 | 
			
		||||
                                         media_ids: status_params[:media_ids],
 | 
			
		||||
                                         sensitive: status_params[:sensitive],
 | 
			
		||||
                                         spoiler_text: status_params[:spoiler_text],
 | 
			
		||||
                                         visibility: status_params[:visibility],
 | 
			
		||||
                                         scheduled_at: status_params[:scheduled_at],
 | 
			
		||||
                                         application: doorkeeper_token.application,
 | 
			
		||||
                                         idempotency: request.headers['Idempotency-Key'],
 | 
			
		||||
                                         local_only: status_params[:local_only])
 | 
			
		||||
 | 
			
		||||
    render json: @status, serializer: REST::StatusSerializer
 | 
			
		||||
    render json: @status, serializer: @status.is_a?(ScheduledStatus) ? REST::ScheduledStatusSerializer : REST::StatusSerializer
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def destroy
 | 
			
		||||
@ -78,7 +79,7 @@ class Api::V1::StatusesController < Api::BaseController
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def status_params
 | 
			
		||||
    params.permit(:status, :in_reply_to_id, :sensitive, :spoiler_text, :visibility, :local_only, media_ids: [])
 | 
			
		||||
    params.permit(:status, :in_reply_to_id, :sensitive, :spoiler_text, :visibility, :scheduled_at, :local_only, media_ids: [])
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def pagination_params(core_params)
 | 
			
		||||
 | 
			
		||||
@ -45,7 +45,7 @@ class Api::V1::Timelines::TagController < Api::BaseController
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def tag_timeline_statuses
 | 
			
		||||
    Status.as_tag_timeline(@tag, current_account, truthy_param?(:local))
 | 
			
		||||
    HashtagQueryService.new.call(@tag, params.slice(:any, :all, :none), current_account, truthy_param?(:local))
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def insert_pagination_headers
 | 
			
		||||
 | 
			
		||||
@ -10,6 +10,7 @@ class Api::Web::EmbedsController < Api::Web::BaseController
 | 
			
		||||
    render json: status, serializer: OEmbedSerializer, width: 400
 | 
			
		||||
  rescue ActiveRecord::RecordNotFound
 | 
			
		||||
    oembed = FetchOEmbedService.new.call(params[:url])
 | 
			
		||||
    oembed[:html] = Formatter.instance.sanitize(oembed[:html], Sanitize::Config::MASTODON_OEMBED) if oembed[:html].present?
 | 
			
		||||
 | 
			
		||||
    if oembed
 | 
			
		||||
      render json: oembed
 | 
			
		||||
 | 
			
		||||
@ -6,9 +6,9 @@ class Auth::ConfirmationsController < Devise::ConfirmationsController
 | 
			
		||||
  before_action :set_body_classes
 | 
			
		||||
  before_action :set_user, only: [:finish_signup]
 | 
			
		||||
 | 
			
		||||
  # GET/PATCH /users/:id/finish_signup
 | 
			
		||||
  def finish_signup
 | 
			
		||||
    return unless request.patch? && params[:user]
 | 
			
		||||
 | 
			
		||||
    if @user.update(user_params)
 | 
			
		||||
      @user.skip_reconfirmation!
 | 
			
		||||
      bypass_sign_in(@user)
 | 
			
		||||
@ -31,4 +31,12 @@ class Auth::ConfirmationsController < Devise::ConfirmationsController
 | 
			
		||||
  def user_params
 | 
			
		||||
    params.require(:user).permit(:email)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def after_confirmation_path_for(_resource_name, user)
 | 
			
		||||
    if user.created_by_application && truthy_param?(:redirect_to_app)
 | 
			
		||||
      user.created_by_application.redirect_uri
 | 
			
		||||
    else
 | 
			
		||||
      super
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -26,6 +26,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController
 | 
			
		||||
 | 
			
		||||
    resource.locale      = I18n.locale
 | 
			
		||||
    resource.invite_code = params[:invite_code] if resource.invite_code.blank?
 | 
			
		||||
    resource.agreement   = true
 | 
			
		||||
 | 
			
		||||
    resource.build_account if resource.account.nil?
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@ -1,21 +0,0 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
module RemoteAccountControllerConcern
 | 
			
		||||
  extend ActiveSupport::Concern
 | 
			
		||||
 | 
			
		||||
  included do
 | 
			
		||||
    layout 'public'
 | 
			
		||||
    before_action :set_account
 | 
			
		||||
    before_action :check_account_suspension
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def set_account
 | 
			
		||||
    @account = Account.find_remote!(params[:acct])
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def check_account_suspension
 | 
			
		||||
    gone if @account.suspended?
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@ -43,7 +43,13 @@ module SignatureVerification
 | 
			
		||||
      return
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    account = account_from_key_id(signature_params['keyId'])
 | 
			
		||||
    account_stoplight = Stoplight("source:#{request.ip}") { account_from_key_id(signature_params['keyId']) }
 | 
			
		||||
      .with_fallback { nil }
 | 
			
		||||
      .with_threshold(1)
 | 
			
		||||
      .with_cool_off_time(5.minutes.seconds)
 | 
			
		||||
      .with_error_handler { |error, handle| error.is_a?(HTTP::Error) ? handle.call(error) : raise(error) }
 | 
			
		||||
 | 
			
		||||
    account = account_stoplight.run
 | 
			
		||||
 | 
			
		||||
    if account.nil?
 | 
			
		||||
      @signature_verification_failure_reason = "Public key not found for key #{signature_params['keyId']}"
 | 
			
		||||
@ -54,23 +60,26 @@ module SignatureVerification
 | 
			
		||||
    signature             = Base64.decode64(signature_params['signature'])
 | 
			
		||||
    compare_signed_string = build_signed_string(signature_params['headers'])
 | 
			
		||||
 | 
			
		||||
    if account.keypair.public_key.verify(OpenSSL::Digest::SHA256.new, signature, compare_signed_string)
 | 
			
		||||
      @signed_request_account = account
 | 
			
		||||
      @signed_request_account
 | 
			
		||||
    elsif account.possibly_stale?
 | 
			
		||||
      account = account.refresh!
 | 
			
		||||
    return account unless verify_signature(account, signature, compare_signed_string).nil?
 | 
			
		||||
 | 
			
		||||
      if account.keypair.public_key.verify(OpenSSL::Digest::SHA256.new, signature, compare_signed_string)
 | 
			
		||||
        @signed_request_account = account
 | 
			
		||||
        @signed_request_account
 | 
			
		||||
      else
 | 
			
		||||
        @signature_verification_failure_reason = "Verification failed for #{account.username}@#{account.domain} #{account.uri}"
 | 
			
		||||
        @signed_request_account = nil
 | 
			
		||||
      end
 | 
			
		||||
    else
 | 
			
		||||
      @signature_verification_failure_reason = "Verification failed for #{account.username}@#{account.domain} #{account.uri}"
 | 
			
		||||
    account_stoplight = Stoplight("source:#{request.ip}") { account.possibly_stale? ? account.refresh! : account_refresh_key(account) }
 | 
			
		||||
      .with_fallback { nil }
 | 
			
		||||
      .with_threshold(1)
 | 
			
		||||
      .with_cool_off_time(5.minutes.seconds)
 | 
			
		||||
      .with_error_handler { |error, handle| error.is_a?(HTTP::Error) ? handle.call(error) : raise(error) }
 | 
			
		||||
 | 
			
		||||
    account = account_stoplight.run
 | 
			
		||||
 | 
			
		||||
    if account.nil?
 | 
			
		||||
      @signature_verification_failure_reason = "Public key not found for key #{signature_params['keyId']}"
 | 
			
		||||
      @signed_request_account = nil
 | 
			
		||||
      return
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    return account unless verify_signature(account, signature, compare_signed_string).nil?
 | 
			
		||||
 | 
			
		||||
    @signature_verification_failure_reason = "Verification failed for #{account.username}@#{account.domain} #{account.uri}"
 | 
			
		||||
    @signed_request_account = nil
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def request_body
 | 
			
		||||
@ -79,6 +88,15 @@ module SignatureVerification
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def verify_signature(account, signature, compare_signed_string)
 | 
			
		||||
    if account.keypair.public_key.verify(OpenSSL::Digest::SHA256.new, signature, compare_signed_string)
 | 
			
		||||
      @signed_request_account = account
 | 
			
		||||
      @signed_request_account
 | 
			
		||||
    end
 | 
			
		||||
  rescue OpenSSL::PKey::RSAError
 | 
			
		||||
    nil
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def build_signed_string(signed_headers)
 | 
			
		||||
    signed_headers = 'date' if signed_headers.blank?
 | 
			
		||||
 | 
			
		||||
@ -125,4 +143,9 @@ module SignatureVerification
 | 
			
		||||
      account
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def account_refresh_key(account)
 | 
			
		||||
    return if account.local? || !account.activitypub?
 | 
			
		||||
    ActivityPub::FetchRemoteAccountService.new.call(account.uri, only_key: true)
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										43
									
								
								app/controllers/directories_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								app/controllers/directories_controller.rb
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,43 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
class DirectoriesController < ApplicationController
 | 
			
		||||
  layout 'public'
 | 
			
		||||
 | 
			
		||||
  before_action :check_enabled
 | 
			
		||||
  before_action :set_instance_presenter
 | 
			
		||||
  before_action :set_tag, only: :show
 | 
			
		||||
  before_action :set_tags
 | 
			
		||||
  before_action :set_accounts
 | 
			
		||||
 | 
			
		||||
  def index
 | 
			
		||||
    render :index
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def show
 | 
			
		||||
    render :index
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def check_enabled
 | 
			
		||||
    return not_found unless Setting.profile_directory
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def set_tag
 | 
			
		||||
    @tag = Tag.discoverable.find_by!(name: params[:id].downcase)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def set_tags
 | 
			
		||||
    @tags = Tag.discoverable.limit(30)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def set_accounts
 | 
			
		||||
    @accounts = Account.discoverable.page(params[:page]).per(40).tap do |query|
 | 
			
		||||
      query.merge!(Account.tagged_with(@tag.id)) if @tag
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def set_instance_presenter
 | 
			
		||||
    @instance_presenter = InstancePresenter.new
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@ -6,12 +6,17 @@ class MediaController < ApplicationController
 | 
			
		||||
  before_action :set_media_attachment
 | 
			
		||||
  before_action :verify_permitted_status!
 | 
			
		||||
 | 
			
		||||
  content_security_policy only: :player do |p|
 | 
			
		||||
    p.frame_ancestors(false)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def show
 | 
			
		||||
    redirect_to @media_attachment.file.url(:original)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def player
 | 
			
		||||
    @body_classes = 'player'
 | 
			
		||||
    response.headers['X-Frame-Options'] = 'ALLOWALL'
 | 
			
		||||
    raise ActiveRecord::RecordNotFound unless @media_attachment.video? || @media_attachment.gifv?
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -5,6 +5,7 @@ class RemoteInteractionController < ApplicationController
 | 
			
		||||
 | 
			
		||||
  layout 'modal'
 | 
			
		||||
 | 
			
		||||
  before_action :set_interaction_type
 | 
			
		||||
  before_action :set_status
 | 
			
		||||
  before_action :set_body_classes
 | 
			
		||||
 | 
			
		||||
@ -45,4 +46,8 @@ class RemoteInteractionController < ApplicationController
 | 
			
		||||
    @body_classes = 'modal-layout'
 | 
			
		||||
    @hide_header  = true
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def set_interaction_type
 | 
			
		||||
    @interaction_type = %w(reply reblog favourite).include?(params[:type]) ? params[:type] : 'reply'
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -1,12 +1,11 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
class Settings::ApplicationsController < ApplicationController
 | 
			
		||||
class Settings::ApplicationsController < Settings::BaseController
 | 
			
		||||
  layout 'admin'
 | 
			
		||||
 | 
			
		||||
  before_action :authenticate_user!
 | 
			
		||||
  before_action :set_application, only: [:show, :update, :destroy, :regenerate]
 | 
			
		||||
  before_action :prepare_scopes, only: [:create, :update]
 | 
			
		||||
  before_action :set_body_classes
 | 
			
		||||
 | 
			
		||||
  def index
 | 
			
		||||
    @applications = current_user.applications.order(id: :desc).page(params[:page])
 | 
			
		||||
@ -70,8 +69,4 @@ class Settings::ApplicationsController < ApplicationController
 | 
			
		||||
    scopes = params.fetch(:doorkeeper_application, {}).fetch(:scopes, nil)
 | 
			
		||||
    params[:doorkeeper_application][:scopes] = scopes.join(' ') if scopes.is_a? Array
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def set_body_classes
 | 
			
		||||
    @body_classes = 'admin'
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										11
									
								
								app/controllers/settings/base_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								app/controllers/settings/base_controller.rb
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,11 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
class Settings::BaseController < ApplicationController
 | 
			
		||||
  before_action :set_body_classes
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def set_body_classes
 | 
			
		||||
    @body_classes = 'admin'
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@ -1,11 +1,10 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
class Settings::DeletesController < ApplicationController
 | 
			
		||||
class Settings::DeletesController < Settings::BaseController
 | 
			
		||||
  layout 'admin'
 | 
			
		||||
 | 
			
		||||
  before_action :check_enabled_deletion
 | 
			
		||||
  before_action :authenticate_user!
 | 
			
		||||
  before_action :set_body_classes
 | 
			
		||||
 | 
			
		||||
  def show
 | 
			
		||||
    @confirmation = Form::DeleteConfirmation.new
 | 
			
		||||
@ -30,8 +29,4 @@ class Settings::DeletesController < ApplicationController
 | 
			
		||||
  def delete_params
 | 
			
		||||
    params.require(:form_delete_confirmation).permit(:password)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def set_body_classes
 | 
			
		||||
    @body_classes = 'admin'
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,19 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
module Settings
 | 
			
		||||
  module Exports
 | 
			
		||||
    class BlockedDomainsController < ApplicationController
 | 
			
		||||
      include ExportControllerConcern
 | 
			
		||||
 | 
			
		||||
      def index
 | 
			
		||||
        send_export_file
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      private
 | 
			
		||||
 | 
			
		||||
      def export_data
 | 
			
		||||
        @export.to_blocked_domains_csv
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										19
									
								
								app/controllers/settings/exports/lists_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								app/controllers/settings/exports/lists_controller.rb
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
module Settings
 | 
			
		||||
  module Exports
 | 
			
		||||
    class ListsController < ApplicationController
 | 
			
		||||
      include ExportControllerConcern
 | 
			
		||||
 | 
			
		||||
      def index
 | 
			
		||||
        send_export_file
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      private
 | 
			
		||||
 | 
			
		||||
      def export_data
 | 
			
		||||
        @export.to_lists_csv
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@ -1,12 +1,11 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
class Settings::ExportsController < ApplicationController
 | 
			
		||||
class Settings::ExportsController < Settings::BaseController
 | 
			
		||||
  include Authorization
 | 
			
		||||
 | 
			
		||||
  layout 'admin'
 | 
			
		||||
 | 
			
		||||
  before_action :authenticate_user!
 | 
			
		||||
  before_action :set_body_classes
 | 
			
		||||
 | 
			
		||||
  def show
 | 
			
		||||
    @export  = Export.new(current_account)
 | 
			
		||||
@ -21,10 +20,4 @@ class Settings::ExportsController < ApplicationController
 | 
			
		||||
 | 
			
		||||
    redirect_to settings_export_path
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def set_body_classes
 | 
			
		||||
    @body_classes = 'admin'
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -1,10 +1,9 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
class Settings::FollowerDomainsController < ApplicationController
 | 
			
		||||
class Settings::FollowerDomainsController < Settings::BaseController
 | 
			
		||||
  layout 'admin'
 | 
			
		||||
 | 
			
		||||
  before_action :authenticate_user!
 | 
			
		||||
  before_action :set_body_classes
 | 
			
		||||
 | 
			
		||||
  def show
 | 
			
		||||
    @account = current_account
 | 
			
		||||
@ -26,8 +25,4 @@ class Settings::FollowerDomainsController < ApplicationController
 | 
			
		||||
  def bulk_params
 | 
			
		||||
    params.permit(select: [])
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def set_body_classes
 | 
			
		||||
    @body_classes = 'admin'
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -1,11 +1,10 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
class Settings::ImportsController < ApplicationController
 | 
			
		||||
class Settings::ImportsController < Settings::BaseController
 | 
			
		||||
  layout 'admin'
 | 
			
		||||
 | 
			
		||||
  before_action :authenticate_user!
 | 
			
		||||
  before_action :set_account
 | 
			
		||||
  before_action :set_body_classes
 | 
			
		||||
 | 
			
		||||
  def show
 | 
			
		||||
    @import = Import.new
 | 
			
		||||
@ -32,8 +31,4 @@ class Settings::ImportsController < ApplicationController
 | 
			
		||||
  def import_params
 | 
			
		||||
    params.require(:import).permit(:data, :type)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def set_body_classes
 | 
			
		||||
    @body_classes = 'admin'
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -1,10 +1,9 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
class Settings::MigrationsController < ApplicationController
 | 
			
		||||
class Settings::MigrationsController < Settings::BaseController
 | 
			
		||||
  layout 'admin'
 | 
			
		||||
 | 
			
		||||
  before_action :authenticate_user!
 | 
			
		||||
  before_action :set_body_classes
 | 
			
		||||
 | 
			
		||||
  def show
 | 
			
		||||
    @migration = Form::Migration.new(account: current_account.moved_to_account)
 | 
			
		||||
@ -32,8 +31,4 @@ class Settings::MigrationsController < ApplicationController
 | 
			
		||||
    current_account.moved_to_account_id != @migration.account&.id &&
 | 
			
		||||
      current_account.id != @migration.account&.id
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def set_body_classes
 | 
			
		||||
    @body_classes = 'admin'
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -1,10 +1,9 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
class Settings::NotificationsController < ApplicationController
 | 
			
		||||
class Settings::NotificationsController < Settings::BaseController
 | 
			
		||||
  layout 'admin'
 | 
			
		||||
 | 
			
		||||
  before_action :authenticate_user!
 | 
			
		||||
  before_action :set_body_classes
 | 
			
		||||
 | 
			
		||||
  def show; end
 | 
			
		||||
 | 
			
		||||
@ -30,8 +29,4 @@ class Settings::NotificationsController < ApplicationController
 | 
			
		||||
      interactions: %i(must_be_follower must_be_following must_be_following_dm)
 | 
			
		||||
    )
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def set_body_classes
 | 
			
		||||
    @body_classes = 'admin'
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -1,10 +1,9 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
class Settings::PreferencesController < ApplicationController
 | 
			
		||||
class Settings::PreferencesController < Settings::BaseController
 | 
			
		||||
  layout 'admin'
 | 
			
		||||
 | 
			
		||||
  before_action :authenticate_user!
 | 
			
		||||
  before_action :set_body_classes
 | 
			
		||||
 | 
			
		||||
  def show; end
 | 
			
		||||
 | 
			
		||||
@ -49,12 +48,9 @@ class Settings::PreferencesController < ApplicationController
 | 
			
		||||
      :setting_noindex,
 | 
			
		||||
      :setting_theme,
 | 
			
		||||
      :setting_hide_network,
 | 
			
		||||
      :setting_aggregate_reblogs,
 | 
			
		||||
      notification_emails: %i(follow follow_request reblog favourite mention digest report),
 | 
			
		||||
      interactions: %i(must_be_follower must_be_following)
 | 
			
		||||
    )
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def set_body_classes
 | 
			
		||||
    @body_classes = 'admin'
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -1,13 +1,12 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
class Settings::ProfilesController < ApplicationController
 | 
			
		||||
class Settings::ProfilesController < Settings::BaseController
 | 
			
		||||
  include ObfuscateFilename
 | 
			
		||||
 | 
			
		||||
  layout 'admin'
 | 
			
		||||
 | 
			
		||||
  before_action :authenticate_user!
 | 
			
		||||
  before_action :set_account
 | 
			
		||||
  before_action :set_body_classes
 | 
			
		||||
 | 
			
		||||
  obfuscate_filename [:account, :avatar]
 | 
			
		||||
  obfuscate_filename [:account, :header]
 | 
			
		||||
@ -29,14 +28,10 @@ class Settings::ProfilesController < ApplicationController
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def account_params
 | 
			
		||||
    params.require(:account).permit(:display_name, :note, :avatar, :header, :locked, :bot, fields_attributes: [:name, :value])
 | 
			
		||||
    params.require(:account).permit(:display_name, :note, :avatar, :header, :locked, :bot, :discoverable, fields_attributes: [:name, :value])
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def set_account
 | 
			
		||||
    @account = current_user.account
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def set_body_classes
 | 
			
		||||
    @body_classes = 'admin'
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,7 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
class Settings::SessionsController < ApplicationController
 | 
			
		||||
class Settings::SessionsController < Settings::BaseController
 | 
			
		||||
  before_action :set_session, only: :destroy
 | 
			
		||||
  before_action :set_body_classes
 | 
			
		||||
 | 
			
		||||
  def destroy
 | 
			
		||||
    @session.destroy!
 | 
			
		||||
@ -15,8 +14,4 @@ class Settings::SessionsController < ApplicationController
 | 
			
		||||
  def set_session
 | 
			
		||||
    @session = current_user.session_activations.find(params[:id])
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def set_body_classes
 | 
			
		||||
    @body_classes = 'admin'
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -2,12 +2,11 @@
 | 
			
		||||
 | 
			
		||||
module Settings
 | 
			
		||||
  module TwoFactorAuthentication
 | 
			
		||||
    class ConfirmationsController < ApplicationController
 | 
			
		||||
    class ConfirmationsController < BaseController
 | 
			
		||||
      layout 'admin'
 | 
			
		||||
 | 
			
		||||
      before_action :authenticate_user!
 | 
			
		||||
      before_action :ensure_otp_secret
 | 
			
		||||
      before_action :set_body_classes
 | 
			
		||||
 | 
			
		||||
      def new
 | 
			
		||||
        prepare_two_factor_form
 | 
			
		||||
@ -44,10 +43,6 @@ module Settings
 | 
			
		||||
      def ensure_otp_secret
 | 
			
		||||
        redirect_to settings_two_factor_authentication_path unless current_user.otp_secret
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      def set_body_classes
 | 
			
		||||
        @body_classes = 'admin'
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -2,11 +2,10 @@
 | 
			
		||||
 | 
			
		||||
module Settings
 | 
			
		||||
  module TwoFactorAuthentication
 | 
			
		||||
    class RecoveryCodesController < ApplicationController
 | 
			
		||||
    class RecoveryCodesController < BaseController
 | 
			
		||||
      layout 'admin'
 | 
			
		||||
 | 
			
		||||
      before_action :authenticate_user!
 | 
			
		||||
      before_action :set_body_classes
 | 
			
		||||
 | 
			
		||||
      def create
 | 
			
		||||
        @recovery_codes = current_user.generate_otp_backup_codes!
 | 
			
		||||
@ -14,12 +13,6 @@ module Settings
 | 
			
		||||
        flash[:notice] = I18n.t('two_factor_authentication.recovery_codes_regenerated')
 | 
			
		||||
        render :index
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      private
 | 
			
		||||
 | 
			
		||||
      def set_body_classes
 | 
			
		||||
        @body_classes = 'admin'
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -1,12 +1,11 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
module Settings
 | 
			
		||||
  class TwoFactorAuthenticationsController < ApplicationController
 | 
			
		||||
  class TwoFactorAuthenticationsController < BaseController
 | 
			
		||||
    layout 'admin'
 | 
			
		||||
 | 
			
		||||
    before_action :authenticate_user!
 | 
			
		||||
    before_action :verify_otp_required, only: [:create]
 | 
			
		||||
    before_action :set_body_classes
 | 
			
		||||
 | 
			
		||||
    def show
 | 
			
		||||
      @confirmation = Form::TwoFactorConfirmation.new
 | 
			
		||||
@ -44,9 +43,5 @@ module Settings
 | 
			
		||||
      current_user.validate_and_consume_otp!(confirmation_params[:code]) ||
 | 
			
		||||
        current_user.invalidate_otp_backup_code!(confirmation_params[:code])
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def set_body_classes
 | 
			
		||||
      @body_classes = 'admin'
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -65,12 +65,13 @@ class StatusesController < ApplicationController
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def create_descendant_thread(depth, statuses)
 | 
			
		||||
  def create_descendant_thread(starting_depth, statuses)
 | 
			
		||||
    depth = starting_depth + statuses.size
 | 
			
		||||
    if depth < DESCENDANTS_DEPTH_LIMIT
 | 
			
		||||
      { statuses: statuses }
 | 
			
		||||
      { statuses: statuses, starting_depth: starting_depth }
 | 
			
		||||
    else
 | 
			
		||||
      next_status = statuses.pop
 | 
			
		||||
      { statuses: statuses, next_status: next_status }
 | 
			
		||||
      { statuses: statuses, starting_depth: starting_depth, next_status: next_status }
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@ -101,16 +102,19 @@ class StatusesController < ApplicationController
 | 
			
		||||
    @descendant_threads = []
 | 
			
		||||
 | 
			
		||||
    if descendants.present?
 | 
			
		||||
      statuses = [descendants.first]
 | 
			
		||||
      depth    = 1
 | 
			
		||||
      statuses       = [descendants.first]
 | 
			
		||||
      starting_depth = 0
 | 
			
		||||
 | 
			
		||||
      descendants.drop(1).each_with_index do |descendant, index|
 | 
			
		||||
        if descendants[index].id == descendant.in_reply_to_id
 | 
			
		||||
          depth += 1
 | 
			
		||||
          statuses << descendant
 | 
			
		||||
        else
 | 
			
		||||
          @descendant_threads << create_descendant_thread(depth, statuses)
 | 
			
		||||
          @descendant_threads << create_descendant_thread(starting_depth, statuses)
 | 
			
		||||
 | 
			
		||||
          # The thread is broken, assume it's a reply to the root status
 | 
			
		||||
          starting_depth = 0
 | 
			
		||||
 | 
			
		||||
          # ... unless we can find its ancestor in one of the already-processed threads
 | 
			
		||||
          @descendant_threads.reverse_each do |descendant_thread|
 | 
			
		||||
            statuses = descendant_thread[:statuses]
 | 
			
		||||
 | 
			
		||||
@ -119,18 +123,16 @@ class StatusesController < ApplicationController
 | 
			
		||||
            end
 | 
			
		||||
 | 
			
		||||
            if index.present?
 | 
			
		||||
              depth += index - statuses.size
 | 
			
		||||
              starting_depth = descendant_thread[:starting_depth] + index + 1
 | 
			
		||||
              break
 | 
			
		||||
            end
 | 
			
		||||
 | 
			
		||||
            depth -= statuses.size
 | 
			
		||||
          end
 | 
			
		||||
 | 
			
		||||
          statuses = [descendant]
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      @descendant_threads << create_descendant_thread(depth, statuses)
 | 
			
		||||
      @descendant_threads << create_descendant_thread(starting_depth, statuses)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    @max_descendant_thread_id = @descendant_threads.pop[:statuses].first.id if descendants.size >= DESCENDANTS_LIMIT
 | 
			
		||||
 | 
			
		||||
@ -16,14 +16,15 @@ class TagsController < ApplicationController
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      format.rss do
 | 
			
		||||
        @statuses = Status.as_tag_timeline(@tag).limit(PAGE_SIZE)
 | 
			
		||||
        @statuses = HashtagQueryService.new.call(@tag, params.slice(:any, :all, :none)).limit(PAGE_SIZE)
 | 
			
		||||
        @statuses = cache_collection(@statuses, Status)
 | 
			
		||||
 | 
			
		||||
        render xml: RSS::TagSerializer.render(@tag, @statuses)
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      format.json do
 | 
			
		||||
        @statuses = Status.as_tag_timeline(@tag, current_account, params[:local]).paginate_by_max_id(PAGE_SIZE, params[:max_id])
 | 
			
		||||
        @statuses = HashtagQueryService.new.call(@tag, params.slice(:any, :all, :none), current_account, params[:local])
 | 
			
		||||
                                       .paginate_by_max_id(PAGE_SIZE, params[:max_id])
 | 
			
		||||
        @statuses = cache_collection(@statuses, Status)
 | 
			
		||||
 | 
			
		||||
        render json: collection_presenter,
 | 
			
		||||
@ -46,7 +47,7 @@ class TagsController < ApplicationController
 | 
			
		||||
 | 
			
		||||
  def collection_presenter
 | 
			
		||||
    ActivityPub::CollectionPresenter.new(
 | 
			
		||||
      id: tag_url(@tag),
 | 
			
		||||
      id: tag_url(@tag, params.slice(:any, :all, :none)),
 | 
			
		||||
      type: :ordered,
 | 
			
		||||
      size: @tag.statuses.count,
 | 
			
		||||
      items: @statuses.map { |s| ActivityPub::TagManager.instance.uri_for(s) }
 | 
			
		||||
 | 
			
		||||
@ -24,7 +24,7 @@ module Admin::AccountModerationNotesHelper
 | 
			
		||||
 | 
			
		||||
  def name_tag_classes(account, inline = false)
 | 
			
		||||
    classes = [inline ? 'inline-name-tag' : 'name-tag']
 | 
			
		||||
    classes << 'suspended' if account.suspended?
 | 
			
		||||
    classes << 'suspended' if account.suspended? || (account.local? && account.user.nil?)
 | 
			
		||||
    classes.join(' ')
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -23,6 +23,8 @@ module Admin::ActionLogsHelper
 | 
			
		||||
      link_to record.domain, "https://#{record.domain}"
 | 
			
		||||
    when 'Status'
 | 
			
		||||
      link_to record.account.acct, TagManager.instance.url_for(record)
 | 
			
		||||
    when 'AccountWarning'
 | 
			
		||||
      link_to record.target_account.acct, admin_account_path(record.target_account_id)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@ -34,6 +36,7 @@ module Admin::ActionLogsHelper
 | 
			
		||||
      link_to attributes['domain'], "https://#{attributes['domain']}"
 | 
			
		||||
    when 'Status'
 | 
			
		||||
      tmp_status = Status.new(attributes.except('reblogs_count', 'favourites_count'))
 | 
			
		||||
 | 
			
		||||
      if tmp_status.account
 | 
			
		||||
        link_to tmp_status.account&.acct || "##{tmp_status.account_id}", admin_account_path(tmp_status.account_id)
 | 
			
		||||
      else
 | 
			
		||||
@ -81,6 +84,8 @@ module Admin::ActionLogsHelper
 | 
			
		||||
      'envelope'
 | 
			
		||||
    when 'Status'
 | 
			
		||||
      'pencil'
 | 
			
		||||
    when 'AccountWarning'
 | 
			
		||||
      'warning'
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@ -92,7 +97,7 @@ module Admin::ActionLogsHelper
 | 
			
		||||
      opposite_verbs?(log) ? 'negative' : 'positive'
 | 
			
		||||
    when :update, :reset_password, :disable_2fa, :memorialize, :change_email
 | 
			
		||||
      'neutral'
 | 
			
		||||
    when :demote, :silence, :disable, :suspend, :remove_avatar, :reopen
 | 
			
		||||
    when :demote, :silence, :disable, :suspend, :remove_avatar, :remove_header, :reopen
 | 
			
		||||
      'negative'
 | 
			
		||||
    when :destroy
 | 
			
		||||
      opposite_verbs?(log) ? 'positive' : 'negative'
 | 
			
		||||
@ -104,6 +109,6 @@ module Admin::ActionLogsHelper
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def opposite_verbs?(log)
 | 
			
		||||
    %w(DomainBlock EmailDomainBlock).include?(log.target_type)
 | 
			
		||||
    %w(DomainBlock EmailDomainBlock AccountWarning).include?(log.target_type)
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -1,12 +1,14 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
module Admin::FilterHelper
 | 
			
		||||
  ACCOUNT_FILTERS      = %i(local remote by_domain silenced suspended alphabetic username display_name email ip staff).freeze
 | 
			
		||||
  ACCOUNT_FILTERS      = %i(local remote by_domain active silenced suspended username display_name email ip staff).freeze
 | 
			
		||||
  REPORT_FILTERS       = %i(resolved account_id target_account_id).freeze
 | 
			
		||||
  INVITE_FILTER        = %i(available expired).freeze
 | 
			
		||||
  CUSTOM_EMOJI_FILTERS = %i(local remote by_domain shortcode).freeze
 | 
			
		||||
  TAGS_FILTERS         = %i(hidden).freeze
 | 
			
		||||
  INSTANCES_FILTERS    = %i(limited).freeze
 | 
			
		||||
 | 
			
		||||
  FILTERS = ACCOUNT_FILTERS + REPORT_FILTERS + INVITE_FILTER + CUSTOM_EMOJI_FILTERS
 | 
			
		||||
  FILTERS = ACCOUNT_FILTERS + REPORT_FILTERS + INVITE_FILTER + CUSTOM_EMOJI_FILTERS + TAGS_FILTERS + INSTANCES_FILTERS
 | 
			
		||||
 | 
			
		||||
  def filter_link_to(text, link_to_params, link_class_params = link_to_params)
 | 
			
		||||
    new_url = filtered_url_for(link_to_params)
 | 
			
		||||
 | 
			
		||||
@ -23,7 +23,7 @@ module HomeHelper
 | 
			
		||||
                  else
 | 
			
		||||
                    link_to(path || TagManager.instance.url_for(account), class: 'account__display-name') do
 | 
			
		||||
                      content_tag(:div, class: 'account__avatar-wrapper') do
 | 
			
		||||
                        content_tag(:div, '', class: 'account__avatar', style: "width: #{size}px; height: #{size}px; background-size: #{size}px #{size}px; background-image: url(#{account.avatar.url})")
 | 
			
		||||
                        content_tag(:div, '', class: 'account__avatar', style: "width: #{size}px; height: #{size}px; background-size: #{size}px #{size}px; background-image: url(#{full_asset_url(current_account&.user&.setting_auto_play_gif ? account.avatar_original_url : account.avatar_static_url)})")
 | 
			
		||||
                      end +
 | 
			
		||||
                        content_tag(:span, class: 'display-name') do
 | 
			
		||||
                          content_tag(:bdi) do
 | 
			
		||||
 | 
			
		||||
@ -1,4 +0,0 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
module MailerHelper
 | 
			
		||||
end
 | 
			
		||||
@ -30,6 +30,7 @@ module SettingsHelper
 | 
			
		||||
    ja: '日本語',
 | 
			
		||||
    ka: 'ქართული',
 | 
			
		||||
    ko: '한국어',
 | 
			
		||||
    ml: 'മലയാളം',
 | 
			
		||||
    nl: 'Nederlands',
 | 
			
		||||
    no: 'Norsk',
 | 
			
		||||
    oc: 'Occitan',
 | 
			
		||||
 | 
			
		||||
@ -34,12 +34,14 @@ module StreamEntriesHelper
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def account_badge(account)
 | 
			
		||||
  def account_badge(account, all: false)
 | 
			
		||||
    if account.bot?
 | 
			
		||||
      content_tag(:div, content_tag(:div, t('accounts.roles.bot'), class: 'account-role bot'), class: 'roles')
 | 
			
		||||
    elsif Setting.show_staff_badge && account.user_staff?
 | 
			
		||||
    elsif (Setting.show_staff_badge && account.user_staff?) || all
 | 
			
		||||
      content_tag(:div, class: 'roles') do
 | 
			
		||||
        if account.user_admin?
 | 
			
		||||
        if all && !account.user_staff?
 | 
			
		||||
          content_tag(:div, t('admin.accounts.roles.user'), class: 'account-role')
 | 
			
		||||
        elsif account.user_admin?
 | 
			
		||||
          content_tag(:div, t('accounts.roles.admin'), class: 'account-role admin')
 | 
			
		||||
        elsif account.user_moderator?
 | 
			
		||||
          content_tag(:div, t('accounts.roles.moderator'), class: 'account-role moderator')
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										4
									
								
								app/javascript/images/icon_flag.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								app/javascript/images/icon_flag.svg
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,4 @@
 | 
			
		||||
<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
 | 
			
		||||
  <path d="M0 0h24v24H0z" fill="none"/>
 | 
			
		||||
  <path d="M14.4 6L14 4H5v17h2v-7h5.6l.4 2h7V6z"/>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 197 B  | 
							
								
								
									
										
											BIN
										
									
								
								app/javascript/images/mailer/icon_warning.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/javascript/images/mailer/icon_warning.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 371 B  | 
							
								
								
									
										1
									
								
								app/javascript/images/screen_federation.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								app/javascript/images/screen_federation.svg
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| 
		 After Width: | Height: | Size: 37 KiB  | 
							
								
								
									
										1
									
								
								app/javascript/images/screen_hello.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								app/javascript/images/screen_hello.svg
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| 
		 After Width: | Height: | Size: 8.8 KiB  | 
							
								
								
									
										1
									
								
								app/javascript/images/screen_interactions.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								app/javascript/images/screen_interactions.svg
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| 
		 After Width: | Height: | Size: 27 KiB  | 
@ -132,6 +132,12 @@ export function submitCompose(routerHistory) {
 | 
			
		||||
        'Idempotency-Key': getState().getIn(['compose', 'idempotencyKey']),
 | 
			
		||||
      },
 | 
			
		||||
    }).then(function (response) {
 | 
			
		||||
      if (response.data.visibility === 'direct' && getState().getIn(['conversations', 'mounted']) <= 0 && routerHistory) {
 | 
			
		||||
        routerHistory.push('/timelines/direct');
 | 
			
		||||
      } else if (routerHistory && routerHistory.location.pathname === '/statuses/new' && window.history.state) {
 | 
			
		||||
        routerHistory.goBack();
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      dispatch(insertIntoTagHistory(response.data.tags, status));
 | 
			
		||||
      dispatch(submitComposeSuccess({ ...response.data }));
 | 
			
		||||
 | 
			
		||||
@ -144,9 +150,7 @@ export function submitCompose(routerHistory) {
 | 
			
		||||
        }
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      if (response.data.visibility === 'direct' && getState().getIn(['conversations', 'mounted']) <= 0 && routerHistory) {
 | 
			
		||||
        routerHistory.push('/timelines/direct');
 | 
			
		||||
      } else if (response.data.visibility !== 'direct') {
 | 
			
		||||
      if (response.data.visibility !== 'direct') {
 | 
			
		||||
        insertIfOnline('home');
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -38,7 +38,7 @@ export const expandConversations = ({ maxId } = {}) => (dispatch, getState) => {
 | 
			
		||||
  const params = { max_id: maxId };
 | 
			
		||||
 | 
			
		||||
  if (!maxId) {
 | 
			
		||||
    params.since_id = getState().getIn(['conversations', 0, 'last_status']);
 | 
			
		||||
    params.since_id = getState().getIn(['conversations', 'items', 0, 'last_status']);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  api(getState).get('/api/v1/conversations', { params })
 | 
			
		||||
 | 
			
		||||
@ -19,6 +19,7 @@ export function fetchCustomEmojis() {
 | 
			
		||||
export function fetchCustomEmojisRequest() {
 | 
			
		||||
  return {
 | 
			
		||||
    type: CUSTOM_EMOJIS_FETCH_REQUEST,
 | 
			
		||||
    skipLoading: true,
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -26,6 +27,7 @@ export function fetchCustomEmojisSuccess(custom_emojis) {
 | 
			
		||||
  return {
 | 
			
		||||
    type: CUSTOM_EMOJIS_FETCH_SUCCESS,
 | 
			
		||||
    custom_emojis,
 | 
			
		||||
    skipLoading: true,
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -33,5 +35,6 @@ export function fetchCustomEmojisFail(error) {
 | 
			
		||||
  return {
 | 
			
		||||
    type: CUSTOM_EMOJIS_FETCH_FAIL,
 | 
			
		||||
    error,
 | 
			
		||||
    skipLoading: true,
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -30,6 +30,7 @@ export function fetchFavouritedStatuses() {
 | 
			
		||||
export function fetchFavouritedStatusesRequest() {
 | 
			
		||||
  return {
 | 
			
		||||
    type: FAVOURITED_STATUSES_FETCH_REQUEST,
 | 
			
		||||
    skipLoading: true,
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -38,6 +39,7 @@ export function fetchFavouritedStatusesSuccess(statuses, next) {
 | 
			
		||||
    type: FAVOURITED_STATUSES_FETCH_SUCCESS,
 | 
			
		||||
    statuses,
 | 
			
		||||
    next,
 | 
			
		||||
    skipLoading: true,
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -45,6 +47,7 @@ export function fetchFavouritedStatusesFail(error) {
 | 
			
		||||
  return {
 | 
			
		||||
    type: FAVOURITED_STATUSES_FETCH_FAIL,
 | 
			
		||||
    error,
 | 
			
		||||
    skipLoading: true,
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -42,6 +42,13 @@ export const LIST_EDITOR_REMOVE_REQUEST = 'LIST_EDITOR_REMOVE_REQUEST';
 | 
			
		||||
export const LIST_EDITOR_REMOVE_SUCCESS = 'LIST_EDITOR_REMOVE_SUCCESS';
 | 
			
		||||
export const LIST_EDITOR_REMOVE_FAIL    = 'LIST_EDITOR_REMOVE_FAIL';
 | 
			
		||||
 | 
			
		||||
export const LIST_ADDER_RESET = 'LIST_ADDER_RESET';
 | 
			
		||||
export const LIST_ADDER_SETUP = 'LIST_ADDER_SETUP';
 | 
			
		||||
 | 
			
		||||
export const LIST_ADDER_LISTS_FETCH_REQUEST = 'LIST_ADDER_LISTS_FETCH_REQUEST';
 | 
			
		||||
export const LIST_ADDER_LISTS_FETCH_SUCCESS = 'LIST_ADDER_LISTS_FETCH_SUCCESS';
 | 
			
		||||
export const LIST_ADDER_LISTS_FETCH_FAIL    = 'LIST_ADDER_LISTS_FETCH_FAIL';
 | 
			
		||||
 | 
			
		||||
export const fetchList = id => (dispatch, getState) => {
 | 
			
		||||
  if (getState().getIn(['lists', id])) {
 | 
			
		||||
    return;
 | 
			
		||||
@ -316,3 +323,50 @@ export const removeFromListFail = (listId, accountId, error) => ({
 | 
			
		||||
  accountId,
 | 
			
		||||
  error,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const resetListAdder = () => ({
 | 
			
		||||
  type: LIST_ADDER_RESET,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const setupListAdder = accountId => (dispatch, getState) => {
 | 
			
		||||
  dispatch({
 | 
			
		||||
    type: LIST_ADDER_SETUP,
 | 
			
		||||
    account: getState().getIn(['accounts', accountId]),
 | 
			
		||||
  });
 | 
			
		||||
  dispatch(fetchLists());
 | 
			
		||||
  dispatch(fetchAccountLists(accountId));
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const fetchAccountLists = accountId => (dispatch, getState) => {
 | 
			
		||||
  dispatch(fetchAccountListsRequest(accountId));
 | 
			
		||||
 | 
			
		||||
  api(getState).get(`/api/v1/accounts/${accountId}/lists`)
 | 
			
		||||
    .then(({ data }) => dispatch(fetchAccountListsSuccess(accountId, data)))
 | 
			
		||||
    .catch(err => dispatch(fetchAccountListsFail(accountId, err)));
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const fetchAccountListsRequest = id => ({
 | 
			
		||||
  type:LIST_ADDER_LISTS_FETCH_REQUEST,
 | 
			
		||||
  id,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const fetchAccountListsSuccess = (id, lists) => ({
 | 
			
		||||
  type: LIST_ADDER_LISTS_FETCH_SUCCESS,
 | 
			
		||||
  id,
 | 
			
		||||
  lists,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const fetchAccountListsFail = (id, err) => ({
 | 
			
		||||
  type: LIST_ADDER_LISTS_FETCH_FAIL,
 | 
			
		||||
  id,
 | 
			
		||||
  err,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const addToListAdder = listId => (dispatch, getState) => {
 | 
			
		||||
  dispatch(addToList(listId, getState().getIn(['listAdder', 'accountId'])));
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const removeFromListAdder = listId => (dispatch, getState) => {
 | 
			
		||||
  dispatch(removeFromList(listId, getState().getIn(['listAdder', 'accountId'])));
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -8,6 +8,7 @@ import {
 | 
			
		||||
  importFetchedStatuses,
 | 
			
		||||
} from './importer';
 | 
			
		||||
import { defineMessages } from 'react-intl';
 | 
			
		||||
import { List as ImmutableList } from 'immutable';
 | 
			
		||||
import { unescapeHTML } from '../utils/html';
 | 
			
		||||
import { getFilters, regexFromFilters } from '../selectors';
 | 
			
		||||
 | 
			
		||||
@ -18,6 +19,8 @@ export const NOTIFICATIONS_EXPAND_REQUEST = 'NOTIFICATIONS_EXPAND_REQUEST';
 | 
			
		||||
export const NOTIFICATIONS_EXPAND_SUCCESS = 'NOTIFICATIONS_EXPAND_SUCCESS';
 | 
			
		||||
export const NOTIFICATIONS_EXPAND_FAIL    = 'NOTIFICATIONS_EXPAND_FAIL';
 | 
			
		||||
 | 
			
		||||
export const NOTIFICATIONS_FILTER_SET = 'NOTIFICATIONS_FILTER_SET';
 | 
			
		||||
 | 
			
		||||
export const NOTIFICATIONS_CLEAR      = 'NOTIFICATIONS_CLEAR';
 | 
			
		||||
export const NOTIFICATIONS_SCROLL_TOP = 'NOTIFICATIONS_SCROLL_TOP';
 | 
			
		||||
 | 
			
		||||
@ -88,11 +91,18 @@ export function updateNotifications(notification, intlMessages, intlLocale) {
 | 
			
		||||
 | 
			
		||||
const excludeTypesFromSettings = state => state.getIn(['settings', 'notifications', 'shows']).filter(enabled => !enabled).keySeq().toJS();
 | 
			
		||||
 | 
			
		||||
const excludeTypesFromFilter = filter => {
 | 
			
		||||
  const allTypes = ImmutableList(['follow', 'favourite', 'reblog', 'mention']);
 | 
			
		||||
  return allTypes.filterNot(item => item === filter).toJS();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const noOp = () => {};
 | 
			
		||||
 | 
			
		||||
export function expandNotifications({ maxId } = {}, done = noOp) {
 | 
			
		||||
  return (dispatch, getState) => {
 | 
			
		||||
    const activeFilter = getState().getIn(['settings', 'notifications', 'quickFilter', 'active']);
 | 
			
		||||
    const notifications = getState().get('notifications');
 | 
			
		||||
    const isLoadingMore = !!maxId;
 | 
			
		||||
 | 
			
		||||
    if (notifications.get('isLoading')) {
 | 
			
		||||
      done();
 | 
			
		||||
@ -101,14 +111,16 @@ export function expandNotifications({ maxId } = {}, done = noOp) {
 | 
			
		||||
 | 
			
		||||
    const params = {
 | 
			
		||||
      max_id: maxId,
 | 
			
		||||
      exclude_types: excludeTypesFromSettings(getState()),
 | 
			
		||||
      exclude_types: activeFilter === 'all'
 | 
			
		||||
        ? excludeTypesFromSettings(getState())
 | 
			
		||||
        : excludeTypesFromFilter(activeFilter),
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if (!maxId && notifications.get('items').size > 0) {
 | 
			
		||||
      params.since_id = notifications.getIn(['items', 0]);
 | 
			
		||||
      params.since_id = notifications.getIn(['items', 0, 'id']);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    dispatch(expandNotificationsRequest());
 | 
			
		||||
    dispatch(expandNotificationsRequest(isLoadingMore));
 | 
			
		||||
 | 
			
		||||
    api(getState).get('/api/v1/notifications', { params }).then(response => {
 | 
			
		||||
      const next = getLinks(response).refs.find(link => link.rel === 'next');
 | 
			
		||||
@ -116,34 +128,37 @@ export function expandNotifications({ maxId } = {}, done = noOp) {
 | 
			
		||||
      dispatch(importFetchedAccounts(response.data.map(item => item.account)));
 | 
			
		||||
      dispatch(importFetchedStatuses(response.data.map(item => item.status).filter(status => !!status)));
 | 
			
		||||
 | 
			
		||||
      dispatch(expandNotificationsSuccess(response.data, next ? next.uri : null));
 | 
			
		||||
      dispatch(expandNotificationsSuccess(response.data, next ? next.uri : null, isLoadingMore));
 | 
			
		||||
      fetchRelatedRelationships(dispatch, response.data);
 | 
			
		||||
      done();
 | 
			
		||||
    }).catch(error => {
 | 
			
		||||
      dispatch(expandNotificationsFail(error));
 | 
			
		||||
      dispatch(expandNotificationsFail(error, isLoadingMore));
 | 
			
		||||
      done();
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export function expandNotificationsRequest() {
 | 
			
		||||
export function expandNotificationsRequest(isLoadingMore) {
 | 
			
		||||
  return {
 | 
			
		||||
    type: NOTIFICATIONS_EXPAND_REQUEST,
 | 
			
		||||
    skipLoading: !isLoadingMore,
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export function expandNotificationsSuccess(notifications, next) {
 | 
			
		||||
export function expandNotificationsSuccess(notifications, next, isLoadingMore) {
 | 
			
		||||
  return {
 | 
			
		||||
    type: NOTIFICATIONS_EXPAND_SUCCESS,
 | 
			
		||||
    notifications,
 | 
			
		||||
    next,
 | 
			
		||||
    skipLoading: !isLoadingMore,
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export function expandNotificationsFail(error) {
 | 
			
		||||
export function expandNotificationsFail(error, isLoadingMore) {
 | 
			
		||||
  return {
 | 
			
		||||
    type: NOTIFICATIONS_EXPAND_FAIL,
 | 
			
		||||
    error,
 | 
			
		||||
    skipLoading: !isLoadingMore,
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -163,3 +178,14 @@ export function scrollTopNotifications(top) {
 | 
			
		||||
    top,
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export function setFilter (filterType) {
 | 
			
		||||
  return dispatch => {
 | 
			
		||||
    dispatch({
 | 
			
		||||
      type: NOTIFICATIONS_FILTER_SET,
 | 
			
		||||
      path: ['notifications', 'quickFilter', 'active'],
 | 
			
		||||
      value: filterType,
 | 
			
		||||
    });
 | 
			
		||||
    dispatch(expandNotifications());
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -1,14 +1,8 @@
 | 
			
		||||
import { openModal } from './modal';
 | 
			
		||||
import { changeSetting, saveSettings } from './settings';
 | 
			
		||||
 | 
			
		||||
export function showOnboardingOnce() {
 | 
			
		||||
  return (dispatch, getState) => {
 | 
			
		||||
    const alreadySeen = getState().getIn(['settings', 'onboarded']);
 | 
			
		||||
export const INTRODUCTION_VERSION = 20181216044202;
 | 
			
		||||
 | 
			
		||||
    if (!alreadySeen) {
 | 
			
		||||
      dispatch(openModal('ONBOARDING'));
 | 
			
		||||
      dispatch(changeSetting(['onboarded'], true));
 | 
			
		||||
      dispatch(saveSettings());
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
export const closeOnboarding = () => dispatch => {
 | 
			
		||||
  dispatch(changeSetting(['introductionVersion'], INTRODUCTION_VERSION));
 | 
			
		||||
  dispatch(saveSettings());
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,7 @@ import { getLocale } from '../locales';
 | 
			
		||||
 | 
			
		||||
const { messages } = getLocale();
 | 
			
		||||
 | 
			
		||||
export function connectTimelineStream (timelineId, path, pollingRefresh = null) {
 | 
			
		||||
export function connectTimelineStream (timelineId, path, pollingRefresh = null, accept = null) {
 | 
			
		||||
 | 
			
		||||
  return connectStream (path, pollingRefresh, (dispatch, getState) => {
 | 
			
		||||
    const locale = getState().getIn(['meta', 'locale']);
 | 
			
		||||
@ -24,7 +24,7 @@ export function connectTimelineStream (timelineId, path, pollingRefresh = null)
 | 
			
		||||
      onReceive (data) {
 | 
			
		||||
        switch(data.event) {
 | 
			
		||||
        case 'update':
 | 
			
		||||
          dispatch(updateTimeline(timelineId, JSON.parse(data.payload)));
 | 
			
		||||
          dispatch(updateTimeline(timelineId, JSON.parse(data.payload), accept));
 | 
			
		||||
          break;
 | 
			
		||||
        case 'delete':
 | 
			
		||||
          dispatch(deleteFromTimelines(data.payload));
 | 
			
		||||
@ -51,6 +51,6 @@ const refreshHomeTimelineAndNotification = (dispatch, done) => {
 | 
			
		||||
export const connectUserStream      = () => connectTimelineStream('home', 'user', refreshHomeTimelineAndNotification);
 | 
			
		||||
export const connectCommunityStream = ({ onlyMedia } = {}) => connectTimelineStream(`community${onlyMedia ? ':media' : ''}`, `public:local${onlyMedia ? ':media' : ''}`);
 | 
			
		||||
export const connectPublicStream    = ({ onlyMedia } = {}) => connectTimelineStream(`public${onlyMedia ? ':media' : ''}`, `public${onlyMedia ? ':media' : ''}`);
 | 
			
		||||
export const connectHashtagStream   = tag => connectTimelineStream(`hashtag:${tag}`, `hashtag&tag=${tag}`);
 | 
			
		||||
export const connectHashtagStream   = (id, tag, accept) => connectTimelineStream(`hashtag:${id}`, `hashtag&tag=${tag}`, null, accept);
 | 
			
		||||
export const connectDirectStream    = () => connectTimelineStream('direct', 'direct');
 | 
			
		||||
export const connectListStream      = id => connectTimelineStream(`list:${id}`, `list&list=${id}`);
 | 
			
		||||
 | 
			
		||||
@ -4,6 +4,7 @@ import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
 | 
			
		||||
 | 
			
		||||
export const TIMELINE_UPDATE  = 'TIMELINE_UPDATE';
 | 
			
		||||
export const TIMELINE_DELETE  = 'TIMELINE_DELETE';
 | 
			
		||||
export const TIMELINE_CLEAR   = 'TIMELINE_CLEAR';
 | 
			
		||||
 | 
			
		||||
export const TIMELINE_EXPAND_REQUEST = 'TIMELINE_EXPAND_REQUEST';
 | 
			
		||||
export const TIMELINE_EXPAND_SUCCESS = 'TIMELINE_EXPAND_SUCCESS';
 | 
			
		||||
@ -13,9 +14,11 @@ export const TIMELINE_SCROLL_TOP = 'TIMELINE_SCROLL_TOP';
 | 
			
		||||
 | 
			
		||||
export const TIMELINE_DISCONNECT = 'TIMELINE_DISCONNECT';
 | 
			
		||||
 | 
			
		||||
export function updateTimeline(timeline, status) {
 | 
			
		||||
  return (dispatch, getState) => {
 | 
			
		||||
    const references = status.reblog ? getState().get('statuses').filter((item, itemId) => (itemId === status.reblog.id || item.get('reblog') === status.reblog.id)).map((_, itemId) => itemId) : [];
 | 
			
		||||
export function updateTimeline(timeline, status, accept) {
 | 
			
		||||
  return dispatch => {
 | 
			
		||||
    if (typeof accept === 'function' && !accept(status)) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    dispatch(importFetchedStatus(status));
 | 
			
		||||
 | 
			
		||||
@ -23,7 +26,6 @@ export function updateTimeline(timeline, status) {
 | 
			
		||||
      type: TIMELINE_UPDATE,
 | 
			
		||||
      timeline,
 | 
			
		||||
      status,
 | 
			
		||||
      references,
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
@ -44,11 +46,24 @@ export function deleteFromTimelines(id) {
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export function clearTimeline(timeline) {
 | 
			
		||||
  return (dispatch) => {
 | 
			
		||||
    dispatch({ type: TIMELINE_CLEAR, timeline });
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const noOp = () => {};
 | 
			
		||||
 | 
			
		||||
const parseTags = (tags = {}, mode) => {
 | 
			
		||||
  return (tags[mode] || []).map((tag) => {
 | 
			
		||||
    return tag.value;
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export function expandTimeline(timelineId, path, params = {}, done = noOp) {
 | 
			
		||||
  return (dispatch, getState) => {
 | 
			
		||||
    const timeline = getState().getIn(['timelines', timelineId], ImmutableMap());
 | 
			
		||||
    const isLoadingMore = !!params.max_id;
 | 
			
		||||
 | 
			
		||||
    if (timeline.get('isLoading')) {
 | 
			
		||||
      done();
 | 
			
		||||
@ -59,15 +74,17 @@ export function expandTimeline(timelineId, path, params = {}, done = noOp) {
 | 
			
		||||
      params.since_id = timeline.getIn(['items', 0]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    dispatch(expandTimelineRequest(timelineId));
 | 
			
		||||
    const isLoadingRecent = !!params.since_id;
 | 
			
		||||
 | 
			
		||||
    dispatch(expandTimelineRequest(timelineId, isLoadingMore));
 | 
			
		||||
 | 
			
		||||
    api(getState).get(path, { params }).then(response => {
 | 
			
		||||
      const next = getLinks(response).refs.find(link => link.rel === 'next');
 | 
			
		||||
      dispatch(importFetchedStatuses(response.data));
 | 
			
		||||
      dispatch(expandTimelineSuccess(timelineId, response.data, next ? next.uri : null, response.code === 206));
 | 
			
		||||
      dispatch(expandTimelineSuccess(timelineId, response.data, next ? next.uri : null, response.code === 206, isLoadingRecent, isLoadingMore));
 | 
			
		||||
      done();
 | 
			
		||||
    }).catch(error => {
 | 
			
		||||
      dispatch(expandTimelineFail(timelineId, error));
 | 
			
		||||
      dispatch(expandTimelineFail(timelineId, error, isLoadingMore));
 | 
			
		||||
      done();
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
@ -79,31 +96,42 @@ export const expandCommunityTimeline       = ({ maxId, onlyMedia } = {}, done =
 | 
			
		||||
export const expandAccountTimeline         = (accountId, { maxId, withReplies } = {}) => expandTimeline(`account:${accountId}${withReplies ? ':with_replies' : ''}`, `/api/v1/accounts/${accountId}/statuses`, { exclude_replies: !withReplies, max_id: maxId });
 | 
			
		||||
export const expandAccountFeaturedTimeline = accountId => expandTimeline(`account:${accountId}:pinned`, `/api/v1/accounts/${accountId}/statuses`, { pinned: true });
 | 
			
		||||
export const expandAccountMediaTimeline    = (accountId, { maxId } = {}) => expandTimeline(`account:${accountId}:media`, `/api/v1/accounts/${accountId}/statuses`, { max_id: maxId, only_media: true });
 | 
			
		||||
export const expandHashtagTimeline         = (hashtag, { maxId } = {}, done = noOp) => expandTimeline(`hashtag:${hashtag}`, `/api/v1/timelines/tag/${hashtag}`, { max_id: maxId }, done);
 | 
			
		||||
export const expandListTimeline            = (id, { maxId } = {}, done = noOp) => expandTimeline(`list:${id}`, `/api/v1/timelines/list/${id}`, { max_id: maxId }, done);
 | 
			
		||||
export const expandHashtagTimeline         = (hashtag, { maxId, tags } = {}, done = noOp) => {
 | 
			
		||||
  return expandTimeline(`hashtag:${hashtag}`, `/api/v1/timelines/tag/${hashtag}`, {
 | 
			
		||||
    max_id: maxId,
 | 
			
		||||
    any:    parseTags(tags, 'any'),
 | 
			
		||||
    all:    parseTags(tags, 'all'),
 | 
			
		||||
    none:   parseTags(tags, 'none'),
 | 
			
		||||
  }, done);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export function expandTimelineRequest(timeline) {
 | 
			
		||||
export function expandTimelineRequest(timeline, isLoadingMore) {
 | 
			
		||||
  return {
 | 
			
		||||
    type: TIMELINE_EXPAND_REQUEST,
 | 
			
		||||
    timeline,
 | 
			
		||||
    skipLoading: !isLoadingMore,
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export function expandTimelineSuccess(timeline, statuses, next, partial) {
 | 
			
		||||
export function expandTimelineSuccess(timeline, statuses, next, partial, isLoadingRecent, isLoadingMore) {
 | 
			
		||||
  return {
 | 
			
		||||
    type: TIMELINE_EXPAND_SUCCESS,
 | 
			
		||||
    timeline,
 | 
			
		||||
    statuses,
 | 
			
		||||
    next,
 | 
			
		||||
    partial,
 | 
			
		||||
    isLoadingRecent,
 | 
			
		||||
    skipLoading: !isLoadingMore,
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export function expandTimelineFail(timeline, error) {
 | 
			
		||||
export function expandTimelineFail(timeline, error, isLoadingMore) {
 | 
			
		||||
  return {
 | 
			
		||||
    type: TIMELINE_EXPAND_FAIL,
 | 
			
		||||
    timeline,
 | 
			
		||||
    error,
 | 
			
		||||
    skipLoading: !isLoadingMore,
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
import axios from 'axios';
 | 
			
		||||
import LinkHeader from 'http-link-header';
 | 
			
		||||
import ready from './ready';
 | 
			
		||||
import LinkHeader from './link_header';
 | 
			
		||||
 | 
			
		||||
export const getLinks = response => {
 | 
			
		||||
  const value = response.headers.link;
 | 
			
		||||
 | 
			
		||||
@ -68,10 +68,10 @@ class Account extends ImmutablePureComponent {
 | 
			
		||||
 | 
			
		||||
    if (hidden) {
 | 
			
		||||
      return (
 | 
			
		||||
        <div>
 | 
			
		||||
        <Fragment>
 | 
			
		||||
          {account.get('display_name')}
 | 
			
		||||
          {account.get('username')}
 | 
			
		||||
        </div>
 | 
			
		||||
        </Fragment>
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -37,6 +37,14 @@ class ColumnHeader extends React.PureComponent {
 | 
			
		||||
    animating: false,
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  historyBack = () => {
 | 
			
		||||
    if (window.history && window.history.length === 1) {
 | 
			
		||||
      this.context.router.history.push('/');
 | 
			
		||||
    } else {
 | 
			
		||||
      this.context.router.history.goBack();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  handleToggleClick = (e) => {
 | 
			
		||||
    e.stopPropagation();
 | 
			
		||||
    this.setState({ collapsed: !this.state.collapsed, animating: true });
 | 
			
		||||
@ -55,16 +63,22 @@ class ColumnHeader extends React.PureComponent {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  handleBackClick = () => {
 | 
			
		||||
    if (window.history && window.history.length === 1) this.context.router.history.push('/');
 | 
			
		||||
    else this.context.router.history.goBack();
 | 
			
		||||
    this.historyBack();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  handleTransitionEnd = () => {
 | 
			
		||||
    this.setState({ animating: false });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  handlePin = () => {
 | 
			
		||||
    if (!this.props.pinned) {
 | 
			
		||||
      this.historyBack();
 | 
			
		||||
    }
 | 
			
		||||
    this.props.onPin();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  render () {
 | 
			
		||||
    const { title, icon, active, children, pinned, onPin, multiColumn, extraButton, showBackButton, intl: { formatMessage } } = this.props;
 | 
			
		||||
    const { title, icon, active, children, pinned, multiColumn, extraButton, showBackButton, intl: { formatMessage } } = this.props;
 | 
			
		||||
    const { collapsed, animating } = this.state;
 | 
			
		||||
 | 
			
		||||
    const wrapperClassName = classNames('column-header__wrapper', {
 | 
			
		||||
@ -95,7 +109,7 @@ class ColumnHeader extends React.PureComponent {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (multiColumn && pinned) {
 | 
			
		||||
      pinButton = <button key='pin-button' className='text-btn column-header__setting-btn' onClick={onPin}><i className='fa fa fa-times' /> <FormattedMessage id='column_header.unpin' defaultMessage='Unpin' /></button>;
 | 
			
		||||
      pinButton = <button key='pin-button' className='text-btn column-header__setting-btn' onClick={this.handlePin}><i className='fa fa fa-times' /> <FormattedMessage id='column_header.unpin' defaultMessage='Unpin' /></button>;
 | 
			
		||||
 | 
			
		||||
      moveButtons = (
 | 
			
		||||
        <div key='move-buttons' className='column-header__setting-arrows'>
 | 
			
		||||
@ -104,7 +118,7 @@ class ColumnHeader extends React.PureComponent {
 | 
			
		||||
        </div>
 | 
			
		||||
      );
 | 
			
		||||
    } else if (multiColumn) {
 | 
			
		||||
      pinButton = <button key='pin-button' className='text-btn column-header__setting-btn' onClick={onPin}><i className='fa fa fa-plus' /> <FormattedMessage id='column_header.pin' defaultMessage='Pin' /></button>;
 | 
			
		||||
      pinButton = <button key='pin-button' className='text-btn column-header__setting-btn' onClick={this.handlePin}><i className='fa fa fa-plus' /> <FormattedMessage id='column_header.pin' defaultMessage='Pin' /></button>;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!pinned && (multiColumn || showBackButton)) {
 | 
			
		||||
 | 
			
		||||
@ -51,6 +51,10 @@ class Item extends React.PureComponent {
 | 
			
		||||
    const { index, onClick } = this.props;
 | 
			
		||||
 | 
			
		||||
    if (e.button === 0 && !(e.ctrlKey || e.metaKey)) {
 | 
			
		||||
      if (this.hoverToPlay()) {
 | 
			
		||||
        e.target.pause();
 | 
			
		||||
        e.target.currentTime = 0;
 | 
			
		||||
      }
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      onClick(index);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user