Browse Source

Merge tag 'v2.7.0' of github.com:tootsuite/mastodon

master
Matt Baer 5 years ago
parent
commit
7cfc852a59
100 changed files with 2602 additions and 1273 deletions
  1. +0
    -66
      .babelrc
  2. +1
    -0
      .buildpacks
  3. +45
    -46
      .circleci/config.yml
  4. +1
    -1
      .codeclimate.yml
  5. +2
    -2
      .env.nanobox
  6. +3
    -2
      .env.production.sample
  7. +0
    -4
      .env.test
  8. +1
    -1
      .env.vagrant
  9. +13
    -30
      .eslintignore
  10. +199
    -0
      .eslintrc.js
  11. +0
    -170
      .eslintrc.yml
  12. +20
    -5
      .github/ISSUE_TEMPLATE/bug_report.md
  13. +10
    -4
      .github/ISSUE_TEMPLATE/feature_request.md
  14. +10
    -0
      .github/ISSUE_TEMPLATE/support.md
  15. +1
    -1
      .nvmrc
  16. +0
    -9
      .postcssrc.yml
  17. +12
    -0
      .rubocop.yml
  18. +1
    -1
      .ruby-version
  19. +522
    -141
      AUTHORS.md
  20. +19
    -0
      Aptfile
  21. +330
    -0
      CHANGELOG.md
  22. +21
    -40
      CONTRIBUTING.md
  23. +17
    -14
      Dockerfile
  24. +54
    -51
      Gemfile
  25. +272
    -262
      Gemfile.lock
  26. +42
    -43
      README.md
  27. +12
    -6
      Vagrantfile
  28. +1
    -1
      app/chewy/statuses_index.rb
  29. +7
    -3
      app/controllers/about_controller.rb
  30. +5
    -3
      app/controllers/accounts_controller.rb
  31. +2
    -2
      app/controllers/activitypub/collections_controller.rb
  32. +1
    -1
      app/controllers/activitypub/inboxes_controller.rb
  33. +36
    -0
      app/controllers/admin/account_actions_controller.rb
  34. +1
    -0
      app/controllers/admin/account_moderation_notes_controller.rb
  35. +30
    -11
      app/controllers/admin/accounts_controller.rb
  36. +12
    -1
      app/controllers/admin/base_controller.rb
  37. +0
    -4
      app/controllers/admin/confirmations_controller.rb
  38. +44
    -0
      app/controllers/admin/dashboard_controller.rb
  39. +4
    -9
      app/controllers/admin/domain_blocks_controller.rb
  40. +18
    -0
      app/controllers/admin/followers_controller.rb
  41. +14
    -13
      app/controllers/admin/instances_controller.rb
  42. +6
    -0
      app/controllers/admin/invites_controller.rb
  43. +58
    -0
      app/controllers/admin/relays_controller.rb
  44. +23
    -53
      app/controllers/admin/reports_controller.rb
  45. +0
    -6
      app/controllers/admin/resets_controller.rb
  46. +0
    -6
      app/controllers/admin/roles_controller.rb
  47. +7
    -0
      app/controllers/admin/settings_controller.rb
  48. +0
    -27
      app/controllers/admin/silences_controller.rb
  49. +13
    -0
      app/controllers/admin/statuses_controller.rb
  50. +0
    -27
      app/controllers/admin/suspensions_controller.rb
  51. +44
    -0
      app/controllers/admin/tags_controller.rb
  52. +2
    -2
      app/controllers/admin/two_factor_authentications_controller.rb
  53. +58
    -0
      app/controllers/admin/warning_presets_controller.rb
  54. +10
    -6
      app/controllers/api/base_controller.rb
  55. +1
    -1
      app/controllers/api/v1/accounts/credentials_controller.rb
  56. +1
    -1
      app/controllers/api/v1/accounts/follower_accounts_controller.rb
  57. +1
    -1
      app/controllers/api/v1/accounts/following_accounts_controller.rb
  58. +32
    -0
      app/controllers/api/v1/accounts/pins_controller.rb
  59. +8
    -7
      app/controllers/api/v1/accounts/statuses_controller.rb
  60. +24
    -4
      app/controllers/api/v1/accounts_controller.rb
  61. +1
    -1
      app/controllers/api/v1/blocks_controller.rb
  62. +71
    -0
      app/controllers/api/v1/conversations_controller.rb
  63. +3
    -1
      app/controllers/api/v1/custom_emojis_controller.rb
  64. +72
    -0
      app/controllers/api/v1/endorsements_controller.rb
  65. +3
    -4
      app/controllers/api/v1/favourites_controller.rb
  66. +2
    -1
      app/controllers/api/v1/follow_requests_controller.rb
  67. +3
    -1
      app/controllers/api/v1/instances_controller.rb
  68. +3
    -3
      app/controllers/api/v1/lists/accounts_controller.rb
  69. +1
    -1
      app/controllers/api/v1/lists_controller.rb
  70. +12
    -14
      app/controllers/api/v1/mutes_controller.rb
  71. +3
    -4
      app/controllers/api/v1/notifications_controller.rb
  72. +1
    -7
      app/controllers/api/v1/reports_controller.rb
  73. +77
    -0
      app/controllers/api/v1/scheduled_statuses_controller.rb
  74. +1
    -1
      app/controllers/api/v1/statuses/favourited_by_accounts_controller.rb
  75. +2
    -2
      app/controllers/api/v1/statuses/reblogged_by_accounts_controller.rb
  76. +6
    -6
      app/controllers/api/v1/statuses_controller.rb
  77. +3
    -2
      app/controllers/api/v1/timelines/home_controller.rb
  78. +3
    -2
      app/controllers/api/v1/timelines/list_controller.rb
  79. +3
    -4
      app/controllers/api/v1/timelines/public_controller.rb
  80. +4
    -5
      app/controllers/api/v1/timelines/tag_controller.rb
  81. +1
    -0
      app/controllers/api/web/embeds_controller.rb
  82. +14
    -14
      app/controllers/application_controller.rb
  83. +14
    -1
      app/controllers/auth/confirmations_controller.rb
  84. +5
    -0
      app/controllers/auth/passwords_controller.rb
  85. +6
    -0
      app/controllers/auth/registrations_controller.rb
  86. +12
    -1
      app/controllers/auth/sessions_controller.rb
  87. +0
    -66
      app/controllers/authorize_follows_controller.rb
  88. +66
    -0
      app/controllers/authorize_interactions_controller.rb
  89. +5
    -0
      app/controllers/concerns/account_controller_concern.rb
  90. +0
    -21
      app/controllers/concerns/remote_account_controller_concern.rb
  91. +47
    -18
      app/controllers/concerns/signature_verification.rb
  92. +10
    -0
      app/controllers/custom_css_controller.rb
  93. +43
    -0
      app/controllers/directories_controller.rb
  94. +1
    -1
      app/controllers/emojis_controller.rb
  95. +6
    -1
      app/controllers/filters_controller.rb
  96. +1
    -1
      app/controllers/home_controller.rb
  97. +1
    -1
      app/controllers/intents_controller.rb
  98. +6
    -1
      app/controllers/invites_controller.rb
  99. +5
    -0
      app/controllers/media_controller.rb
  100. +14
    -0
      app/controllers/oauth/authorizations_controller.rb

+ 0
- 66
.babelrc View File

@@ -1,66 +0,0 @@
{
"presets": [
"react",
[
"env",
{
"exclude": ["transform-async-to-generator", "transform-regenerator"],
"loose": true,
"modules": false,
"targets": {
"browsers": ["last 2 versions", "IE >= 11", "iOS >= 9"]
}
}
]
],
"plugins": [
"syntax-dynamic-import",
["transform-object-rest-spread", { "useBuiltIns": true }],
"transform-decorators-legacy",
"transform-class-properties",
[
"react-intl",
{
"messagesDir": "./build/messages"
}
],
"preval"
],
"env": {
"development": {
"plugins": [
"transform-react-jsx-source",
"transform-react-jsx-self"
]
},
"production": {
"plugins": [
"lodash",
[
"transform-react-remove-prop-types",
{
"mode": "remove",
"removeImport": true,
"additionalLibraries": [
"react-immutable-proptypes"
]
}
],
"transform-react-inline-elements",
[
"transform-runtime",
{
"helpers": true,
"polyfill": false,
"regenerator": false
}
]
]
},
"test": {
"plugins": [
"transform-es2015-modules-commonjs"
]
}
}
}

+ 1
- 0
.buildpacks View File

@@ -1,3 +1,4 @@
https://github.com/heroku/heroku-buildpack-apt https://github.com/heroku/heroku-buildpack-apt
https://github.com/Scalingo/ffmpeg-buildpack
https://github.com/Scalingo/nodejs-buildpack https://github.com/Scalingo/nodejs-buildpack
https://github.com/Scalingo/ruby-buildpack https://github.com/Scalingo/ruby-buildpack

+ 45
- 46
.circleci/config.yml View File

@@ -3,7 +3,7 @@ version: 2
aliases: aliases:
- &defaults - &defaults
docker: docker:
- image: circleci/ruby:2.5.1-stretch-node
- image: circleci/ruby:2.6.0-stretch-node
environment: &ruby_environment environment: &ruby_environment
BUNDLE_APP_CONFIG: ./.bundle/ BUNDLE_APP_CONFIG: ./.bundle/
DB_HOST: localhost DB_HOST: localhost
@@ -13,6 +13,9 @@ aliases:
ALLOW_NOPAM: true ALLOW_NOPAM: true
CONTINUOUS_INTEGRATION: true CONTINUOUS_INTEGRATION: true
DISABLE_SIMPLECOV: true DISABLE_SIMPLECOV: true
PAM_ENABLED: true
PAM_DEFAULT_SERVICE: pam_test
PAM_CONTROLLED_SERVICE: pam_test_controlled
working_directory: ~/projects/mastodon/ working_directory: ~/projects/mastodon/


- &attach_workspace - &attach_workspace
@@ -64,12 +67,17 @@ aliases:


- run: ruby -e 'puts RUBY_VERSION' | tee /tmp/.ruby-version - run: ruby -e 'puts RUBY_VERSION' | tee /tmp/.ruby-version
- *restore_ruby_dependencies - *restore_ruby_dependencies
- run: bundle install --clean --jobs 16 --path ./vendor/bundle/ --retry 3 --with pam_authentication --without development production
- run: bundle install --clean --jobs 16 --path ./vendor/bundle/ --retry 3 --with pam_authentication --without development production && bundle clean
- save_cache: - save_cache:
key: v2-ruby-dependencies-{{ checksum "/tmp/.ruby-version" }}-{{ checksum "Gemfile.lock" }} key: v2-ruby-dependencies-{{ checksum "/tmp/.ruby-version" }}-{{ checksum "Gemfile.lock" }}
paths: paths:
- ./.bundle/ - ./.bundle/
- ./vendor/bundle/ - ./vendor/bundle/
- persist_to_workspace:
root: ~/projects/
paths:
- ./mastodon/.bundle/
- ./mastodon/vendor/bundle/


- &test_steps - &test_steps
steps: steps:
@@ -78,15 +86,6 @@ aliases:
- *install_system_dependencies - *install_system_dependencies
- run: sudo apt-get install -y ffmpeg - run: sudo apt-get install -y ffmpeg


- run: ruby -e 'puts RUBY_VERSION' | tee /tmp/.ruby-version
- *restore_ruby_dependencies

- restore_cache:
keys:
- precompiled-assets-{{ .Branch }}-{{ .Revision }}
- precompiled-assets-{{ .Branch }}-
- precompiled-assets-

- run: - run:
name: Prepare Tests name: Prepare Tests
command: ./bin/rails parallel:create parallel:load_schema parallel:prepare command: ./bin/rails parallel:create parallel:load_schema parallel:prepare
@@ -99,21 +98,21 @@ jobs:
<<: *defaults <<: *defaults
<<: *install_steps <<: *install_steps


install-ruby2.5:
install-ruby2.6:
<<: *defaults <<: *defaults
<<: *install_ruby_dependencies <<: *install_ruby_dependencies


install-ruby2.4:
install-ruby2.5:
<<: *defaults <<: *defaults
docker: docker:
- image: circleci/ruby:2.4.4-stretch-node
- image: circleci/ruby:2.5.3-stretch-node
environment: *ruby_environment environment: *ruby_environment
<<: *install_ruby_dependencies <<: *install_ruby_dependencies


install-ruby2.3:
install-ruby2.4:
<<: *defaults <<: *defaults
docker: docker:
- image: circleci/ruby:2.3.7-stretch-node
- image: circleci/ruby:2.4.5-stretch-node
environment: *ruby_environment environment: *ruby_environment
<<: *install_ruby_dependencies <<: *install_ruby_dependencies


@@ -122,52 +121,50 @@ jobs:
steps: steps:
- *attach_workspace - *attach_workspace
- *install_system_dependencies - *install_system_dependencies
- run: ruby -e 'puts RUBY_VERSION' | tee /tmp/.ruby-version
- *restore_ruby_dependencies
- run: ./bin/rails assets:precompile - run: ./bin/rails assets:precompile
- save_cache:
key: precompiled-assets-{{ .Branch }}-{{ .Revision }}
- persist_to_workspace:
root: ~/projects/
paths: paths:
- ./public/assets
- ./public/packs-test/
- ./mastodon/public/assets
- ./mastodon/public/packs-test/


test-ruby2.5:
test-ruby2.6:
<<: *defaults <<: *defaults
docker: docker:
- image: circleci/ruby:2.5.1-stretch-node
- image: circleci/ruby:2.6.0-stretch-node
environment: *ruby_environment environment: *ruby_environment
- image: circleci/postgres:10.3-alpine
- image: circleci/postgres:10.6-alpine
environment: environment:
POSTGRES_USER: root POSTGRES_USER: root
- image: circleci/redis:4.0.9-alpine
- image: circleci/redis:5.0.3-alpine3.8
<<: *test_steps <<: *test_steps


test-ruby2.4:
test-ruby2.5:
<<: *defaults <<: *defaults
docker: docker:
- image: circleci/ruby:2.4.4-stretch-node
- image: circleci/ruby:2.5.3-stretch-node
environment: *ruby_environment environment: *ruby_environment
- image: circleci/postgres:10.3-alpine
- image: circleci/postgres:10.6-alpine
environment: environment:
POSTGRES_USER: root POSTGRES_USER: root
- image: circleci/redis:4.0.9-alpine
- image: circleci/redis:4.0.12-alpine
<<: *test_steps <<: *test_steps


test-ruby2.3:
test-ruby2.4:
<<: *defaults <<: *defaults
docker: docker:
- image: circleci/ruby:2.3.7-stretch-node
- image: circleci/ruby:2.4.5-stretch-node
environment: *ruby_environment environment: *ruby_environment
- image: circleci/postgres:10.3-alpine
- image: circleci/postgres:10.6-alpine
environment: environment:
POSTGRES_USER: root POSTGRES_USER: root
- image: circleci/redis:4.0.9-alpine
- image: circleci/redis:4.0.12-alpine
<<: *test_steps <<: *test_steps


test-webui: test-webui:
<<: *defaults <<: *defaults
docker: docker:
- image: circleci/node:8.11.1-stretch
- image: circleci/node:8.15.0-stretch
steps: steps:
- *attach_workspace - *attach_workspace
- run: ./bin/retry yarn test:jest - run: ./bin/retry yarn test:jest
@@ -176,28 +173,34 @@ jobs:
<<: *defaults <<: *defaults
steps: steps:
- *attach_workspace - *attach_workspace
- run: ruby -e 'puts RUBY_VERSION' | tee /tmp/.ruby-version
- *restore_ruby_dependencies
- run: bundle exec i18n-tasks check-normalized - run: bundle exec i18n-tasks check-normalized
- run: bundle exec i18n-tasks unused - run: bundle exec i18n-tasks unused
- run: bundle exec i18n-tasks missing -t plural
- run: bundle exec i18n-tasks check-consistent-interpolations


workflows: workflows:
version: 2 version: 2
build-and-test: build-and-test:
jobs: jobs:
- install - install
- install-ruby2.5:
- install-ruby2.6:
requires: requires:
- install - install
- install-ruby2.4:
- install-ruby2.5:
requires: requires:
- install - install
- install-ruby2.3:
- install-ruby2.6
- install-ruby2.4:
requires: requires:
- install - install
- install-ruby2.6
- build: - build:
requires: requires:
- install-ruby2.5
- install-ruby2.6
- test-ruby2.6:
requires:
- install-ruby2.6
- build
- test-ruby2.5: - test-ruby2.5:
requires: requires:
- install-ruby2.5 - install-ruby2.5
@@ -206,13 +209,9 @@ workflows:
requires: requires:
- install-ruby2.4 - install-ruby2.4
- build - build
- test-ruby2.3:
requires:
- install-ruby2.3
- build
- test-webui: - test-webui:
requires: requires:
- install - install
- check-i18n: - check-i18n:
requires: requires:
- install-ruby2.5
- install-ruby2.6

+ 1
- 1
.codeclimate.yml View File

@@ -27,7 +27,7 @@ plugins:
enabled: true enabled: true
eslint: eslint:
enabled: true enabled: true
channel: eslint-4
channel: eslint-5
rubocop: rubocop:
enabled: true enabled: true
channel: rubocop-0-54 channel: rubocop-0-54


+ 2
- 2
.env.nanobox View File

@@ -136,8 +136,8 @@ SMTP_FROM_ADDRESS=notifications@${APP_NAME}.nanoapp.io
# Defaults to 60 seconds. Set to 0 to disable # Defaults to 60 seconds. Set to 0 to disable
# SWIFT_CACHE_TTL= # SWIFT_CACHE_TTL=


# Optional alias for S3 if you want to use Cloudfront or Cloudflare in front
# S3_CLOUDFRONT_HOST=
# Optional alias for S3 (e.g. to serve files on a custom domain, possibly using Cloudfront or Cloudflare)
# S3_ALIAS_HOST=


# Streaming API integration # Streaming API integration
# STREAMING_API_BASE_URL= # STREAMING_API_BASE_URL=


+ 3
- 2
.env.production.sample View File

@@ -134,8 +134,8 @@ SMTP_FROM_ADDRESS=notifications@example.com
# Defaults to 60 seconds. Set to 0 to disable # Defaults to 60 seconds. Set to 0 to disable
# SWIFT_CACHE_TTL= # SWIFT_CACHE_TTL=


# Optional alias for S3 if you want to use Cloudfront or Cloudflare in front
# S3_CLOUDFRONT_HOST=
# Optional alias for S3 (e.g. to serve files on a custom domain, possibly using Cloudfront or Cloudflare)
# S3_ALIAS_HOST=


# Streaming API integration # Streaming API integration
# STREAMING_API_BASE_URL= # STREAMING_API_BASE_URL=
@@ -162,6 +162,7 @@ STREAMING_CLUSTER_NUM=1
# LDAP_BIND_DN= # LDAP_BIND_DN=
# LDAP_PASSWORD= # LDAP_PASSWORD=
# LDAP_UID=cn # LDAP_UID=cn
# LDAP_SEARCH_FILTER="%{uid}=%{email}"


# PAM authentication (optional) # PAM authentication (optional)
# PAM authentication uses for the email generation the "email" pam variable # PAM authentication uses for the email generation the "email" pam variable


+ 0
- 4
.env.test View File

@@ -3,7 +3,3 @@ NODE_ENV=test
# Federation # Federation
LOCAL_DOMAIN=cb6e6126.ngrok.io LOCAL_DOMAIN=cb6e6126.ngrok.io
LOCAL_HTTPS=true LOCAL_HTTPS=true
# test pam authentication
PAM_ENABLED=true
PAM_DEFAULT_SERVICE=pam_test
PAM_CONTROLLED_SERVICE=pam_test_controlled

+ 1
- 1
.env.vagrant View File

@@ -1,2 +1,2 @@
VAGRANT=true VAGRANT=true
LOCAL_DOMAIN=mastodon.dev
LOCAL_DOMAIN=mastodon.local

+ 13
- 30
.eslintignore View File

@@ -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
- 0
.eslintrc.js View 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',
},
};

+ 0
- 170
.eslintrc.yml View File

@@ -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

+ 20
- 5
.github/ISSUE_TEMPLATE/bug_report.md View File

@@ -1,12 +1,27 @@
--- ---
name: Bug Report name: Bug Report
about: Create a report to help us improve
about: If something isn't working as expected


--- ---


[Issue text goes here].
<!-- Make sure that you are submitting a new bug that was not previously reported or already fixed -->


* * * *
<!-- Please use a concise and distinct title for the issue -->


- [ ] I searched or browsed the repo’s other issues to ensure this is not a duplicate.
- [ ] This bug happens on a [tagged release](https://github.com/tootsuite/mastodon/releases) and not on `master` (If you're a user, don't worry about this).
### Expected behaviour

<!-- What should have happened? -->

### Actual behaviour

<!-- What happened? -->

### Steps to reproduce the problem

<!-- What were you trying to do? -->

### Specifications

<!-- What version or commit hash of Mastodon did you find this bug in? -->

<!-- If a front-end issue, what browser and operating systems were you using? -->

+ 10
- 4
.github/ISSUE_TEMPLATE/feature_request.md View File

@@ -1,11 +1,17 @@
--- ---
name: Feature Request name: Feature Request
about: Suggest an idea for this project
about: I have a suggestion


--- ---


[Issue text goes here].
<!-- Please use a concise and distinct title for the issue -->


* * * *
<!-- Consider: Could it be implemented as a 3rd party app using the REST API instead? -->


- [ ] I searched or browsed the repo’s other issues to ensure this is not a duplicate.
### Pitch

<!-- Describe your idea for a feature. Make sure it has not already been suggested/implemented/turned down before -->

### Motivation

<!-- Why do you think this feature is needed? Who would benefit from it? -->

+ 10
- 0
.github/ISSUE_TEMPLATE/support.md View File

@@ -0,0 +1,10 @@
---
name: Support
about: Ask for help with your deployment

---

We primarily use GitHub as a bug and feature tracker. For usage questions, troubleshooting of deployments and other individual technical assistance, please use one of the resources below:

- https://discourse.joinmastodon.org
- #mastodon on irc.freenode.net

+ 1
- 1
.nvmrc View File

@@ -1 +1 @@
6
8

+ 0
- 9
.postcssrc.yml View File

@@ -1,9 +0,0 @@
plugins:
postcss-smart-import: {}
precss: {}
autoprefixer:
browsers:
- last 2 versions
- IE >= 11
- iOS >= 9
postcss-object-fit-images: {}

+ 12
- 0
.rubocop.yml View File

@@ -11,6 +11,7 @@ AllCops:
- 'Vagrantfile' - 'Vagrantfile'
- 'vendor/**/*' - 'vendor/**/*'
- 'lib/json_ld/*' - 'lib/json_ld/*'
- 'lib/templates/**/*'


Bundler/OrderedGems: Bundler/OrderedGems:
Enabled: false Enabled: false
@@ -61,6 +62,9 @@ Metrics/ParameterLists:
Metrics/PerceivedComplexity: Metrics/PerceivedComplexity:
Max: 20 Max: 20


Naming/MemoizedInstanceVariableName:
Enabled: false

Rails: Rails:
Enabled: true Enabled: true


@@ -70,6 +74,14 @@ Rails/HasAndBelongsToMany:
Rails/SkipsModelValidations: Rails/SkipsModelValidations:
Enabled: false Enabled: false


Rails/HttpStatus:
Enabled: false

Rails/Exit:
Exclude:
- 'lib/mastodon/*'
- 'lib/cli'

Style/ClassAndModuleChildren: Style/ClassAndModuleChildren:
Enabled: false Enabled: false




+ 1
- 1
.ruby-version View File

@@ -1 +1 @@
2.5.1
2.6.0

+ 522
- 141
AUTHORS.md View File

@@ -1,158 +1,199 @@
Authors
=======

Mastodon is available on [GitHub](https://github.com/tootsuite/mastodon) Mastodon is available on [GitHub](https://github.com/tootsuite/mastodon)
and provided thanks to the work of the following contributors: and provided thanks to the work of the following contributors:


* [Gargron](https://github.com/Gargron) * [Gargron](https://github.com/Gargron)
* [ykzts](https://github.com/ykzts) * [ykzts](https://github.com/ykzts)
* [mjankowski](https://github.com/mjankowski)
* [akihikodaki](https://github.com/akihikodaki) * [akihikodaki](https://github.com/akihikodaki)
* [ThibG](https://github.com/ThibG)
* [mjankowski](https://github.com/mjankowski)
* [unarist](https://github.com/unarist) * [unarist](https://github.com/unarist)
* [yiskah](https://github.com/yiskah)
* [m4sk1n](https://github.com/m4sk1n) * [m4sk1n](https://github.com/m4sk1n)
* [dependabot[bot]](https://github.com/apps/dependabot)
* [yiskah](https://github.com/yiskah)
* [nolanlawson](https://github.com/nolanlawson) * [nolanlawson](https://github.com/nolanlawson)
* [sorin-davidoi](https://github.com/sorin-davidoi) * [sorin-davidoi](https://github.com/sorin-davidoi)
* [ysksn](https://github.com/ysksn)
* [abcang](https://github.com/abcang) * [abcang](https://github.com/abcang)
* [ThibG](https://github.com/ThibG)
* [lynlynlynx](https://github.com/lynlynlynx) * [lynlynlynx](https://github.com/lynlynlynx)
* [alpaca-tc](https://github.com/alpaca-tc) * [alpaca-tc](https://github.com/alpaca-tc)
* [mayaeh](https://github.com/mayaeh)
* [renatolond](https://github.com/renatolond)
* [nclm](https://github.com/nclm) * [nclm](https://github.com/nclm)
* [ineffyble](https://github.com/ineffyble) * [ineffyble](https://github.com/ineffyble)
* [jeroenpraat](https://github.com/jeroenpraat) * [jeroenpraat](https://github.com/jeroenpraat)
* [blackle](https://github.com/blackle) * [blackle](https://github.com/blackle)
* [Quent-in](https://github.com/Quent-in) * [Quent-in](https://github.com/Quent-in)
* [JantsoP](https://github.com/JantsoP) * [JantsoP](https://github.com/JantsoP)
* [mabkenar](https://github.com/mabkenar)
* [nullkal](https://github.com/nullkal) * [nullkal](https://github.com/nullkal)
* [yookoala](https://github.com/yookoala) * [yookoala](https://github.com/yookoala)
* [ysksn](https://github.com/ysksn)
* [Kjwon15](https://github.com/Kjwon15)
* [shuheiktgw](https://github.com/shuheiktgw)
* [ashfurrow](https://github.com/ashfurrow) * [ashfurrow](https://github.com/ashfurrow)
* [eramdam](https://github.com/eramdam)
* [mayaeh](https://github.com/mayaeh)
* [Quenty31](https://github.com/Quenty31)
* [zunda](https://github.com/zunda) * [zunda](https://github.com/zunda)
* [ticky](https://github.com/ticky)
* [eramdam](https://github.com/eramdam)
* [takayamaki](https://github.com/takayamaki)
* [masarakki](https://github.com/masarakki) * [masarakki](https://github.com/masarakki)
* [ticky](https://github.com/ticky)
* [danhunsaker](https://github.com/danhunsaker)
* [ThisIsMissEm](https://github.com/ThisIsMissEm)
* [hcmiya](https://github.com/hcmiya)
* [stephenburgess8](https://github.com/stephenburgess8)
* [Wonderfall](https://github.com/Wonderfall) * [Wonderfall](https://github.com/Wonderfall)
* [matteoaquila](https://github.com/matteoaquila) * [matteoaquila](https://github.com/matteoaquila)
* [rkarabut](https://github.com/rkarabut) * [rkarabut](https://github.com/rkarabut)
* [stephenburgess8](https://github.com/stephenburgess8)
* [Kjwon15](https://github.com/Kjwon15)
* [Artoria2e5](https://github.com/Artoria2e5)
* [yukimochi](https://github.com/yukimochi) * [yukimochi](https://github.com/yukimochi)
* [Artoria2e5](https://github.com/Artoria2e5)
* [marrus-sh](https://github.com/marrus-sh) * [marrus-sh](https://github.com/marrus-sh)
* [krainboltgreene](https://github.com/krainboltgreene) * [krainboltgreene](https://github.com/krainboltgreene)
* [renatolond](https://github.com/renatolond)
* [BoFFire](https://github.com/BoFFire)
* [clworld](https://github.com/clworld)
* [danhunsaker](https://github.com/danhunsaker)
* [patf](https://github.com/patf) * [patf](https://github.com/patf)
* [Quenty31](https://github.com/Quenty31)
* [MitarashiDango](https://github.com/MitarashiDango)
* [Aldarone](https://github.com/Aldarone) * [Aldarone](https://github.com/Aldarone)
* [BoFFire](https://github.com/BoFFire)
* [clworld](https://github.com/clworld)
* [dracos](https://github.com/dracos)
* [SerCom_KC](mailto:sercom-kc@users.noreply.github.com)
* [Sylvhem](https://github.com/Sylvhem)
* [nightpool](https://github.com/nightpool)
* [MasterGroosha](https://github.com/MasterGroosha)
* [JeanGauthier](https://github.com/JeanGauthier) * [JeanGauthier](https://github.com/JeanGauthier)
* [kschaper](https://github.com/kschaper) * [kschaper](https://github.com/kschaper)
* [takayamaki](https://github.com/takayamaki)
* [MaciekBaron](https://github.com/MaciekBaron)
* [MitarashiDango](mailto:mitarashidango@users.noreply.github.com)
* [beatrix-bitrot](https://github.com/beatrix-bitrot)
* [adbelle](https://github.com/adbelle) * [adbelle](https://github.com/adbelle)
* [evanminto](https://github.com/evanminto) * [evanminto](https://github.com/evanminto)
* [mabkenar](https://github.com/mabkenar)
* [MightyPork](https://github.com/MightyPork) * [MightyPork](https://github.com/MightyPork)
* [beatrix-bitrot](https://github.com/beatrix-bitrot)
* [yhirano55](https://github.com/yhirano55) * [yhirano55](https://github.com/yhirano55)
* [camponez](https://github.com/camponez) * [camponez](https://github.com/camponez)
* [SerCom-KC](https://github.com/SerCom-KC)
* [aschmitz](https://github.com/aschmitz) * [aschmitz](https://github.com/aschmitz)
* [devkral](https://github.com/devkral)
* [fpiesche](https://github.com/fpiesche) * [fpiesche](https://github.com/fpiesche)
* [gandaro](https://github.com/gandaro) * [gandaro](https://github.com/gandaro)
* [johnsudaar](https://github.com/johnsudaar) * [johnsudaar](https://github.com/johnsudaar)
* [trebmuh](https://github.com/trebmuh) * [trebmuh](https://github.com/trebmuh)
* [Sylvhem](https://github.com/Sylvhem)
* [Rakib Hasan](mailto:rmhasan@gmail.com)
* [lindwurm](https://github.com/lindwurm) * [lindwurm](https://github.com/lindwurm)
* [victorhck](mailto:victorhck@geeko.site)
* [voidsatisfaction](https://github.com/voidsatisfaction) * [voidsatisfaction](https://github.com/voidsatisfaction)
* [neetshin](https://github.com/neetshin)
* [valentin2105](https://github.com/valentin2105)
* [hikari-no-yume](https://github.com/hikari-no-yume) * [hikari-no-yume](https://github.com/hikari-no-yume)
* [Angristan](https://github.com/Angristan)
* [angristan](https://github.com/angristan)
* [seefood](https://github.com/seefood) * [seefood](https://github.com/seefood)
* [jackjennings](https://github.com/jackjennings) * [jackjennings](https://github.com/jackjennings)
* [hcmiya](https://github.com/hcmiya)
* [nightpool](https://github.com/nightpool)
* [salvadorpla](https://github.com/salvadorpla)
* [spla](mailto:spla@mastodont.cat)
* [expenses](https://github.com/expenses) * [expenses](https://github.com/expenses)
* [walf443](https://github.com/walf443) * [walf443](https://github.com/walf443)
* [JoelQ](https://github.com/JoelQ) * [JoelQ](https://github.com/JoelQ)
* [mistydemeo](https://github.com/mistydemeo) * [mistydemeo](https://github.com/mistydemeo)
* [dunn](https://github.com/dunn) * [dunn](https://github.com/dunn)
* [xqus](https://github.com/xqus) * [xqus](https://github.com/xqus)
* [hugogameiro](https://github.com/hugogameiro)
* [pfm-eyesightjp](https://github.com/pfm-eyesightjp) * [pfm-eyesightjp](https://github.com/pfm-eyesightjp)
* [fakenine](https://github.com/fakenine) * [fakenine](https://github.com/fakenine)
* [tsuwatch](https://github.com/tsuwatch) * [tsuwatch](https://github.com/tsuwatch)
* [victorhck](https://github.com/victorhck) * [victorhck](https://github.com/victorhck)
* [ashleyhull-versent](https://github.com/ashleyhull-versent)
* [kedamaDQ](https://github.com/kedamaDQ)
* [puckipedia](https://github.com/puckipedia) * [puckipedia](https://github.com/puckipedia)
* [fvh-P](https://github.com/fvh-P)
* [contraexemplo](https://github.com/contraexemplo) * [contraexemplo](https://github.com/contraexemplo)
* [kazu9su](https://github.com/kazu9su) * [kazu9su](https://github.com/kazu9su)
* [Komic](https://github.com/Komic) * [Komic](https://github.com/Komic)
* [lmorchard](https://github.com/lmorchard)
* [diomed](https://github.com/diomed) * [diomed](https://github.com/diomed)
* [ariasuni](https://github.com/ariasuni)
* [Neetshin](mailto:neetshin@neetsh.in)
* [rainyday](https://github.com/rainyday) * [rainyday](https://github.com/rainyday)
* [ProgVal](https://github.com/ProgVal)
* [valentin2105](https://github.com/valentin2105)
* [yuntan](https://github.com/yuntan)
* [goofy-bz](mailto:goofy@babelzilla.org)
* [kadiix](https://github.com/kadiix) * [kadiix](https://github.com/kadiix)
* [kodacs](https://github.com/kodacs) * [kodacs](https://github.com/kodacs)
* [ProgVal](https://github.com/ProgVal)
* [rtucker](https://github.com/rtucker)
* [KScl](https://github.com/KScl)
* [sterdev](https://github.com/sterdev) * [sterdev](https://github.com/sterdev)
* [TheKinrar](https://github.com/TheKinrar) * [TheKinrar](https://github.com/TheKinrar)
* [AA4ch1](https://github.com/AA4ch1) * [AA4ch1](https://github.com/AA4ch1)
* [alexgleason](https://github.com/alexgleason) * [alexgleason](https://github.com/alexgleason)
* [cpytel](https://github.com/cpytel) * [cpytel](https://github.com/cpytel)
* [northerner](https://github.com/northerner) * [northerner](https://github.com/northerner)
* [hnrysmth](https://github.com/hnrysmth)
* [hugogameiro](https://github.com/hugogameiro)
* [fhemberger](https://github.com/fhemberger)
* [greysteil](https://github.com/greysteil)
* [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) * [JohnD28](https://github.com/JohnD28)
* [znz](https://github.com/znz) * [znz](https://github.com/znz)
* [Naouak](https://github.com/Naouak) * [Naouak](https://github.com/Naouak)
* [rtucker](https://github.com/rtucker)
* [pawelngei](https://github.com/pawelngei)
* [reneklacan](https://github.com/reneklacan) * [reneklacan](https://github.com/reneklacan)
* [KScl](https://github.com/KScl)
* [SerCom-KC](https://github.com/SerCom-KC)
* [ekiru](https://github.com/ekiru)
* [tcitworld](https://github.com/tcitworld) * [tcitworld](https://github.com/tcitworld)
* [geta6](https://github.com/geta6) * [geta6](https://github.com/geta6)
* [goofy-bz](https://github.com/goofy-bz)
* [happycoloredbanana](https://github.com/happycoloredbanana) * [happycoloredbanana](https://github.com/happycoloredbanana)
* [leopku](https://github.com/leopku) * [leopku](https://github.com/leopku)
* [SansPseudoFix](https://github.com/SansPseudoFix) * [SansPseudoFix](https://github.com/SansPseudoFix)
* [tomfhowe](https://github.com/tomfhowe) * [tomfhowe](https://github.com/tomfhowe)
* [noraworld](https://github.com/noraworld) * [noraworld](https://github.com/noraworld)
* [fvh-P](https://github.com/fvh-P)
* [theboss](https://github.com/theboss)
* [178inaba](https://github.com/178inaba) * [178inaba](https://github.com/178inaba)
* [devkral](https://github.com/devkral)
* [Aditoo17](https://github.com/Aditoo17)
* [alyssais](https://github.com/alyssais) * [alyssais](https://github.com/alyssais)
* [kodnaplakal](https://github.com/kodnaplakal) * [kodnaplakal](https://github.com/kodnaplakal)
* [stalker314314](https://github.com/stalker314314) * [stalker314314](https://github.com/stalker314314)
* [huertanix](https://github.com/huertanix) * [huertanix](https://github.com/huertanix)
* [genesixx](https://github.com/genesixx) * [genesixx](https://github.com/genesixx)
* [fhemberger](https://github.com/fhemberger)
* [halkeye](https://github.com/halkeye) * [halkeye](https://github.com/halkeye)
* [treby](https://github.com/treby) * [treby](https://github.com/treby)
* [d6rkaiz](https://github.com/d6rkaiz)
* [jpdevries](https://github.com/jpdevries) * [jpdevries](https://github.com/jpdevries)
* [rndm-stranger](https://github.com/rndm-stranger)
* [gdpelican](https://github.com/gdpelican)
* [kmichl](https://github.com/kmichl)
* [Kurtis Rainbolt-Greene](mailto:me@kurtisrainboltgreene.name)
* [saper](https://github.com/saper) * [saper](https://github.com/saper)
* [nevillepark](https://github.com/nevillepark) * [nevillepark](https://github.com/nevillepark)
* [ornithocoder](https://github.com/ornithocoder) * [ornithocoder](https://github.com/ornithocoder)
* [pierreozoux](https://github.com/pierreozoux) * [pierreozoux](https://github.com/pierreozoux)
* [ramlmn](https://github.com/ramlmn)
* [qguv](https://github.com/qguv)
* [Ram Lmn](mailto:ramlmn@users.noreply.github.com)
* [harukasan](https://github.com/harukasan) * [harukasan](https://github.com/harukasan)
* [stamak](https://github.com/stamak) * [stamak](https://github.com/stamak)
* [noellabo](https://github.com/noellabo)
* [Technowix](mailto:technowix@users.noreply.github.com)
* [Eychics](https://github.com/Eychics) * [Eychics](https://github.com/Eychics)
* [thor-the-norseman](https://github.com/thor-the-norseman)
* [Thor Harald Johansen](mailto:thj@thj.no)
* [0x70b1a5](https://github.com/0x70b1a5) * [0x70b1a5](https://github.com/0x70b1a5)
* [gled-rs](https://github.com/gled-rs) * [gled-rs](https://github.com/gled-rs)
* [Valentin_NC](mailto:valentin.ouvrard@nautile.sarl)
* [R0ckweb](https://github.com/R0ckweb) * [R0ckweb](https://github.com/R0ckweb)
* [caasi](https://github.com/caasi)
* [chr-1x](https://github.com/chr-1x)
* [esetomo](https://github.com/esetomo) * [esetomo](https://github.com/esetomo)
* [foxiehkins](https://github.com/foxiehkins) * [foxiehkins](https://github.com/foxiehkins)
* [sdukhovni](https://github.com/sdukhovni)
* [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) * [unsmell](https://github.com/unsmell)
* [valerauko](https://github.com/valerauko)
* [chriswmartin](https://github.com/chriswmartin) * [chriswmartin](https://github.com/chriswmartin)
* [vahnj](https://github.com/vahnj) * [vahnj](https://github.com/vahnj)
* [ikuradon](https://github.com/ikuradon) * [ikuradon](https://github.com/ikuradon)
* [AndreLewin](https://github.com/AndreLewin) * [AndreLewin](https://github.com/AndreLewin)
* [rinsuki](https://github.com/rinsuki)
* [0xflotus](https://github.com/0xflotus)
* [redtachyons](https://github.com/redtachyons) * [redtachyons](https://github.com/redtachyons)
* [thurloat](https://github.com/thurloat) * [thurloat](https://github.com/thurloat)
* [aaribaud](https://github.com/aaribaud) * [aaribaud](https://github.com/aaribaud)
* [Andrew](mailto:andrewlchronister@gmail.com)
* [estuans](https://github.com/estuans) * [estuans](https://github.com/estuans)
* [BenLubar](https://github.com/BenLubar)
* [dissolve](https://github.com/dissolve) * [dissolve](https://github.com/dissolve)
* [PurpleBooth](https://github.com/PurpleBooth) * [PurpleBooth](https://github.com/PurpleBooth)
* [bradurani](https://github.com/bradurani) * [bradurani](https://github.com/bradurani)
@@ -164,37 +205,50 @@ and provided thanks to the work of the following contributors:
* [cdutson](https://github.com/cdutson) * [cdutson](https://github.com/cdutson)
* [farlistener](https://github.com/farlistener) * [farlistener](https://github.com/farlistener)
* [DavidLibeau](https://github.com/DavidLibeau) * [DavidLibeau](https://github.com/DavidLibeau)
* [SirCmpwn](https://github.com/SirCmpwn)
* [MasterGroosha](https://github.com/MasterGroosha)
* [ddevault](https://github.com/ddevault)
* [Fjoerfoks](https://github.com/Fjoerfoks) * [Fjoerfoks](https://github.com/Fjoerfoks)
* [fmauNeko](https://github.com/fmauNeko) * [fmauNeko](https://github.com/fmauNeko)
* [gloaec](https://github.com/gloaec) * [gloaec](https://github.com/gloaec)
* [greysteil](https://github.com/greysteil)
* [Gomasy](https://github.com/Gomasy)
* [unstabler](https://github.com/unstabler) * [unstabler](https://github.com/unstabler)
* [potato4d](https://github.com/potato4d) * [potato4d](https://github.com/potato4d)
* [h-izumi](https://github.com/h-izumi) * [h-izumi](https://github.com/h-izumi)
* [ErikXXon](https://github.com/ErikXXon) * [ErikXXon](https://github.com/ErikXXon)
* [ian-kelling](https://github.com/ian-kelling) * [ian-kelling](https://github.com/ian-kelling)
* [immae](https://github.com/immae)
* [foozmeat](https://github.com/foozmeat) * [foozmeat](https://github.com/foozmeat)
* [jasonrhodes](https://github.com/jasonrhodes) * [jasonrhodes](https://github.com/jasonrhodes)
* [asm](https://github.com/asm)
* [Jason Snell](mailto:jason@newrelic.com)
* [jviide](https://github.com/jviide) * [jviide](https://github.com/jviide)
* [YuleZ](https://github.com/YuleZ)
* [crakaC](https://github.com/crakaC) * [crakaC](https://github.com/crakaC)
* [tkbky](https://github.com/tkbky) * [tkbky](https://github.com/tkbky)
* [Kaylee](mailto:kaylee@codethat.sucks)
* [Kazhnuz](https://github.com/Kazhnuz) * [Kazhnuz](https://github.com/Kazhnuz)
* [connyduck](https://github.com/connyduck)
* [Lindsey Bieda](mailto:lindseyb@users.noreply.github.com)
* [Lorenz Diener](mailto:halcyon@icosahedron.website)
* [alimony](https://github.com/alimony) * [alimony](https://github.com/alimony)
* [mig5](https://github.com/mig5) * [mig5](https://github.com/mig5)
* [ndarville](https://github.com/ndarville) * [ndarville](https://github.com/ndarville)
* [Abzol](https://github.com/Abzol) * [Abzol](https://github.com/Abzol)
* [pwoolcoc](https://github.com/pwoolcoc)
* [xPaw](https://github.com/xPaw) * [xPaw](https://github.com/xPaw)
* [petzah](https://github.com/petzah)
* [ignisf](https://github.com/ignisf)
* [raymestalez](https://github.com/raymestalez) * [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) * [sim6](https://github.com/sim6)
* [ekiru](https://github.com/ekiru)
* [Technowix](https://github.com/Technowix)
* [stemid](https://github.com/stemid)
* [sumdog](https://github.com/sumdog)
* [ThomasLeister](https://github.com/ThomasLeister) * [ThomasLeister](https://github.com/ThomasLeister)
* [mcat-ee](https://github.com/mcat-ee) * [mcat-ee](https://github.com/mcat-ee)
* [tototoshi](https://github.com/tototoshi) * [tototoshi](https://github.com/tototoshi)
* [TrashMacNugget](https://github.com/TrashMacNugget)
* [VirtuBox](https://github.com/VirtuBox) * [VirtuBox](https://github.com/VirtuBox)
* [Vladyslav](mailto:vaden@tuta.io)
* [kaniini](https://github.com/kaniini) * [kaniini](https://github.com/kaniini)
* [vayan](https://github.com/vayan) * [vayan](https://github.com/vayan)
* [yannicka](https://github.com/yannicka) * [yannicka](https://github.com/yannicka)
@@ -202,45 +256,61 @@ and provided thanks to the work of the following contributors:
* [zacanger](https://github.com/zacanger) * [zacanger](https://github.com/zacanger)
* [amazedkoumei](https://github.com/amazedkoumei) * [amazedkoumei](https://github.com/amazedkoumei)
* [anon5r](https://github.com/anon5r) * [anon5r](https://github.com/anon5r)
* [aus-social](https://github.com/aus-social)
* [imbsky](https://github.com/imbsky)
* [bsky](mailto:me@imbsky.net)
* [codl](https://github.com/codl) * [codl](https://github.com/codl)
* [cpsdqs](https://github.com/cpsdqs)
* [barzamin](https://github.com/barzamin) * [barzamin](https://github.com/barzamin)
* [fhalna](https://github.com/fhalna) * [fhalna](https://github.com/fhalna)
* [haoyayoi](https://github.com/haoyayoi) * [haoyayoi](https://github.com/haoyayoi)
* [ik11235](https://github.com/ik11235) * [ik11235](https://github.com/ik11235)
* [kawax](https://github.com/kawax) * [kawax](https://github.com/kawax)
* [007lva](https://github.com/007lva) * [007lva](https://github.com/007lva)
* [mbajur](https://github.com/mbajur)
* [matsurai25](https://github.com/matsurai25) * [matsurai25](https://github.com/matsurai25)
* [mecab](https://github.com/mecab) * [mecab](https://github.com/mecab)
* [nicobz25](https://github.com/nicobz25) * [nicobz25](https://github.com/nicobz25)
* [oliverkeeble](https://github.com/oliverkeeble) * [oliverkeeble](https://github.com/oliverkeeble)
* [pinfort](https://github.com/pinfort) * [pinfort](https://github.com/pinfort)
* [rbaumert](https://github.com/rbaumert) * [rbaumert](https://github.com/rbaumert)
* [rhoio](https://github.com/rhoio)
* [usagi-f](https://github.com/usagi-f) * [usagi-f](https://github.com/usagi-f)
* [vidarlee](https://github.com/vidarlee) * [vidarlee](https://github.com/vidarlee)
* [vjackson725](https://github.com/vjackson725) * [vjackson725](https://github.com/vjackson725)
* [wxcafe](https://github.com/wxcafe) * [wxcafe](https://github.com/wxcafe)
* [rinsuki](https://github.com/rinsuki)
* [新都心(Neet Shin)](mailto:nucx@dio-vox.com)
* [cygnan](https://github.com/cygnan) * [cygnan](https://github.com/cygnan)
* [Awea](https://github.com/Awea) * [Awea](https://github.com/Awea)
* [halcy](https://github.com/halcy) * [halcy](https://github.com/halcy)
* [bounshi](https://github.com/bounshi)
* [naaaaaaaaaaaf](https://github.com/naaaaaaaaaaaf)
* [8398a7](https://github.com/8398a7) * [8398a7](https://github.com/8398a7)
* [857b](https://github.com/857b) * [857b](https://github.com/857b)
* [insom](https://github.com/insom)
* [tachyons](https://github.com/tachyons)
* [Esteth](https://github.com/Esteth)
* [unascribed](https://github.com/unascribed) * [unascribed](https://github.com/unascribed)
* [Aguay-val](https://github.com/Aguay-val) * [Aguay-val](https://github.com/Aguay-val)
* [Akihiko Odaki](mailto:nekomanma@pixiv.co.jp)
* [knu](https://github.com/knu) * [knu](https://github.com/knu)
* [h3poteto](https://github.com/h3poteto)
* [unleashed](https://github.com/unleashed)
* [alxrcs](https://github.com/alxrcs) * [alxrcs](https://github.com/alxrcs)
* [console-cowboy](https://github.com/console-cowboy) * [console-cowboy](https://github.com/console-cowboy)
* [pointlessone](https://github.com/pointlessone) * [pointlessone](https://github.com/pointlessone)
* [Alkarex](https://github.com/Alkarex)
* [a2](https://github.com/a2) * [a2](https://github.com/a2)
* [0xa](https://github.com/0xa) * [0xa](https://github.com/0xa)
* [palindromordnilap](https://github.com/palindromordnilap)
* [virtualpain](https://github.com/virtualpain) * [virtualpain](https://github.com/virtualpain)
* [sapphirus](https://github.com/sapphirus) * [sapphirus](https://github.com/sapphirus)
* [amandavisconti](https://github.com/amandavisconti) * [amandavisconti](https://github.com/amandavisconti)
* [ameliavoncat](https://github.com/ameliavoncat) * [ameliavoncat](https://github.com/ameliavoncat)
* [ilpianista](https://github.com/ilpianista) * [ilpianista](https://github.com/ilpianista)
* [andydrop](https://github.com/andydrop)
* [Andreas Drop](mailto:andy@remline.de)
* [andi1984](https://github.com/andi1984)
* [schas002](https://github.com/schas002) * [schas002](https://github.com/schas002)
* [abackstrom](https://github.com/abackstrom)
* [jumbosushi](https://github.com/jumbosushi) * [jumbosushi](https://github.com/jumbosushi)
* [ayumin](https://github.com/ayumin) * [ayumin](https://github.com/ayumin)
* [BaptisteGelez](https://github.com/BaptisteGelez) * [BaptisteGelez](https://github.com/BaptisteGelez)
@@ -251,6 +321,7 @@ and provided thanks to the work of the following contributors:
* [brycied00d](https://github.com/brycied00d) * [brycied00d](https://github.com/brycied00d)
* [carlosjs23](https://github.com/carlosjs23) * [carlosjs23](https://github.com/carlosjs23)
* [cgxxx](https://github.com/cgxxx) * [cgxxx](https://github.com/cgxxx)
* [kibitan](https://github.com/kibitan)
* [chrisheninger](https://github.com/chrisheninger) * [chrisheninger](https://github.com/chrisheninger)
* [chris-martin](https://github.com/chris-martin) * [chris-martin](https://github.com/chris-martin)
* [DoubleMalt](https://github.com/DoubleMalt) * [DoubleMalt](https://github.com/DoubleMalt)
@@ -259,22 +330,34 @@ and provided thanks to the work of the following contributors:
* [chriswk](https://github.com/chriswk) * [chriswk](https://github.com/chriswk)
* [csu](https://github.com/csu) * [csu](https://github.com/csu)
* [kklleemm](https://github.com/kklleemm) * [kklleemm](https://github.com/kklleemm)
* [monsterpit-daggertooth](https://github.com/monsterpit-daggertooth)
* [colindean](https://github.com/colindean)
* [dachinat](https://github.com/dachinat)
* [multiple-creatures](https://github.com/multiple-creatures)
* [watilde](https://github.com/watilde) * [watilde](https://github.com/watilde)
* [daprice](https://github.com/daprice) * [daprice](https://github.com/daprice)
* [dar5hak](https://github.com/dar5hak) * [dar5hak](https://github.com/dar5hak)
* [kant](https://github.com/kant) * [kant](https://github.com/kant)
* [maxolasersquad](https://github.com/maxolasersquad)
* [singingwolfboy](https://github.com/singingwolfboy) * [singingwolfboy](https://github.com/singingwolfboy)
* [davidcelis](https://github.com/davidcelis) * [davidcelis](https://github.com/davidcelis)
* [davefp](https://github.com/davefp)
* [yipdw](https://github.com/yipdw) * [yipdw](https://github.com/yipdw)
* [debanshuk](https://github.com/debanshuk) * [debanshuk](https://github.com/debanshuk)
* [Derek Lewis](mailto:derekcecillewis@gmail.com)
* [dblandin](https://github.com/dblandin) * [dblandin](https://github.com/dblandin)
* [aranaur](https://github.com/aranaur)
* [Drew Gates](mailto:aranaur@users.noreply.github.com)
* [dtschust](https://github.com/dtschust)
* [Dryusdan](https://github.com/Dryusdan)
* [eai04191](https://github.com/eai04191)
* [d3vgru](https://github.com/d3vgru) * [d3vgru](https://github.com/d3vgru)
* [Elizafox](https://github.com/Elizafox) * [Elizafox](https://github.com/Elizafox)
* [ericblade](https://github.com/ericblade) * [ericblade](https://github.com/ericblade)
* [mikoim](https://github.com/mikoim) * [mikoim](https://github.com/mikoim)
* [espenronnevik](https://github.com/espenronnevik)
* [Finariel](https://github.com/Finariel)
* [siuying](https://github.com/siuying) * [siuying](https://github.com/siuying)
* [fwenzel](https://github.com/fwenzel)
* [GenbuHase](https://github.com/GenbuHase)
* [hattori6789](https://github.com/hattori6789) * [hattori6789](https://github.com/hattori6789)
* [algernon](https://github.com/algernon) * [algernon](https://github.com/algernon)
* [Fastbyte01](https://github.com/Fastbyte01) * [Fastbyte01](https://github.com/Fastbyte01)
@@ -283,22 +366,25 @@ and provided thanks to the work of the following contributors:
* [Fiaxhs](https://github.com/Fiaxhs) * [Fiaxhs](https://github.com/Fiaxhs)
* [reedcourty](https://github.com/reedcourty) * [reedcourty](https://github.com/reedcourty)
* [anneau](https://github.com/anneau) * [anneau](https://github.com/anneau)
* [lanodan](https://github.com/lanodan)
* [Harmon758](https://github.com/Harmon758)
* [HellPie](https://github.com/HellPie) * [HellPie](https://github.com/HellPie)
* [Habu-Kagumba](https://github.com/Habu-Kagumba) * [Habu-Kagumba](https://github.com/Habu-Kagumba)
* [hinaloe](https://github.com/hinaloe)
* [suzukaze](https://github.com/suzukaze) * [suzukaze](https://github.com/suzukaze)
* [Hiromi-Kai](https://github.com/Hiromi-Kai) * [Hiromi-Kai](https://github.com/Hiromi-Kai)
* [hishamhm](https://github.com/hishamhm)
* [musashino205](https://github.com/musashino205) * [musashino205](https://github.com/musashino205)
* [iwaim](https://github.com/iwaim) * [iwaim](https://github.com/iwaim)
* [valrus](https://github.com/valrus) * [valrus](https://github.com/valrus)
* [IMcD23](https://github.com/IMcD23) * [IMcD23](https://github.com/IMcD23)
* [yi0713](https://github.com/yi0713) * [yi0713](https://github.com/yi0713)
* [immae](https://github.com/immae)
* [iblech](https://github.com/iblech) * [iblech](https://github.com/iblech)
* [usbsnowcrash](https://github.com/usbsnowcrash)
* [jack-michaud](https://github.com/jack-michaud) * [jack-michaud](https://github.com/jack-michaud)
* [Floppy](https://github.com/Floppy) * [Floppy](https://github.com/Floppy)
* [loomchild](https://github.com/loomchild) * [loomchild](https://github.com/loomchild)
* [docjkl](https://github.com/docjkl)
* [jenkr55](https://github.com/jenkr55)
* [press5](https://github.com/press5)
* [TrollDecker](https://github.com/TrollDecker) * [TrollDecker](https://github.com/TrollDecker)
* [jmontane](https://github.com/jmontane) * [jmontane](https://github.com/jmontane)
* [jonathanklee](https://github.com/jonathanklee) * [jonathanklee](https://github.com/jonathanklee)
@@ -307,28 +393,33 @@ and provided thanks to the work of the following contributors:
* [joshuap](https://github.com/joshuap) * [joshuap](https://github.com/joshuap)
* [Tiwy57](https://github.com/Tiwy57) * [Tiwy57](https://github.com/Tiwy57)
* [xuv](https://github.com/xuv) * [xuv](https://github.com/xuv)
* [Jnsll](https://github.com/Jnsll)
* [June Sallou](mailto:jnsll@users.noreply.github.com)
* [j0k3r](https://github.com/j0k3r) * [j0k3r](https://github.com/j0k3r)
* [KEINOS](https://github.com/KEINOS) * [KEINOS](https://github.com/KEINOS)
* [futoase](https://github.com/futoase) * [futoase](https://github.com/futoase)
* [abjectio](https://github.com/abjectio)
* [Pneumaticat](https://github.com/Pneumaticat)
* [Kit Redgrave](mailto:qwertyitis@gmail.com)
* [Knut Erik](mailto:abjectio@users.noreply.github.com)
* [mkody](https://github.com/mkody) * [mkody](https://github.com/mkody)
* [connyduck](https://github.com/connyduck)
* [k0ta0uchi](https://github.com/k0ta0uchi) * [k0ta0uchi](https://github.com/k0ta0uchi)
* [KrzysiekJ](https://github.com/KrzysiekJ) * [KrzysiekJ](https://github.com/KrzysiekJ)
* [leowzukw](https://github.com/leowzukw) * [leowzukw](https://github.com/leowzukw)
* [lmorchard](https://github.com/lmorchard)
* [Tak](https://github.com/Tak)
* [cacheflow](https://github.com/cacheflow) * [cacheflow](https://github.com/cacheflow)
* [ldidry](https://github.com/ldidry) * [ldidry](https://github.com/ldidry)
* [jemus42](https://github.com/jemus42) * [jemus42](https://github.com/jemus42)
* [lfuelling](https://github.com/lfuelling) * [lfuelling](https://github.com/lfuelling)
* [Grabacr07](https://github.com/Grabacr07) * [Grabacr07](https://github.com/Grabacr07)
* [mistermantas](https://github.com/mistermantas) * [mistermantas](https://github.com/mistermantas)
* [mareklach](https://github.com/mareklach)
* [wirehack7](https://github.com/wirehack7) * [wirehack7](https://github.com/wirehack7)
* [martymcguire](https://github.com/martymcguire)
* [marvinkopf](https://github.com/marvinkopf) * [marvinkopf](https://github.com/marvinkopf)
* [otsune](https://github.com/otsune) * [otsune](https://github.com/otsune)
* [m-blc](https://github.com/m-blc)
* [Mathias B](mailto:10813340+mathias-b@users.noreply.github.com)
* [matt-auckland](https://github.com/matt-auckland) * [matt-auckland](https://github.com/matt-auckland)
* [webroo](https://github.com/webroo)
* [matthiasbeyer](https://github.com/matthiasbeyer)
* [mattjmattj](https://github.com/mattjmattj) * [mattjmattj](https://github.com/mattjmattj)
* [mtparet](https://github.com/mtparet) * [mtparet](https://github.com/mtparet)
* [maximeborges](https://github.com/maximeborges) * [maximeborges](https://github.com/maximeborges)
@@ -336,16 +427,21 @@ and provided thanks to the work of the following contributors:
* [michaeljdeeb](https://github.com/michaeljdeeb) * [michaeljdeeb](https://github.com/michaeljdeeb)
* [Themimitoof](https://github.com/Themimitoof) * [Themimitoof](https://github.com/Themimitoof)
* [cyweo](https://github.com/cyweo) * [cyweo](https://github.com/cyweo)
* [M1dgard](https://github.com/M1dgard)
* [Midgard](mailto:m1dgard@users.noreply.github.com)
* [mike-burns](https://github.com/mike-burns) * [mike-burns](https://github.com/mike-burns)
* [verymilan](https://github.com/verymilan) * [verymilan](https://github.com/verymilan)
* [milmazz](https://github.com/milmazz) * [milmazz](https://github.com/milmazz)
* [premist](https://github.com/premist)
* [Mnkai](https://github.com/Mnkai) * [Mnkai](https://github.com/Mnkai)
* [mitchhentges](https://github.com/mitchhentges) * [mitchhentges](https://github.com/mitchhentges)
* [moritzheiber](https://github.com/moritzheiber) * [moritzheiber](https://github.com/moritzheiber)
* [mouse-reeve](https://github.com/mouse-reeve) * [mouse-reeve](https://github.com/mouse-reeve)
* [Mozinet-fr](https://github.com/Mozinet-fr)
* [lae](https://github.com/lae) * [lae](https://github.com/lae)
* [Nanamachi](https://github.com/Nanamachi) * [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) * [ngerakines](https://github.com/ngerakines)
* [vonneudeck](https://github.com/vonneudeck) * [vonneudeck](https://github.com/vonneudeck)
* [Ninetailed](https://github.com/Ninetailed) * [Ninetailed](https://github.com/Ninetailed)
@@ -355,96 +451,381 @@ and provided thanks to the work of the following contributors:
* [norayr](https://github.com/norayr) * [norayr](https://github.com/norayr)
* [joyeusenoelle](https://github.com/joyeusenoelle) * [joyeusenoelle](https://github.com/joyeusenoelle)
* [OlivierNicole](https://github.com/OlivierNicole) * [OlivierNicole](https://github.com/OlivierNicole)
* [noppa](https://github.com/noppa)
* [Otakan951](https://github.com/Otakan951) * [Otakan951](https://github.com/Otakan951)
* [fahy](https://github.com/fahy) * [fahy](https://github.com/fahy)
* [PatrickRWells](https://github.com/PatrickRWells)
* [Pangoraw](https://github.com/Pangoraw) * [Pangoraw](https://github.com/Pangoraw)
* [pwoolcoc](https://github.com/pwoolcoc)
* [peterkeen](https://github.com/peterkeen) * [peterkeen](https://github.com/peterkeen)
* [petzah](https://github.com/petzah)
* [ignisf](https://github.com/ignisf)
* [pgate](https://github.com/pgate)
* [retokromer](https://github.com/retokromer)
* [rfwatson](https://github.com/rfwatson) * [rfwatson](https://github.com/rfwatson)
* [rfreebern](https://github.com/rfreebern) * [rfreebern](https://github.com/rfreebern)
* [Ryan Wade](mailto:ryan.wade@protonmail.com)
* [sylph01](https://github.com/sylph01) * [sylph01](https://github.com/sylph01)
* [S-H-GAMELINKS](https://github.com/S-H-GAMELINKS)
* [staticsafe](https://github.com/staticsafe) * [staticsafe](https://github.com/staticsafe)
* [snwh](https://github.com/snwh) * [snwh](https://github.com/snwh)
* [sts10](https://github.com/sts10)
* [skoji](https://github.com/skoji) * [skoji](https://github.com/skoji)
* [ScienJus](https://github.com/ScienJus) * [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)
* [sossii](https://github.com/sossii)
* [StefOfficiel](https://github.com/StefOfficiel)
* [svetlik](https://github.com/svetlik)
* [dereckson](https://github.com/dereckson)
* [theboss](https://github.com/theboss)
* [takp](https://github.com/takp)
* [tkusano](https://github.com/tkusano)
* [TheInventrix](https://github.com/TheInventrix)
* [shug0](https://github.com/shug0)
* [Fortyseven](https://github.com/Fortyseven)
* [tobypinder](https://github.com/tobypinder)
* [tomosm](https://github.com/tomosm)
* [TomoyaShibata](https://github.com/TomoyaShibata)
* [TrashMacNugget](https://github.com/TrashMacNugget)
* [treyssatvincent](https://github.com/treyssatvincent)
* [optikfluffel](https://github.com/optikfluffel)
* [vmincev](https://github.com/vmincev)
* [waldyrious](https://github.com/waldyrious)
* [tahnok](https://github.com/tahnok)
* [YDrogen](https://github.com/YDrogen)
* [YOSHIOKAEiichiro](https://github.com/YOSHIOKAEiichiro)
* [S-YOU](https://github.com/S-YOU)
* [YaQ00](https://github.com/YaQ00)
* [yanakend](https://github.com/yanakend)
* [orzFly](https://github.com/orzFly)
* [chansuke](https://github.com/chansuke)
* [yuntan](https://github.com/yuntan)
* [LogicalDash](https://github.com/LogicalDash)
* [ZiiX](https://github.com/ZiiX)
* [benklop](https://github.com/benklop)
* [caasi](https://github.com/caasi)
* [caesarologia](https://github.com/caesarologia)
* [chrolis](https://github.com/chrolis)
* [cormojs](https://github.com/cormojs)
* [cpsdqs](https://github.com/cpsdqs)
* [d0p1s4m4](https://github.com/d0p1s4m4)
* [evilny0](https://github.com/evilny0)
* [febrezo](https://github.com/febrezo)
* [fsubal](https://github.com/fsubal)
* [dikky1218](https://github.com/dikky1218)
* [gentarok](https://github.com/gentarok)
* [hakoai](https://github.com/hakoai)
* [chaosbunker](https://github.com/chaosbunker)
* [isati](https://github.com/isati)
* [jkap](https://github.com/jkap)
* [jirayudech](https://github.com/jirayudech)
* [jukper](https://github.com/jukper)
* [karlyeurl](https://github.com/karlyeurl)
* [kedamaDQ](https://github.com/kedamaDQ)
* [kuro5hin](https://github.com/kuro5hin)
* [maxypy](https://github.com/maxypy)
* [marcus-herrmann](https://github.com/marcus-herrmann)
* [mshrtkch](https://github.com/mshrtkch)
* [muan](https://github.com/muan)
* [rch850](https://github.com/rch850)
* [roikale](https://github.com/roikale)
* [rysiekpl](https://github.com/rysiekpl)
* [saturday06](https://github.com/saturday06)
* [scriptjunkie](https://github.com/scriptjunkie)
* [seekr](https://github.com/seekr)
* [syui](https://github.com/syui)
* [tackeyy](https://github.com/tackeyy)
* [tmyt](https://github.com/tmyt)
* [utam0k](https://github.com/utam0k)
* [vpzomtrrfrt](https://github.com/vpzomtrrfrt)
* [walfie](https://github.com/walfie)
* [y-temp4](https://github.com/y-temp4)
* [ymmtmdk](https://github.com/ymmtmdk)
* [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)
* [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)
* [Takayoshi Nishida](mailto:takayoshi.nishida@gmail.com)
* [Takayuki KUSANO](mailto:github@tkusano.jp)
* [TakesxiSximada](mailto:takesxi.sximada@gmail.com)
* [TheInventrix](mailto:theinventrix@users.noreply.github.com)
* [Thomas Alberola](mailto:thomas@needacoffee.fr)
* [Toby Deshane](mailto:fortyseven@users.noreply.github.com)
* [Toby Pinder](mailto:gigitrix@gmail.com)
* [Tomonori Murakami](mailto:crosslife777@gmail.com)
* [TomoyaShibata](mailto:wind.of.hometown@gmail.com)
* [Treyssat-Vincent Nino](mailto:treyssatvincent@users.noreply.github.com)
* [Udo Kramer](mailto:optik@fluffel.io)
* [Una](mailto:una@unascribed.com)
* [Ushitora Anqou](mailto:ushitora_anqou@yahoo.co.jp)
* [Valentin Lorentz](mailto:progval+git@progval.net)
* [Vladimir Mincev](mailto:vladimir@canicinteractive.com)
* [Waldir Pimenta](mailto:waldyrious@gmail.com)
* [Wesley Ellis](mailto:tahnok@gmail.com)
* [Wiktor](mailto:wiktor@metacode.biz)
* [Wonderfall](mailto:wonderfall@schrodinger.io)
* [YDrogen](mailto:ydrogen45@gmail.com)
* [YMHuang](mailto:ymhuang@fmbase.tw)
* [YOSHIOKA Eiichiro](mailto:yoshioka.eiichiro@gmail.com)
* [YOU](mailto:stackexchange.you@gmail.com)
* [YaQ](mailto:i_k_o_m_a_7@yahoo.co.jp)
* [Yanaken](mailto:yanakend@gmail.com)
* [Yann Klis](mailto:yann.klis@gmail.com)
* [Yeechan Lu](mailto:wz.bluesnow@gmail.com)
* [Yusuke Abe](mailto:moonset20@gmail.com)
* [Zachary Spector](mailto:logicaldash@gmail.com)
* [ZiiX](mailto:ziix@users.noreply.github.com)
* [asria-jp](mailto:is@alicematic.com)
* [ava](mailto:vladooku@users.noreply.github.com)
* [benklop](mailto:benklop@gmail.com)
* [bsky](mailto:git@imbsky.net)
* [caesarologia](mailto:lopesgemelli.1@gmail.com)
* [cbayerlein](mailto:c.bayerlein@gmail.com)
* [chrolis](mailto:chrolis@users.noreply.github.com)
* [cormo](mailto:cormorant2+github@gmail.com)
* [d0p1](mailto:dopi-sama@hush.com)
* [evilny0](mailto:evilny0@moomoocamp.net)
* [febrezo](mailto:felixbrezo@gmail.com)
* [fsubal](mailto:fsubal@users.noreply.github.com)
* [fusshi-](mailto:dikky1218@users.noreply.github.com)
* [gentaro](mailto:gentaroooo@gmail.com)
* [hakoai](mailto:hk--76@qa2.so-net.ne.jp)
* [haosbvnker](mailto:github@chaosbunker.com)
* [isati](mailto:phil@juchnowi.cz)
* [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)
* [mhe](mailto:mail@marcus-herrmann.com)
* [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)
* [roikale](mailto:roikale@users.noreply.github.com)
* [rysiekpl](mailto:rysiek@hackerspace.pl)
* [saturday06](mailto:dyob@lunaport.net)
* [scriptjunkie](mailto:scriptjunkie@scriptjunkie.us)
* [seekr](mailto:mario.drs@gmail.com)
* [sundevour](mailto:31990469+sundevour@users.noreply.github.com)
* [syui](mailto:syui@users.noreply.github.com)
* [tackeyy](mailto:mailto.takita.yusuke@gmail.com)
* [tateisu](mailto:tateisu@gmail.com)
* [tmyt](mailto:shigure@refy.net)
* [trevDev()](mailto:trev@trevdev.ca)
* [utam0k](mailto:k0ma@utam0k.jp)
* [vpzomtrrfrt](mailto:vpzomtrrfrt@gmail.com)
* [walfie](mailto:walfington@gmail.com)
* [y-temp4](mailto:y.temp4@gmail.com)
* [ymmtmdk](mailto:ymmtmdk@gmail.com)
* [yoshipc](mailto:yoooo@yoshipc.net)
* [Özcan Zafer AYAN](mailto:ozcanzaferayan@gmail.com)
* [ばん](mailto:detteiu0321@gmail.com)
* [みたらしだんご](mailto:mitarashidango@users.noreply.github.com)
* [りんすき](mailto:6533808+rinsuki@users.noreply.github.com)
* [ヨイツの賢狼ホロ | 3rd style](mailto:horo@yoitsu.moe)
* [猫吸血鬼ディフリス / 猫ロキP](mailto:deflis@gmail.com)
* [艮 鮟鱇](mailto:ushitora_anqou@yahoo.co.jp)
* [西小倉宏信](mailto:nishiko@mindia.jp)
* [雨宮美羽](mailto:k737566@gmail.com)


This document is provided for informational purposes only. Since it is only updated once per release, the version you are looking at may be currently out of date. To see the full list of contributors, consider looking at the [git history](https://github.com/tootsuite/mastodon/graphs/contributors) instead. This document is provided for informational purposes only. Since it is only updated once per release, the version you are looking at may be currently out of date. To see the full list of contributors, consider looking at the [git history](https://github.com/tootsuite/mastodon/graphs/contributors) instead.

## Translators

Following people have contributed to translation of Mastodon:

- **Arabic**
- ButterflyOfFire
- **Asturian**
- ButterflyOfFire
- Enol P.
- **Basque**
- Aitzol
- ButterflyOfFire
- Gorka Azkarate
- Osoitz
- Peru Iparragirre
- **Bulgarian**
- ButterflyOfFire
- **Catalan**
- ButterflyOfFire
- Joan Montané
- Jose Luis
- spla
- **Chinese (Hong Kong)**
- ButterflyOfFire
- Luzi Leung
- **Chinese (Simplified)**
- Allen Zhong
- ButterflyOfFire
- SerCom_KC
- **Chinese (Traditional)**
- ButterflyOfFire
- James58899
- Jeff Huang
- S1ttidoe477
- SHA265
- **Corsican**
- Alix D. R.
- ButterflyOfFire
- **Croatian**
- ButterflyOfFire
- **Czech**
- ButterflyOfFire
- Lorem Ipsum
- Marek Ľach
- **Danish**
- ButterflyOfFire
- Rasmus Sæderup
- **Dutch**
- ButterflyOfFire
- Jelv
- jeroenpraat
- rscmbbng
- **English**
- ButterflyOfFire
- Renato "Lond" Cerqueira
- **Esperanto**
- ButterflyOfFire
- Jeong Arm
- Martin Bodin
- Mélanie Chauvel
- Vanege
- tuxayo/Victor Grousset
- **Finnish**
- ButterflyOfFire
- Jonne Arjoranta
- S Heija
- Taru Luojola
- **French**
- Alda Marteau-Hardi
- Alix D. R.
- Baptiste Jonglez
- ButterflyOfFire
- Franck Paul
- Jean-Baptiste Holcroft
- Jonathan Chan
- Letiteuf55
- Martin Bodin
- Mélanie Chauvel
- Olivier Humbert
- Paul Marques Mota
- Sylvhem
- Technowix
- Thibaut Girka
- Théodore
- azenet
- codl
- **Galician**
- ButterflyOfFire
- Xose M.
- manequim
- **Georgian**
- ButterflyOfFire
- **German**
- Benedikt Geißler
- ButterflyOfFire
- Daniel
- Eugen Rochko
- Koyu Berteon
- Patrick Figel
- Weblate Admin
- averageunicorn
- ePirat
- koyu
- larsreineke
- lilo
- **Greek**
- Antonis
- ButterflyOfFire
- Dimitris Maroulidis
- Konstantinos Grevenitis
- **Hebrew**
- ButterflyOfFire
- Ira
- Yaron Shahrabani
- **Hungarian**
- Adam Paszternak
- ButterflyOfFire
- Tibike Miklós
- **Ido**
- ButterflyOfFire
- **Indonesian**
- Alfiana Sibuea
- ButterflyOfFire
- Dito Kurnia Pratama
- Eirworks
- afachri
- se7entime
- **Italian**
- Alessandro Levati
- ButterflyOfFire
- Giuseppe Pignataro
- Stefano
- **Japanese**
- ButterflyOfFire
- Kumasun Morino
- Yamagishi Kazutoshi
- mayaeh
- osapon
- unarist
- 小鳥遊まりあ
- 森の子リスのミーコの大冒険
- **Korean**
- ButterflyOfFire
- Jeong Arm
- Minori Hiraoka
- Yamagishi Kazutoshi
- **Malay**
- ButterflyOfFire
- Muhammad Nur Hidayat (MNH48)
- **Norwegian (old code)**
- ButterflyOfFire
- Espen Rønnevik
- Tale
- **Occitan**
- ButterflyOfFire
- Maxenç
- Quenti2
- Quentí
- **Persian**
- ButterflyOfFire
- Masoud Abkenar
- **Polish**
- ButterflyOfFire
- Jakub Mendyk
- Marcin Mikołajczak
- Marek Ľach
- Stasiek Michalski
- krkk
- **Portuguese**
- ButterflyOfFire
- Hugo Gameiro
- manequim
- **Portuguese (Brazil)**
- André Andrade
- Anna e só
- ButterflyOfFire
- Renato "Lond" Cerqueira
- **Romanian**
- ButterflyOfFire
- adrianbblk
- **Russian**
- Andrew Zyabin
- ButterflyOfFire
- Evgeny Petrov
- Yaron Shahrabani
- **Serbian**
- Branko Kokanovic
- Burekz Finezt
- ButterflyOfFire
- **Serbian (latin)**
- ButterflyOfFire
- **Slovak**
- ButterflyOfFire
- Ivan Pleva
- Lorem Ipsum
- Marek Ľach
- Peter
- **Slovenian**
- ButterflyOfFire
- Kristijan Tkalec
- **Spanish**
- Angeles Broullón
- Antón López
- ButterflyOfFire
- Carlos Mondragon
- David Charte
- Emmanuel
- Lothar Wolf
- Pablo de la Concepción Sanz
- **Swedish**
- ButterflyOfFire
- Elias Mårtenson
- Isak Holmström
- Shellkr
- Stefan Midjich
- Tim Stahel
- **Telugu**
- ButterflyOfFire
- Joseph Nuthalapati
- Ranjith Tellakula
- avndp
- **Thai**
- ButterflyOfFire
- **Turkish**
- ButterflyOfFire
- **Ukrainian**
- ButterflyOfFire
- Ivan Verchenko
- alexcleac
- **Welsh**
- ButterflyOfFire
- Jaz-Michael King
- Kevin Beynon
- Owain Rhys Lewis
- Renato "Lond" Cerqueira
- Rhoslyn Prys
- carl morris
- **Armenian**
- ButterflyOfFire
- **Latvian**
- ButterflyOfFire
- **Tamil**
- ButterflyOfFire
- Prasanna Venkadesh

+ 19
- 0
Aptfile View File

@@ -5,6 +5,25 @@ libidn11
libidn11-dev libidn11-dev
libpq-dev libpq-dev
libprotobuf-dev libprotobuf-dev
libssl-dev
libxdamage1 libxdamage1
libxfixes3 libxfixes3
protobuf-compiler protobuf-compiler
zlib1g-dev
libcairo2
libcroco3
libdatrie1
libgdk-pixbuf2.0-0
libgraphite2-3
libharfbuzz0b
libpango-1.0-0
libpangocairo-1.0-0
libpangoft2-1.0-0
libpixman-1-0
librsvg2-2
libthai-data
libthai0
libvpx5
libxcb-render0
libxcb-shm0
libxrender1

+ 330
- 0
CHANGELOG.md View File

@@ -0,0 +1,330 @@
Changelog
=========

All notable changes to this project will be documented in this file.

## [2.7.0] - 2019-01-20
### Added

- Add link for adding a user to a list from their profile ([namelessGonbai](https://github.com/tootsuite/mastodon/pull/9062))
- Add joining several hashtags in a single column ([gdpelican](https://github.com/tootsuite/mastodon/pull/8904))
- Add volume sliders for videos ([sumdog](https://github.com/tootsuite/mastodon/pull/9366))
- Add a tooltip explaining what a locked account is ([pawelngei](https://github.com/tootsuite/mastodon/pull/9403))
- Add preloaded cache for common JSON-LD contexts ([ThibG](https://github.com/tootsuite/mastodon/pull/9412))
- Add profile directory ([Gargron](https://github.com/tootsuite/mastodon/pull/9427))
- Add setting to not group reblogs in home feed ([ThibG](https://github.com/tootsuite/mastodon/pull/9248))
- Add admin ability to remove a user's header image ([ThibG](https://github.com/tootsuite/mastodon/pull/9495))
- Add account hashtags to ActivityPub actor JSON ([Gargron](https://github.com/tootsuite/mastodon/pull/9450))
- Add error message for avatar image that's too large ([sumdog](https://github.com/tootsuite/mastodon/pull/9518))
- Add notification quick-filter bar ([pawelngei](https://github.com/tootsuite/mastodon/pull/9399))
- Add new first-time tutorial ([Gargron](https://github.com/tootsuite/mastodon/pull/9531))
- Add moderation warnings ([Gargron](https://github.com/tootsuite/mastodon/pull/9519))
- Add emoji codepoint mappings for v11.0 ([Gargron](https://github.com/tootsuite/mastodon/pull/9618))
- Add REST API for creating an account ([Gargron](https://github.com/tootsuite/mastodon/pull/9572))
- Add support for Malayalam in language filter ([tachyons](https://github.com/tootsuite/mastodon/pull/9624))
- Add exclude_reblogs option to account statuses API ([Gargron](https://github.com/tootsuite/mastodon/pull/9640))
- Add local followers page to admin account UI ([chr-1x](https://github.com/tootsuite/mastodon/pull/9610))
- Add healthcheck commands to docker-compose.yml ([BenLubar](https://github.com/tootsuite/mastodon/pull/9143))
- Add handler for Move activity to migrate followers ([Gargron](https://github.com/tootsuite/mastodon/pull/9629))
- Add CSV export for lists and domain blocks ([Gargron](https://github.com/tootsuite/mastodon/pull/9677))
- Add `tootctl accounts follow ACCT` ([Gargron](https://github.com/tootsuite/mastodon/pull/9414))
- Add scheduled statuses ([Gargron](https://github.com/tootsuite/mastodon/pull/9706))
- Add immutable caching for S3 objects ([nolanlawson](https://github.com/tootsuite/mastodon/pull/9722))
- Add cache to custom emojis API ([Gargron](https://github.com/tootsuite/mastodon/pull/9732))
- Add preview cards to non-detailed statuses on public pages ([Gargron](https://github.com/tootsuite/mastodon/pull/9714))
- Add `mod` and `moderator` to list of default reserved usernames ([Gargron](https://github.com/tootsuite/mastodon/pull/9713))
- Add quick links to the admin interface in the web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/8545))
- Add `tootctl domains crawl` ([Gargron](https://github.com/tootsuite/mastodon/pull/9809))
- Add attachment list fallback to public pages ([ThibG](https://github.com/tootsuite/mastodon/pull/9780))
- Add `tootctl --version` ([Gargron](https://github.com/tootsuite/mastodon/pull/9835))
- Add information about how to opt-in to the directory on the directory ([Gargron](https://github.com/tootsuite/mastodon/pull/9834))
- Add timeouts for S3 ([Gargron](https://github.com/tootsuite/mastodon/pull/9842))
- Add support for non-public reblogs from ActivityPub ([Gargron](https://github.com/tootsuite/mastodon/pull/9841))
- Add sending of `Reject` activity when sending a `Block` activity ([ThibG](https://github.com/tootsuite/mastodon/pull/9811))

### Changed

- Temporarily pause timeline if mouse moved recently ([lmorchard](https://github.com/tootsuite/mastodon/pull/9200))
- Change the password form order ([mayaeh](https://github.com/tootsuite/mastodon/pull/9267))
- Redesign admin UI for accounts ([Gargron](https://github.com/tootsuite/mastodon/pull/9340), [Gargron](https://github.com/tootsuite/mastodon/pull/9643))
- Redesign admin UI for instances/domain blocks ([Gargron](https://github.com/tootsuite/mastodon/pull/9645))
- Swap avatar and header input fields in profile page ([ThibG](https://github.com/tootsuite/mastodon/pull/9271))
- When posting in mobile mode, go back to previous history location ([ThibG](https://github.com/tootsuite/mastodon/pull/9502))
- Split out is_changing_upload from is_submitting ([ThibG](https://github.com/tootsuite/mastodon/pull/9536))
- Back to the getting-started when pins the timeline. ([kedamaDQ](https://github.com/tootsuite/mastodon/pull/9561))
- Allow unauthenticated REST API access to GET /api/v1/accounts/:id/statuses ([Gargron](https://github.com/tootsuite/mastodon/pull/9573))
- Limit maximum visibility of local silenced users to unlisted ([ThibG](https://github.com/tootsuite/mastodon/pull/9583))
- Change API error message for unconfirmed accounts ([noellabo](https://github.com/tootsuite/mastodon/pull/9625))
- Change the icon to "reply-all" when it's a reply to other accounts ([mayaeh](https://github.com/tootsuite/mastodon/pull/9378))
- Do not ignore federated reports targetting already-reported accounts ([ThibG](https://github.com/tootsuite/mastodon/pull/9534))
- Upgrade default Ruby version to 2.6.0 ([Gargron](https://github.com/tootsuite/mastodon/pull/9688))
- Change e-mail digest frequency ([Gargron](https://github.com/tootsuite/mastodon/pull/9689))
- Change Docker images for Tor support in docker-compose.yml ([Sir-Boops](https://github.com/tootsuite/mastodon/pull/9438))
- Display fallback link card thumbnail when none is given ([Gargron](https://github.com/tootsuite/mastodon/pull/9715))
- Change account bio length validation to ignore mention domains and URLs ([Gargron](https://github.com/tootsuite/mastodon/pull/9717))
- Use configured contact user for "anonymous" federation activities ([yukimochi](https://github.com/tootsuite/mastodon/pull/9661))
- Change remote interaction dialog to use specific actions instead of generic "interact" ([Gargron](https://github.com/tootsuite/mastodon/pull/9743))
- Always re-fetch public key when signature verification fails to support blind key rotation ([ThibG](https://github.com/tootsuite/mastodon/pull/9667))
- Make replies to boosts impossible, connect reply to original status instead ([valerauko](https://github.com/tootsuite/mastodon/pull/9129))
- Change e-mail MX validation to check both A and MX records against blacklist ([Gargron](https://github.com/tootsuite/mastodon/pull/9489))
- Hide floating action button on search and getting started pages ([tmm576](https://github.com/tootsuite/mastodon/pull/9826))
- Redesign public hashtag page to use a masonry layout ([Gargron](https://github.com/tootsuite/mastodon/pull/9822))
- Use `summary` as summary instead of content warning for converted ActivityPub objects ([Gargron](https://github.com/tootsuite/mastodon/pull/9823))
- Display a double reply arrow on public pages for toots that are replies ([ThibG](https://github.com/tootsuite/mastodon/pull/9808))
- Change admin UI right panel size to be wider ([Kjwon15](https://github.com/tootsuite/mastodon/pull/9768))

### Removed

- Remove links to bridge.joinmastodon.org (non-functional) ([Gargron](https://github.com/tootsuite/mastodon/pull/9608))
- Remove LD-Signatures from activities that do not need them ([ThibG](https://github.com/tootsuite/mastodon/pull/9659))

### Fixed

- Remove unused computation of reblog references from updateTimeline ([ThibG](https://github.com/tootsuite/mastodon/pull/9244))
- Fix loaded embeds resetting if a status arrives from API again ([ThibG](https://github.com/tootsuite/mastodon/pull/9270))
- Fix race condition causing shallow status with only a "favourited" attribute ([ThibG](https://github.com/tootsuite/mastodon/pull/9272))
- Remove intermediary arrays when creating hash maps from results ([Gargron](https://github.com/tootsuite/mastodon/pull/9291))
- Extract counters from accounts table to account_stats table to improve performance ([Gargron](https://github.com/tootsuite/mastodon/pull/9295))
- Change identities id column to a bigint ([Gargron](https://github.com/tootsuite/mastodon/pull/9371))
- Fix conversations API pagination ([ThibG](https://github.com/tootsuite/mastodon/pull/9407))
- Improve account suspension speed and completeness ([Gargron](https://github.com/tootsuite/mastodon/pull/9290))
- Fix thread depth computation in statuses_controller ([ThibG](https://github.com/tootsuite/mastodon/pull/9426))
- Fix database deadlocks by moving account stats update outside transaction ([ThibG](https://github.com/tootsuite/mastodon/pull/9437))
- Escape HTML in profile name preview in profile settings ([pawelngei](https://github.com/tootsuite/mastodon/pull/9446))
- Use same CORS policy for /@:username and /users/:username ([ThibG](https://github.com/tootsuite/mastodon/pull/9485))
- Make custom emoji domains case insensitive ([Esteth](https://github.com/tootsuite/mastodon/pull/9474))
- Various fixes to scrollable lists and media gallery ([ThibG](https://github.com/tootsuite/mastodon/pull/9501))
- Fix bootsnap cache directory being declared relatively ([Gargron](https://github.com/tootsuite/mastodon/pull/9511))
- Fix timeline pagination in the web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/9516))
- Fix padding on dropdown elements in preferences ([ThibG](https://github.com/tootsuite/mastodon/pull/9517))
- Make avatar and headers respect GIF autoplay settings ([ThibG](https://github.com/tootsuite/mastodon/pull/9515))
- Do no retry Web Push workers if the server returns a 4xx response ([Gargron](https://github.com/tootsuite/mastodon/pull/9434))
- Minor scrollable list fixes ([ThibG](https://github.com/tootsuite/mastodon/pull/9551))
- Ignore low-confidence CharlockHolmes guesses when parsing link cards ([ThibG](https://github.com/tootsuite/mastodon/pull/9510))
- Fix `tootctl accounts rotate` not updating public keys ([Gargron](https://github.com/tootsuite/mastodon/pull/9556))
- Fix CSP / X-Frame-Options for media players ([jomo](https://github.com/tootsuite/mastodon/pull/9558))
- Fix unnecessary loadMore calls when the end of a timeline has been reached ([ThibG](https://github.com/tootsuite/mastodon/pull/9581))
- Skip mailer job retries when a record no longer exists ([Gargron](https://github.com/tootsuite/mastodon/pull/9590))
- Fix composer not getting focus after reply confirmation dialog ([ThibG](https://github.com/tootsuite/mastodon/pull/9602))
- Fix signature verification stoplight triggering on non-timeout errors ([Gargron](https://github.com/tootsuite/mastodon/pull/9617))
- Fix ThreadResolveWorker getting queued with invalid URLs ([Gargron](https://github.com/tootsuite/mastodon/pull/9628))
- Fix crash when clearing uninitialized timeline ([ThibG](https://github.com/tootsuite/mastodon/pull/9662))
- Avoid duplicate work by merging ReplyDistributionWorker into DistributionWorker ([ThibG](https://github.com/tootsuite/mastodon/pull/9660))
- Skip full text search if it fails, instead of erroring out completely ([Kjwon15](https://github.com/tootsuite/mastodon/pull/9654))
- Fix profile metadata links not verifying correctly sometimes ([shrft](https://github.com/tootsuite/mastodon/pull/9673))
- Ensure blocked user unfollows blocker if Block/Undo-Block activities are processed out of order ([ThibG](https://github.com/tootsuite/mastodon/pull/9687))
- Fix unreadable text color in report modal for some statuses ([Gargron](https://github.com/tootsuite/mastodon/pull/9716))
- Stop GIFV timeline preview explicitly when it's opened in modal ([kedamaDQ](https://github.com/tootsuite/mastodon/pull/9749))
- Fix scrollbar width compensation ([ThibG](https://github.com/tootsuite/mastodon/pull/9824))
- Fix race conditions when processing deleted toots ([ThibG](https://github.com/tootsuite/mastodon/pull/9815))
- Fix SSO issues on WebKit browsers by disabling Same-Site cookie again ([moritzheiber](https://github.com/tootsuite/mastodon/pull/9819))
- Fix empty OEmbed error ([renatolond](https://github.com/tootsuite/mastodon/pull/9807))
- Fix drag & drop modal not disappearing sometimes ([hinaloe](https://github.com/tootsuite/mastodon/pull/9797))
- Fix statuses with content warnings being displayed in web push notifications sometimes ([ThibG](https://github.com/tootsuite/mastodon/pull/9778))
- Fix scroll-to-detailed status not working on public pages ([ThibG](https://github.com/tootsuite/mastodon/pull/9773))
- Fix media modal loading indicator ([ThibG](https://github.com/tootsuite/mastodon/pull/9771))
- Fix hashtag search results not having a permalink fallback in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/9810))
- Fix slightly cropped font on settings page dropdowns when using system font ([ariasuni](https://github.com/tootsuite/mastodon/pull/9839))
- Fix not being able to drag & drop text into forms ([tmm576](https://github.com/tootsuite/mastodon/pull/9840))

### Security

- Sanitize and sandbox toot embeds in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/9552))
- Add tombstones for remote statuses to prevent replay attacks ([ThibG](https://github.com/tootsuite/mastodon/pull/9830))

## [2.6.5] - 2018-12-01
### Changed

- Change lists to display replies to others on the list and list owner ([ThibG](https://github.com/tootsuite/mastodon/pull/9324))

### Fixed

- Fix failures caused by commonly-used JSON-LD contexts being unavailable ([ThibG](https://github.com/tootsuite/mastodon/pull/9412))

## [2.6.4] - 2018-11-30
### Fixed

- Fix yarn dependencies not installing due to yanked event-stream package ([Gargron](https://github.com/tootsuite/mastodon/pull/9401))

## [2.6.3] - 2018-11-30
### Added

- Add hyphen to characters allowed in remote usernames ([ThibG](https://github.com/tootsuite/mastodon/pull/9345))

### Changed

- Change server user count to exclude suspended accounts ([Gargron](https://github.com/tootsuite/mastodon/pull/9380))

### Fixed

- Fix ffmpeg processing sometimes stalling due to overfilled stdout buffer ([hugogameiro](https://github.com/tootsuite/mastodon/pull/9368))
- Fix missing DNS records raising the wrong kind of exception ([Gargron](https://github.com/tootsuite/mastodon/pull/9379))
- Fix already queued deliveries still trying to reach inboxes marked as unavailable ([Gargron](https://github.com/tootsuite/mastodon/pull/9358))

### Security

- Fix TLS handshake timeout not being enforced ([Gargron](https://github.com/tootsuite/mastodon/pull/9381))

## [2.6.2] - 2018-11-23
### Added

- Add Page to whitelisted ActivityPub types ([mbajur](https://github.com/tootsuite/mastodon/pull/9188))
- Add 20px to column width in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/9227))
- Add amount of freed disk space in `tootctl media remove` ([Gargron](https://github.com/tootsuite/mastodon/pull/9229), [Gargron](https://github.com/tootsuite/mastodon/pull/9239), [mayaeh](https://github.com/tootsuite/mastodon/pull/9288))
- Add "Show thread" link to self-replies ([Gargron](https://github.com/tootsuite/mastodon/pull/9228))

### Changed

- Change order of Atom and RSS links so Atom is first ([Alkarex](https://github.com/tootsuite/mastodon/pull/9302))
- Change Nginx configuration for Nanobox apps ([danhunsaker](https://github.com/tootsuite/mastodon/pull/9310))
- Change the follow action to appear instant in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/9220))
- Change how the ActiveRecord connection is instantiated in on_worker_boot ([Gargron](https://github.com/tootsuite/mastodon/pull/9238))
- Change `tootctl accounts cull` to always touch accounts so they can be skipped ([renatolond](https://github.com/tootsuite/mastodon/pull/9293))
- Change mime type comparison to ignore JSON-LD profile ([valerauko](https://github.com/tootsuite/mastodon/pull/9179))

### Fixed

- Fix web UI crash when conversation has no last status ([sammy8806](https://github.com/tootsuite/mastodon/pull/9207))
- Fix follow limit validator reporting lower number past threshold ([Gargron](https://github.com/tootsuite/mastodon/pull/9230))
- Fix form validation flash message color and input borders ([Gargron](https://github.com/tootsuite/mastodon/pull/9235))
- Fix invalid twitter:player cards being displayed ([ThibG](https://github.com/tootsuite/mastodon/pull/9254))
- Fix emoji update date being processed incorrectly ([ThibG](https://github.com/tootsuite/mastodon/pull/9255))
- Fix playing embed resetting if status is reloaded in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/9270), [Gargron](https://github.com/tootsuite/mastodon/pull/9275))
- Fix web UI crash when favouriting a deleted status ([ThibG](https://github.com/tootsuite/mastodon/pull/9272))
- Fix intermediary arrays being created for hash maps ([Gargron](https://github.com/tootsuite/mastodon/pull/9291))
- Fix filter ID not being a string in REST API ([Gargron](https://github.com/tootsuite/mastodon/pull/9303))

### Security

- Fix multiple remote account deletions being able to deadlock the database ([Gargron](https://github.com/tootsuite/mastodon/pull/9292))
- Fix HTTP connection timeout of 10s not being enforced ([Gargron](https://github.com/tootsuite/mastodon/pull/9329))

## [2.6.1] - 2018-10-30
### Fixed

- Fix resolving resources by URL not working due to a regression in [valerauko](https://github.com/tootsuite/mastodon/pull/9132) ([Gargron](https://github.com/tootsuite/mastodon/pull/9171))
- Fix reducer error in web UI when a conversation has no last status ([Gargron](https://github.com/tootsuite/mastodon/pull/9173))

## [2.6.0] - 2018-10-30
### Added

- Add link ownership verification ([Gargron](https://github.com/tootsuite/mastodon/pull/8703))
- Add conversations API ([Gargron](https://github.com/tootsuite/mastodon/pull/8832))
- Add limit for the number of people that can be followed from one account ([Gargron](https://github.com/tootsuite/mastodon/pull/8807))
- Add admin setting to customize mascot ([ashleyhull-versent](https://github.com/tootsuite/mastodon/pull/8766))
- Add support for more granular ActivityPub audiences from other software, i.e. circles ([Gargron](https://github.com/tootsuite/mastodon/pull/8950), [Gargron](https://github.com/tootsuite/mastodon/pull/9093), [Gargron](https://github.com/tootsuite/mastodon/pull/9150))
- Add option to block all reports from a domain ([Gargron](https://github.com/tootsuite/mastodon/pull/8830))
- Add user preference to always expand toots marked with content warnings ([webroo](https://github.com/tootsuite/mastodon/pull/8762))
- Add user preference to always hide all media ([fvh-P](https://github.com/tootsuite/mastodon/pull/8569))
- Add `force_login` param to OAuth authorize page ([Gargron](https://github.com/tootsuite/mastodon/pull/8655))
- Add `tootctl accounts backup` ([Gargron](https://github.com/tootsuite/mastodon/pull/8642), [Gargron](https://github.com/tootsuite/mastodon/pull/8811))
- Add `tootctl accounts create` ([Gargron](https://github.com/tootsuite/mastodon/pull/8642), [Gargron](https://github.com/tootsuite/mastodon/pull/8811))
- Add `tootctl accounts cull` ([Gargron](https://github.com/tootsuite/mastodon/pull/8642), [Gargron](https://github.com/tootsuite/mastodon/pull/8811))
- Add `tootctl accounts delete` ([Gargron](https://github.com/tootsuite/mastodon/pull/8642), [Gargron](https://github.com/tootsuite/mastodon/pull/8811))
- Add `tootctl accounts modify` ([Gargron](https://github.com/tootsuite/mastodon/pull/8642), [Gargron](https://github.com/tootsuite/mastodon/pull/8811))
- Add `tootctl accounts refresh` ([Gargron](https://github.com/tootsuite/mastodon/pull/8642), [Gargron](https://github.com/tootsuite/mastodon/pull/8811))
- Add `tootctl feeds build` ([Gargron](https://github.com/tootsuite/mastodon/pull/8642), [Gargron](https://github.com/tootsuite/mastodon/pull/8811))
- Add `tootctl feeds clear` ([Gargron](https://github.com/tootsuite/mastodon/pull/8642), [Gargron](https://github.com/tootsuite/mastodon/pull/8811))
- Add `tootctl settings registrations open` ([Gargron](https://github.com/tootsuite/mastodon/pull/8642), [Gargron](https://github.com/tootsuite/mastodon/pull/8811))
- Add `tootctl settings registrations close` ([Gargron](https://github.com/tootsuite/mastodon/pull/8642), [Gargron](https://github.com/tootsuite/mastodon/pull/8811))
- Add `min_id` param to REST API to support backwards pagination ([Gargron](https://github.com/tootsuite/mastodon/pull/8736))
- Add a confirmation dialog when hitting reply and the compose box isn't empty ([ThibG](https://github.com/tootsuite/mastodon/pull/8893))
- Add PostgreSQL disk space growth tracking in PGHero ([Gargron](https://github.com/tootsuite/mastodon/pull/8906))
- Add button for disabling local account to report quick actions bar ([Gargron](https://github.com/tootsuite/mastodon/pull/9024))
- Add Czech language ([Aditoo17](https://github.com/tootsuite/mastodon/pull/8594))
- Add `same-site` (`lax`) attribute to cookies ([sorin-davidoi](https://github.com/tootsuite/mastodon/pull/8626))
- Add support for styled scrollbars in Firefox Nightly ([sorin-davidoi](https://github.com/tootsuite/mastodon/pull/8653))
- Add highlight to the active tab in web UI profiles ([rhoio](https://github.com/tootsuite/mastodon/pull/8673))
- Add auto-focus for comment textarea in report modal ([ThibG](https://github.com/tootsuite/mastodon/pull/8689))
- Add auto-focus for emoji picker's search field ([ThibG](https://github.com/tootsuite/mastodon/pull/8688))
- Add nginx and systemd templates to `dist/` directory ([Gargron](https://github.com/tootsuite/mastodon/pull/8770))
- Add support for `/.well-known/change-password` ([Gargron](https://github.com/tootsuite/mastodon/pull/8828))
- Add option to override FFMPEG binary path ([sascha-sl](https://github.com/tootsuite/mastodon/pull/8855))
- Add `dns-prefetch` tag when using different host for assets or uploads ([Gargron](https://github.com/tootsuite/mastodon/pull/8942))
- Add `description` meta tag ([Gargron](https://github.com/tootsuite/mastodon/pull/8941))
- Add `Content-Security-Policy` header ([ThibG](https://github.com/tootsuite/mastodon/pull/8957))
- Add cache for the instance info API ([ykzts](https://github.com/tootsuite/mastodon/pull/8765))
- Add suggested follows to search screen in mobile layout ([Gargron](https://github.com/tootsuite/mastodon/pull/9010))
- Add CORS header to `/.well-known/*` routes ([BenLubar](https://github.com/tootsuite/mastodon/pull/9083))
- Add `card` attribute to statuses returned from REST API ([Gargron](https://github.com/tootsuite/mastodon/pull/9120))
- Add in-stream link preview ([Gargron](https://github.com/tootsuite/mastodon/pull/9120))
- Add support for ActivityPub `Page` objects ([mbajur](https://github.com/tootsuite/mastodon/pull/9121))

### Changed

- Change forms design ([Gargron](https://github.com/tootsuite/mastodon/pull/8703))
- Change reports overview to group by target account ([Gargron](https://github.com/tootsuite/mastodon/pull/8674))
- Change web UI to show "read more" link on overly long in-stream statuses ([lanodan](https://github.com/tootsuite/mastodon/pull/8205))
- Change design of direct messages column ([Gargron](https://github.com/tootsuite/mastodon/pull/8832), [Gargron](https://github.com/tootsuite/mastodon/pull/9022))
- Change home timelines to exclude DMs ([Gargron](https://github.com/tootsuite/mastodon/pull/8940))
- Change list timelines to exclude all replies ([cbayerlein](https://github.com/tootsuite/mastodon/pull/8683))
- Change admin accounts UI default sort to most recent ([Gargron](https://github.com/tootsuite/mastodon/pull/8813))
- Change documentation URL in the UI ([Gargron](https://github.com/tootsuite/mastodon/pull/8898))
- Change style of success and failure messages ([Gargron](https://github.com/tootsuite/mastodon/pull/8973))
- Change DM filtering to always allow DMs from staff ([qguv](https://github.com/tootsuite/mastodon/pull/8993))
- Change recommended Ruby version to 2.5.3 ([zunda](https://github.com/tootsuite/mastodon/pull/9003))
- Change docker-compose default to persist volumes in current directory ([Gargron](https://github.com/tootsuite/mastodon/pull/9055))
- Change character counters on edit profile page to input length limit ([Gargron](https://github.com/tootsuite/mastodon/pull/9100))
- Change notification filtering to always let through messages from staff ([Gargron](https://github.com/tootsuite/mastodon/pull/9152))
- Change "hide boosts from user" function also hiding notifications about boosts ([ThibG](https://github.com/tootsuite/mastodon/pull/9147))
- Change CSS `detailed-status__wrapper` class actually wrap the detailed status ([trwnh](https://github.com/tootsuite/mastodon/pull/8547))

### Deprecated

- `GET /api/v1/timelines/direct` → `GET /api/v1/conversations` ([Gargron](https://github.com/tootsuite/mastodon/pull/8832))
- `POST /api/v1/notifications/dismiss` → `POST /api/v1/notifications/:id/dismiss` ([Gargron](https://github.com/tootsuite/mastodon/pull/8905))
- `GET /api/v1/statuses/:id/card` → `card` attributed included in status ([Gargron](https://github.com/tootsuite/mastodon/pull/9120))

### Removed

- Remove "on this device" label in column push settings ([rhoio](https://github.com/tootsuite/mastodon/pull/8704))
- Remove rake tasks in favour of tootctl commands ([Gargron](https://github.com/tootsuite/mastodon/pull/8675))

### Fixed

- Fix remote statuses using instance's default locale if no language given ([Kjwon15](https://github.com/tootsuite/mastodon/pull/8861))
- Fix streaming API not exiting when port or socket is unavailable ([Gargron](https://github.com/tootsuite/mastodon/pull/9023))
- Fix network calls being performed in database transaction in ActivityPub handler ([Gargron](https://github.com/tootsuite/mastodon/pull/8951))
- Fix dropdown arrow position ([ThibG](https://github.com/tootsuite/mastodon/pull/8637))
- Fix first element of dropdowns being focused even if not using keyboard ([ThibG](https://github.com/tootsuite/mastodon/pull/8679))
- Fix tootctl requiring `bundle exec` invocation ([abcang](https://github.com/tootsuite/mastodon/pull/8619))
- Fix public pages not using animation preference for avatars ([renatolond](https://github.com/tootsuite/mastodon/pull/8614))
- Fix OEmbed/OpenGraph cards not understanding relative URLs ([ThibG](https://github.com/tootsuite/mastodon/pull/8669))
- Fix some dark emojis not having a white outline ([ThibG](https://github.com/tootsuite/mastodon/pull/8597))
- Fix media description not being displayed in various media modals ([ThibG](https://github.com/tootsuite/mastodon/pull/8678))
- Fix generated URLs of desktop notifications missing base URL ([GenbuHase](https://github.com/tootsuite/mastodon/pull/8758))
- Fix RTL styles ([mabkenar](https://github.com/tootsuite/mastodon/pull/8764), [mabkenar](https://github.com/tootsuite/mastodon/pull/8767), [mabkenar](https://github.com/tootsuite/mastodon/pull/8823), [mabkenar](https://github.com/tootsuite/mastodon/pull/8897), [mabkenar](https://github.com/tootsuite/mastodon/pull/9005), [mabkenar](https://github.com/tootsuite/mastodon/pull/9007), [mabkenar](https://github.com/tootsuite/mastodon/pull/9018), [mabkenar](https://github.com/tootsuite/mastodon/pull/9021), [mabkenar](https://github.com/tootsuite/mastodon/pull/9145), [mabkenar](https://github.com/tootsuite/mastodon/pull/9146))
- Fix crash in streaming API when tag param missing ([Gargron](https://github.com/tootsuite/mastodon/pull/8955))
- Fix hotkeys not working when no element is focused ([ThibG](https://github.com/tootsuite/mastodon/pull/8998))
- Fix some hotkeys not working on detailed status view ([ThibG](https://github.com/tootsuite/mastodon/pull/9006))
- Fix og:url on status pages ([ThibG](https://github.com/tootsuite/mastodon/pull/9047))
- Fix upload option buttons only being visible on hover ([Gargron](https://github.com/tootsuite/mastodon/pull/9074))
- Fix tootctl not returning exit code 1 on wrong arguments ([sascha-sl](https://github.com/tootsuite/mastodon/pull/9094))
- Fix preview cards for appearing for profiles mentioned in toot ([ThibG](https://github.com/tootsuite/mastodon/pull/6934), [ThibG](https://github.com/tootsuite/mastodon/pull/9158))
- Fix local accounts sometimes being duplicated as faux-remote ([Gargron](https://github.com/tootsuite/mastodon/pull/9109))
- Fix emoji search when the shortcode has multiple separators ([ThibG](https://github.com/tootsuite/mastodon/pull/9124))
- Fix dropdowns sometimes being partially obscured by other elements ([kedamaDQ](https://github.com/tootsuite/mastodon/pull/9126))
- Fix cache not updating when reply/boost/favourite counters or media sensitivity update ([Gargron](https://github.com/tootsuite/mastodon/pull/9119))
- Fix empty display name precedence over username in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/9163))
- Fix td instead of th in sessions table header ([Gargron](https://github.com/tootsuite/mastodon/pull/9162))
- Fix handling of content types with profile ([valerauko](https://github.com/tootsuite/mastodon/pull/9132))

## [2.5.2] - 2018-10-12
### Security

- Fix XSS vulnerability ([Gargron](https://github.com/tootsuite/mastodon/pull/8959))

## [2.5.1] - 2018-10-07
### Fixed

- Fix database migrations for PostgreSQL below 9.5 ([Gargron](https://github.com/tootsuite/mastodon/pull/8903))
- Fix class autoloading issue in ActivityPub Create handler ([Gargron](https://github.com/tootsuite/mastodon/pull/8820))
- Fix cache statistics not being sent via statsd when statsd enabled ([ykzts](https://github.com/tootsuite/mastodon/pull/8831))
- Bump puma from 3.11.4 to 3.12.0 ([dependabot[bot]](https://github.com/tootsuite/mastodon/pull/8883))

### Security

- Fix some local images not having their EXIF metadata stripped on upload ([ThibG](https://github.com/tootsuite/mastodon/pull/8714))
- Fix being able to enable a disabled relay via ActivityPub Accept handler ([ThibG](https://github.com/tootsuite/mastodon/pull/8864))
- Bump nokogiri from 1.8.4 to 1.8.5 ([dependabot[bot]](https://github.com/tootsuite/mastodon/pull/8881))
- Fix being able to report statuses not belonging to the reported account ([ThibG](https://github.com/tootsuite/mastodon/pull/8916))

+ 21
- 40
CONTRIBUTING.md View File

@@ -1,56 +1,37 @@
CONTRIBUTING
Contributing
============ ============


There are three ways in which you can contribute to this repository:
Thank you for considering contributing to Mastodon 🐘


1. By improving the documentation
2. By working on the back-end application
3. By working on the front-end application
You can contribute in the following ways:


Choosing what to work on in a large open source project is not easy. The list of [GitHub issues](https://github.com/tootsuite/mastodon/issues) may provide some ideas, but not every feature request has been greenlit. Likewise, not every change or feature that resolves a personal itch will be merged into the main repository. Some communication ahead of time may be wise. If your addition creates a new feature or setting, or otherwise changes how things work in some substantial way, please remember to submit a correlating pull request to document your changes in the [documentation](http://github.com/tootsuite/documentation).
- Finding and reporting bugs
- Translating the Mastodon interface into various languages
- Contributing code to Mastodon by fixing bugs or implementing features
- Improving the documentation


Below are the guidelines for working on pull requests:
## Bug reports


## General
Bug reports and feature suggestions can be submitted to [GitHub Issues](https://github.com/tootsuite/mastodon/issues). Please make sure that you are not submitting duplicates, and that a similar report or request has not already been resolved or rejected in the past using the search function. Please also use descriptive, concise titles.


- 2 spaces indentation
## Translations


## Documentation

- No spelling mistakes
- No orthographic mistakes
- No Markdown syntax errors

## Requirements

- Ruby
- Node.js
- PostgreSQL
- Redis
- Nginx (optional)

## Back-end application
You can submit translations via [Weblate](https://weblate.joinmastodon.org/). They are periodically merged into the codebase.


It is expected that you have a working development environment set up. The development environment includes [rubocop](https://github.com/bbatsov/rubocop), which checks your Ruby code for compliance with our style guide and best practices. Sublime Text, likely like other editors, has a [Rubocop plugin](https://github.com/pderichs/sublime_rubocop) that runs checks on files as you edit them. The codebase also has a test suite.

* The codebase is not perfect, at the time of writing, but it is expected that you do not introduce new code style violations
* The rspec test suite must pass
* To the extent that it is possible, verify your changes. In the best case, by adding new tests to the test suite. At the very least, by running the server or console and checking it manually
* If you are introducing new strings to the user interface, they must be using localization methods
[![Mastodon translation statistics by language](https://weblate.joinmastodon.org/widgets/mastodon/-/multi-auto.svg)](https://weblate.joinmastodon.org/)


If your code has syntax errors that won't let it run, it's a good sign that the pull request isn't ready for submission yet.
## Pull requests


## Front-end application
Please use clean, concise titles for your pull requests. We use commit squashing, so the final commit in the master branch will carry the title of the pull request.


It is expected that you have a working development environment set up (see back-end application section). This project includes an ESLint configuration file, with which you can lint your changes.
The smaller the set of changes in the pull request is, the quicker it can be reviewed and merged. Splitting tasks into multiple smaller pull requests is often preferable.


* Avoid grave ESLint violations
* Verify that your changes work
* If you are introducing new strings, they must be using localization methods
**Pull requests that do not pass automated checks may not be reviewed**. In particular, you need to keep in mind:


If the JavaScript or CSS assets won't compile due to a syntax error, it's a good sign that the pull request isn't ready for submission yet.
- Unit and integration tests (rspec, jest)
- Code style rules (rubocop, eslint)
- Normalization of locale files (i18n-tasks)


## Translate
## Documentation


You can contribute to translating Mastodon via Weblate at [weblate.joinmastodon.org](https://weblate.joinmastodon.org/).
[![Mastodon translation statistics by language](https://weblate.joinmastodon.org/widgets/mastodon/-/multi-auto.svg)](https://weblate.joinmastodon.org/)
The [Mastodon documentation](https://docs.joinmastodon.org) is a statically generated site. You can [submit merge requests to mastodon/docs](https://source.joinmastodon.org/mastodon/docs).

+ 17
- 14
Dockerfile View File

@@ -1,4 +1,5 @@
FROM ruby:2.4.4-alpine3.6
FROM node:8.15-alpine as node
FROM ruby:2.6-alpine3.8


LABEL maintainer="https://github.com/tootsuite/mastodon" \ LABEL maintainer="https://github.com/tootsuite/mastodon" \
description="Your self-hosted, globally interconnected microblogging community" description="Your self-hosted, globally interconnected microblogging community"
@@ -11,8 +12,6 @@ ENV PATH=/mastodon/bin:$PATH \
RAILS_ENV=production \ RAILS_ENV=production \
NODE_ENV=production NODE_ENV=production


ARG YARN_VERSION=1.3.2
ARG YARN_DOWNLOAD_SHA256=6cfe82e530ef0837212f13e45c1565ba53f5199eec2527b85ecbcd88bf26821d
ARG LIBICONV_VERSION=1.15 ARG LIBICONV_VERSION=1.15
ARG LIBICONV_DOWNLOAD_SHA256=ccf536620a45458d26ba83887a983b96827001e92a13847b45e4925cc8913178 ARG LIBICONV_DOWNLOAD_SHA256=ccf536620a45458d26ba83887a983b96827001e92a13847b45e4925cc8913178


@@ -20,6 +19,11 @@ EXPOSE 3000 4000


WORKDIR /mastodon WORKDIR /mastodon


COPY --from=node /usr/local/bin/node /usr/local/bin/node
COPY --from=node /usr/local/lib/node_modules /usr/local/lib/node_modules
COPY --from=node /usr/local/bin/npm /usr/local/bin/npm
COPY --from=node /opt/yarn-* /opt/yarn

RUN apk -U upgrade \ RUN apk -U upgrade \
&& apk add -t build-dependencies \ && apk add -t build-dependencies \
build-base \ build-base \
@@ -27,6 +31,8 @@ RUN apk -U upgrade \
libidn-dev \ libidn-dev \
libressl \ libressl \
libtool \ libtool \
libxml2-dev \
libxslt-dev \
postgresql-dev \ postgresql-dev \
protobuf-dev \ protobuf-dev \
python \ python \
@@ -39,20 +45,15 @@ RUN apk -U upgrade \
imagemagick \ imagemagick \
libidn \ libidn \
libpq \ libpq \
nodejs \
nodejs-npm \
libxml2 \
libxslt \
protobuf \ protobuf \
tini \ tini \
tzdata \ tzdata \
&& update-ca-certificates \ && update-ca-certificates \
&& mkdir -p /tmp/src /opt \
&& wget -O yarn.tar.gz "https://github.com/yarnpkg/yarn/releases/download/v$YARN_VERSION/yarn-v$YARN_VERSION.tar.gz" \
&& echo "$YARN_DOWNLOAD_SHA256 *yarn.tar.gz" | sha256sum -c - \
&& tar -xzf yarn.tar.gz -C /tmp/src \
&& rm yarn.tar.gz \
&& mv /tmp/src/yarn-v$YARN_VERSION /opt/yarn \
&& ln -s /opt/yarn/bin/yarn /usr/local/bin/yarn \ && ln -s /opt/yarn/bin/yarn /usr/local/bin/yarn \
&& ln -s /opt/yarn/bin/yarnpkg /usr/local/bin/yarnpkg \ && ln -s /opt/yarn/bin/yarnpkg /usr/local/bin/yarnpkg \
&& mkdir -p /tmp/src /opt \
&& wget -O libiconv.tar.gz "https://ftp.gnu.org/pub/gnu/libiconv/libiconv-$LIBICONV_VERSION.tar.gz" \ && wget -O libiconv.tar.gz "https://ftp.gnu.org/pub/gnu/libiconv/libiconv-$LIBICONV_VERSION.tar.gz" \
&& echo "$LIBICONV_DOWNLOAD_SHA256 *libiconv.tar.gz" | sha256sum -c - \ && echo "$LIBICONV_DOWNLOAD_SHA256 *libiconv.tar.gz" | sha256sum -c - \
&& tar -xzf libiconv.tar.gz -C /tmp/src \ && tar -xzf libiconv.tar.gz -C /tmp/src \
@@ -67,9 +68,9 @@ RUN apk -U upgrade \


COPY Gemfile Gemfile.lock package.json yarn.lock .yarnclean /mastodon/ 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 \ && bundle install -j$(getconf _NPROCESSORS_ONLN) --deployment --without test development \
&& yarn --pure-lockfile \
&& yarn install --pure-lockfile --ignore-engines \
&& yarn cache clean && yarn cache clean


RUN addgroup -g ${GID} mastodon && adduser -h /mastodon -s /bin/sh -D -G mastodon -u ${UID} mastodon \ RUN addgroup -g ${GID} mastodon && adduser -h /mastodon -s /bin/sh -D -G mastodon -u ${UID} mastodon \
@@ -80,8 +81,10 @@ COPY . /mastodon


RUN chown -R mastodon:mastodon /mastodon RUN chown -R mastodon:mastodon /mastodon


VOLUME /mastodon/public/system /mastodon/public/assets /mastodon/public/packs
VOLUME /mastodon/public/system


USER mastodon USER mastodon


RUN OTP_SECRET=precompile_placeholder SECRET_KEY_BASE=precompile_placeholder bundle exec rails assets:precompile

ENTRYPOINT ["/sbin/tini", "--"] ENTRYPOINT ["/sbin/tini", "--"]

+ 54
- 51
Gemfile View File

@@ -1,138 +1,139 @@
# frozen_string_literal: true # frozen_string_literal: true


source 'https://rubygems.org' source 'https://rubygems.org'
ruby '>= 2.3.0', '< 2.6.0'
ruby '>= 2.4.0', '< 2.7.0'


gem 'pkg-config', '~> 1.3' gem 'pkg-config', '~> 1.3'


gem 'puma', '~> 3.11'
gem 'rails', '~> 5.2.0'
gem 'puma', '~> 3.12'
gem 'rails', '~> 5.2.2'
gem 'thor', '~> 0.20'


gem 'hamlit-rails', '~> 0.2' gem 'hamlit-rails', '~> 0.2'
gem 'pg', '~> 1.0'
gem 'pghero', '~> 2.1'
gem 'dotenv-rails', '~> 2.2', '< 2.3'
gem 'aws-sdk-s3', '~> 1.9', require: false
gem 'fog-core', '~> 1.45'
gem 'fog-local', '~> 0.5', require: false
gem 'fog-openstack', '~> 0.1', require: false
gem 'pg', '~> 1.1'
gem 'makara', '~> 0.4'
gem 'pghero', '~> 2.2'
gem 'dotenv-rails', '~> 2.6'
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' gem 'paperclip', '~> 6.0'
gem 'paperclip-av-transcoder', '~> 0.6' gem 'paperclip-av-transcoder', '~> 0.6'
gem 'streamio-ffmpeg', '~> 3.0' gem 'streamio-ffmpeg', '~> 3.0'


gem 'active_model_serializers', '~> 0.10' gem 'active_model_serializers', '~> 0.10'
gem 'addressable', '~> 2.5' gem 'addressable', '~> 2.5'
gem 'bootsnap', '~> 1.3'
gem 'bootsnap', '~> 1.3', require: false
gem 'browser' gem 'browser'
gem 'charlock_holmes', '~> 0.7.6' gem 'charlock_holmes', '~> 0.7.6'
gem 'iso-639' gem 'iso-639'
gem 'chewy', '~> 5.0' gem 'chewy', '~> 5.0'
gem 'cld3', '~> 3.2.0'
gem 'devise', '~> 4.4'
gem 'cld3', '~> 3.2.3'
gem 'devise', '~> 4.5'
gem 'devise-two-factor', '~> 3.0' gem 'devise-two-factor', '~> 3.0'


group :pam_authentication, optional: true do group :pam_authentication, optional: true do
gem 'devise_pam_authenticatable2', '~> 9.1'
gem 'devise_pam_authenticatable2', '~> 9.2'
end end


gem 'net-ldap', '~> 0.10' gem 'net-ldap', '~> 0.10'
gem 'omniauth-cas', '~> 1.1' gem 'omniauth-cas', '~> 1.1'
gem 'omniauth-saml', '~> 1.10' gem 'omniauth-saml', '~> 1.10'
gem 'omniauth', '~> 1.2'
gem 'omniauth', '~> 1.9'


gem 'doorkeeper', '~> 4.4'
gem 'doorkeeper', '~> 5.0'
gem 'fast_blank', '~> 1.0' gem 'fast_blank', '~> 1.0'
gem 'fastimage' gem 'fastimage'
gem 'goldfinger', '~> 2.1' gem 'goldfinger', '~> 2.1'
gem 'hiredis', '~> 0.6' gem 'hiredis', '~> 0.6'
gem 'redis-namespace', '~> 1.5' gem 'redis-namespace', '~> 1.5'
gem 'htmlentities', '~> 4.3' gem 'htmlentities', '~> 4.3'
gem 'http', '~> 3.2'
gem 'http', '~> 3.3'
gem 'http_accept_language', '~> 2.1' gem 'http_accept_language', '~> 2.1'
gem 'http_parser.rb', '~> 0.6', git: 'https://github.com/tmm1/http_parser.rb', ref: '54b17ba8c7d8d20a16dfc65d1775241833219cf2' gem 'http_parser.rb', '~> 0.6', git: 'https://github.com/tmm1/http_parser.rb', ref: '54b17ba8c7d8d20a16dfc65d1775241833219cf2'
gem 'httplog', '~> 1.0'
gem 'httplog', '~> 1.2'
gem 'idn-ruby', require: 'idn' gem 'idn-ruby', require: 'idn'
gem 'kaminari', '~> 1.1' gem 'kaminari', '~> 1.1'
gem 'link_header', '~> 0.0' gem 'link_header', '~> 0.0'
gem 'mime-types', '~> 3.1', require: 'mime/types/columnar'
gem 'nokogiri', '~> 1.8'
gem 'mime-types', '~> 3.2', require: 'mime/types/columnar'
gem 'nokogiri', '~> 1.10'
gem 'nsa', '~> 0.2' gem 'nsa', '~> 0.2'
gem 'oj', '~> 3.5'
gem 'oj', '~> 3.7'
gem 'ostatus2', '~> 2.0' gem 'ostatus2', '~> 2.0'
gem 'ox', '~> 2.9'
gem 'ox', '~> 2.10'
gem 'posix-spawn', git: 'https://github.com/rtomayko/posix-spawn', ref: '58465d2e213991f8afb13b984854a49fcdcc980c' gem 'posix-spawn', git: 'https://github.com/rtomayko/posix-spawn', ref: '58465d2e213991f8afb13b984854a49fcdcc980c'
gem 'pundit', '~> 1.1'
gem 'pundit', '~> 2.0'
gem 'premailer-rails' gem 'premailer-rails'
gem 'rack-attack', '~> 5.2'
gem 'rack-attack', '~> 5.4'
gem 'rack-cors', '~> 1.0', require: 'rack/cors' gem 'rack-cors', '~> 1.0', require: 'rack/cors'
gem 'rails-i18n', '~> 5.1' gem 'rails-i18n', '~> 5.1'
gem 'rails-settings-cached', '~> 0.6' 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 'mario-redis-lock', '~> 1.2', require: 'redis_lock'
gem 'rqrcode', '~> 0.10' gem 'rqrcode', '~> 0.10'
gem 'ruby-progressbar', '~> 1.4'
gem 'sanitize', '~> 4.6'
gem 'sidekiq', '~> 5.1'
gem 'sidekiq-scheduler', '~> 2.2'
gem 'sidekiq-unique-jobs', '~> 5.0'
gem 'sidekiq-bulk', '~>0.1.1'
gem 'sanitize', '~> 5.0'
gem 'sidekiq', '~> 5.2'
gem 'sidekiq-scheduler', '~> 3.0'
gem 'sidekiq-unique-jobs', '~> 6.0'
gem 'sidekiq-bulk', '~>0.2.0'
gem 'simple-navigation', '~> 4.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 'sprockets-rails', '~> 3.2', require: 'sprockets/railtie'
gem 'stoplight', '~> 2.1.3' gem 'stoplight', '~> 2.1.3'
gem 'strong_migrations', '~> 0.2'
gem 'strong_migrations', '~> 0.3'
gem 'tty-command', '~> 0.8', require: false gem 'tty-command', '~> 0.8', require: false
gem 'tty-prompt', '~> 0.16', require: false
gem 'tty-prompt', '~> 0.18', require: false
gem 'twitter-text', '~> 1.14' gem 'twitter-text', '~> 1.14'
gem 'tzinfo-data', '~> 1.2018' gem 'tzinfo-data', '~> 1.2018'
gem 'webpacker', '~> 3.4'
gem 'webpacker', '~> 3.5'
gem 'webpush' gem 'webpush'


gem 'json-ld', '~> 2.2'
gem 'json-ld', '~> 3.0'
gem 'json-ld-preloaded', '~> 3.0'
gem 'rdf-normalize', '~> 0.3' gem 'rdf-normalize', '~> 0.3'


group :development, :test do group :development, :test do
gem 'fabrication', '~> 2.20' gem 'fabrication', '~> 2.20'
gem 'fuubar', '~> 2.2'
gem 'fuubar', '~> 2.3'
gem 'i18n-tasks', '~> 0.9', require: false gem 'i18n-tasks', '~> 0.9', require: false
gem 'pry-byebug', '~> 3.6' gem 'pry-byebug', '~> 3.6'
gem 'pry-rails', '~> 0.3' gem 'pry-rails', '~> 0.3'
gem 'rspec-rails', '~> 3.7'
gem 'rspec-rails', '~> 3.8'
end end


group :production, :test do group :production, :test do
gem 'private_address_check', '~> 0.4.1'
gem 'private_address_check', '~> 0.5'
end end


group :test do group :test do
gem 'capybara', '~> 2.18'
gem 'capybara', '~> 3.12'
gem 'climate_control', '~> 0.2' gem 'climate_control', '~> 0.2'
gem 'faker', '~> 1.8'
gem 'faker', '~> 1.9'
gem 'microformats', '~> 4.0' gem 'microformats', '~> 4.0'
gem 'rails-controller-testing', '~> 1.0' gem 'rails-controller-testing', '~> 1.0'
gem 'rspec-sidekiq', '~> 3.0' gem 'rspec-sidekiq', '~> 3.0'
gem 'simplecov', '~> 0.16', require: false gem 'simplecov', '~> 0.16', require: false
gem 'webmock', '~> 3.3'
gem 'parallel_tests', '~> 2.21'
gem 'webmock', '~> 3.5'
gem 'parallel_tests', '~> 2.27'
end end


group :development do group :development do
gem 'active_record_query_trace', '~> 1.5' gem 'active_record_query_trace', '~> 1.5'
gem 'annotate', '~> 2.7' gem 'annotate', '~> 2.7'
gem 'better_errors', '~> 2.4'
gem 'better_errors', '~> 2.5'
gem 'binding_of_caller', '~> 0.7' 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 'letter_opener_web', '~> 1.3'
gem 'memory_profiler' gem 'memory_profiler'
gem 'rubocop', '~> 0.55', require: false
gem 'brakeman', '~> 4.2', require: false
gem 'rubocop', '~> 0.63', require: false
gem 'brakeman', '~> 4.4', require: false
gem 'bundler-audit', '~> 0.6', require: false gem 'bundler-audit', '~> 0.6', require: false
gem 'scss_lint', '~> 0.57', require: false gem 'scss_lint', '~> 0.57', require: false


gem 'capistrano', '~> 3.10'
gem 'capistrano-rails', '~> 1.3'
gem 'capistrano', '~> 3.11'
gem 'capistrano-rails', '~> 1.4'
gem 'capistrano-rbenv', '~> 2.1' gem 'capistrano-rbenv', '~> 2.1'
gem 'capistrano-yarn', '~> 2.0' gem 'capistrano-yarn', '~> 2.0'


@@ -144,3 +145,5 @@ group :production do
gem 'lograge', '~> 0.10' gem 'lograge', '~> 0.10'
gem 'redis-rails', '~> 5.0' gem 'redis-rails', '~> 5.0'
end end

gem 'concurrent-ruby', require: false

+ 272
- 262
Gemfile.lock View File

@@ -15,49 +15,49 @@ GIT
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
actioncable (5.2.0)
actionpack (= 5.2.0)
actioncable (5.2.2)
actionpack (= 5.2.2)
nio4r (~> 2.0) nio4r (~> 2.0)
websocket-driver (>= 0.6.1) websocket-driver (>= 0.6.1)
actionmailer (5.2.0)
actionpack (= 5.2.0)
actionview (= 5.2.0)
activejob (= 5.2.0)
actionmailer (5.2.2)
actionpack (= 5.2.2)
actionview (= 5.2.2)
activejob (= 5.2.2)
mail (~> 2.5, >= 2.5.4) mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
actionpack (5.2.0)
actionview (= 5.2.0)
activesupport (= 5.2.0)
actionpack (5.2.2)
actionview (= 5.2.2)
activesupport (= 5.2.2)
rack (~> 2.0) rack (~> 2.0)
rack-test (>= 0.6.3) rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.2) rails-html-sanitizer (~> 1.0, >= 1.0.2)
actionview (5.2.0)
activesupport (= 5.2.0)
actionview (5.2.2)
activesupport (= 5.2.2)
builder (~> 3.1) builder (~> 3.1)
erubi (~> 1.4) erubi (~> 1.4)
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.3) rails-html-sanitizer (~> 1.0, >= 1.0.3)
active_model_serializers (0.10.7)
active_model_serializers (0.10.8)
actionpack (>= 4.1, < 6) actionpack (>= 4.1, < 6)
activemodel (>= 4.1, < 6) activemodel (>= 4.1, < 6)
case_transform (>= 0.2) case_transform (>= 0.2)
jsonapi-renderer (>= 0.1.1.beta1, < 0.3) jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
active_record_query_trace (1.5.4) active_record_query_trace (1.5.4)
activejob (5.2.0)
activesupport (= 5.2.0)
activejob (5.2.2)
activesupport (= 5.2.2)
globalid (>= 0.3.6) globalid (>= 0.3.6)
activemodel (5.2.0)
activesupport (= 5.2.0)
activerecord (5.2.0)
activemodel (= 5.2.0)
activesupport (= 5.2.0)
activemodel (5.2.2)
activesupport (= 5.2.2)
activerecord (5.2.2)
activemodel (= 5.2.2)
activesupport (= 5.2.2)
arel (>= 9.0) arel (>= 9.0)
activestorage (5.2.0)
actionpack (= 5.2.0)
activerecord (= 5.2.0)
activestorage (5.2.2)
actionpack (= 5.2.2)
activerecord (= 5.2.2)
marcel (~> 0.3.1) marcel (~> 0.3.1)
activesupport (5.2.0)
activesupport (5.2.2)
concurrent-ruby (~> 1.0, >= 1.0.2) concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2) i18n (>= 0.7, < 2)
minitest (~> 5.1) minitest (~> 5.1)
@@ -66,7 +66,7 @@ GEM
public_suffix (>= 2.0.2, < 4.0) public_suffix (>= 2.0.2, < 4.0)
airbrussh (1.3.0) airbrussh (1.3.0)
sshkit (>= 1.6.1, != 1.7.0) sshkit (>= 1.6.1, != 1.7.0)
annotate (2.7.3)
annotate (2.7.4)
activerecord (>= 3.2, < 6.0) activerecord (>= 3.2, < 6.0)
rake (>= 10.4, < 13.0) rake (>= 10.4, < 13.0)
arel (9.0.0) arel (9.0.0)
@@ -75,40 +75,42 @@ GEM
encryptor (~> 3.0.0) encryptor (~> 3.0.0)
av (0.9.0) av (0.9.0)
cocaine (~> 0.5.3) cocaine (~> 0.5.3)
aws-partitions (1.80.0)
aws-sdk-core (3.19.0)
aws-eventstream (1.0.1)
aws-partitions (1.131.0)
aws-sdk-core (3.45.0)
aws-eventstream (~> 1.0)
aws-partitions (~> 1.0) aws-partitions (~> 1.0)
aws-sigv4 (~> 1.0) aws-sigv4 (~> 1.0)
jmespath (~> 1.0) jmespath (~> 1.0)
aws-sdk-kms (1.5.0)
aws-sdk-core (~> 3)
aws-sdk-kms (1.13.0)
aws-sdk-core (~> 3, >= 3.39.0)
aws-sigv4 (~> 1.0) aws-sigv4 (~> 1.0)
aws-sdk-s3 (1.9.1)
aws-sdk-core (~> 3)
aws-sdk-s3 (1.30.1)
aws-sdk-core (~> 3, >= 3.39.0)
aws-sdk-kms (~> 1) aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.0) aws-sigv4 (~> 1.0)
aws-sigv4 (1.0.2)
aws-sigv4 (1.0.3)
bcrypt (3.1.12) bcrypt (3.1.12)
benchmark-ips (2.7.2) benchmark-ips (2.7.2)
better_errors (2.4.0)
better_errors (2.5.0)
coderay (>= 1.0.0) coderay (>= 1.0.0)
erubi (>= 1.0.0) erubi (>= 1.0.0)
rack (>= 0.9.0) rack (>= 0.9.0)
binding_of_caller (0.8.0) binding_of_caller (0.8.0)
debug_inspector (>= 0.0.1) debug_inspector (>= 0.0.1)
bootsnap (1.3.0)
bootsnap (1.3.2)
msgpack (~> 1.0) msgpack (~> 1.0)
brakeman (4.2.1)
brakeman (4.4.0)
browser (2.5.3) browser (2.5.3)
builder (3.2.3) builder (3.2.3)
bullet (5.7.5)
bullet (5.9.0)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
uniform_notifier (~> 1.11.0)
bundler-audit (0.6.0)
bundler (~> 1.2)
uniform_notifier (~> 1.11)
bundler-audit (0.6.1)
bundler (>= 1.2.0, < 3)
thor (~> 0.18) thor (~> 0.18)
byebug (10.0.2) byebug (10.0.2)
capistrano (3.10.2)
capistrano (3.11.0)
airbrussh (>= 1.0.0) airbrussh (>= 1.0.0)
i18n i18n
rake (>= 10.0.0) rake (>= 10.0.0)
@@ -116,21 +118,22 @@ GEM
capistrano-bundler (1.3.0) capistrano-bundler (1.3.0)
capistrano (~> 3.1) capistrano (~> 3.1)
sshkit (~> 1.2) sshkit (~> 1.2)
capistrano-rails (1.3.1)
capistrano-rails (1.4.0)
capistrano (~> 3.1) capistrano (~> 3.1)
capistrano-bundler (~> 1.1) capistrano-bundler (~> 1.1)
capistrano-rbenv (2.1.3)
capistrano-rbenv (2.1.4)
capistrano (~> 3.1) capistrano (~> 3.1)
sshkit (~> 1.3) sshkit (~> 1.3)
capistrano-yarn (2.0.2) capistrano-yarn (2.0.2)
capistrano (~> 3.0) capistrano (~> 3.0)
capybara (2.18.0)
capybara (3.12.0)
addressable addressable
mini_mime (>= 0.1.3) mini_mime (>= 0.1.3)
nokogiri (>= 1.3.3)
rack (>= 1.0.0)
rack-test (>= 0.5.4)
xpath (>= 2.0, < 4.0)
nokogiri (~> 1.8)
rack (>= 1.6.0)
rack-test (>= 0.6.3)
regexp_parser (~> 1.2)
xpath (~> 3.2)
case_transform (0.2) case_transform (0.2)
activesupport activesupport
charlock_holmes (0.7.6) charlock_holmes (0.7.6)
@@ -139,22 +142,21 @@ GEM
elasticsearch (>= 2.0.0) elasticsearch (>= 2.0.0)
elasticsearch-dsl elasticsearch-dsl
chunky_png (1.3.10) chunky_png (1.3.10)
cld3 (3.2.2)
cld3 (3.2.3)
ffi (>= 1.1.0, < 1.10.0) ffi (>= 1.1.0, < 1.10.0)
climate_control (0.2.0) climate_control (0.2.0)
cocaine (0.5.8) cocaine (0.5.8)
climate_control (>= 0.0.3, < 1.0) climate_control (>= 0.0.3, < 1.0)
coderay (1.1.2) coderay (1.1.2)
colorize (0.8.1)
concurrent-ruby (1.0.5)
connection_pool (2.2.1)
concurrent-ruby (1.1.4)
connection_pool (2.2.2)
crack (0.4.3) crack (0.4.3)
safe_yaml (~> 1.0.0) safe_yaml (~> 1.0.0)
crass (1.0.4) crass (1.0.4)
css_parser (1.6.0) css_parser (1.6.0)
addressable addressable
debug_inspector (0.0.3) debug_inspector (0.0.3)
derailed_benchmarks (1.3.4)
derailed_benchmarks (1.3.5)
benchmark-ips (~> 2) benchmark-ips (~> 2)
get_process_mem (~> 0) get_process_mem (~> 0)
heapy (~> 0) heapy (~> 0)
@@ -162,7 +164,7 @@ GEM
rack (>= 1) rack (>= 1)
rake (> 10, < 13) rake (> 10, < 13)
thor (~> 0.19) thor (~> 0.19)
devise (4.4.3)
devise (4.5.0)
bcrypt (~> 3.0) bcrypt (~> 3.0)
orm_adapter (~> 0.1) orm_adapter (~> 0.1)
railties (>= 4.1.0, < 6.0) railties (>= 4.1.0, < 6.0)
@@ -174,22 +176,19 @@ GEM
devise (~> 4.0) devise (~> 4.0)
railties (< 5.3) railties (< 5.3)
rotp (~> 2.0) rotp (~> 2.0)
devise_pam_authenticatable2 (9.1.0)
devise_pam_authenticatable2 (9.2.0)
devise (>= 4.0.0) devise (>= 4.0.0)
rpam2 (~> 4.0) rpam2 (~> 4.0)
diff-lcs (1.3) diff-lcs (1.3)
docile (1.3.0) docile (1.3.0)
domain_name (0.5.20180417) domain_name (0.5.20180417)
unf (>= 0.0.5, < 1.0.0) unf (>= 0.0.5, < 1.0.0)
doorkeeper (4.4.2)
doorkeeper (5.0.2)
railties (>= 4.2) railties (>= 4.2)
dotenv (2.2.2)
dotenv-rails (2.2.2)
dotenv (= 2.2.2)
dotenv (2.6.0)
dotenv-rails (2.6.0)
dotenv (= 2.6.0)
railties (>= 3.2, < 6.0) railties (>= 3.2, < 6.0)
easy_translate (0.5.1)
thread
thread_safe
elasticsearch (6.0.2) elasticsearch (6.0.2)
elasticsearch-api (= 6.0.2) elasticsearch-api (= 6.0.2)
elasticsearch-transport (= 6.0.2) elasticsearch-transport (= 6.0.2)
@@ -201,36 +200,38 @@ GEM
multi_json multi_json
encryptor (3.0.0) encryptor (3.0.0)
equatable (0.5.0) equatable (0.5.0)
erubi (1.7.1)
et-orbi (1.1.0)
erubi (1.8.0)
et-orbi (1.1.6)
tzinfo tzinfo
excon (0.62.0) excon (0.62.0)
fabrication (2.20.1) fabrication (2.20.1)
faker (1.8.7)
faker (1.9.1)
i18n (>= 0.7) i18n (>= 0.7)
faraday (0.15.0) faraday (0.15.0)
multipart-post (>= 1.2, < 3) multipart-post (>= 1.2, < 3)
fast_blank (1.0.0) fast_blank (1.0.0)
fastimage (2.1.1)
ffi (1.9.23)
fog-core (1.45.0)
fastimage (2.1.5)
ffi (1.9.25)
fog-core (2.1.0)
builder builder
excon (~> 0.58) excon (~> 0.58)
formatador (~> 0.2) formatador (~> 0.2)
fog-json (1.0.2)
fog-core (~> 1.0)
mime-types
fog-json (1.2.0)
fog-core
multi_json (~> 1.10) multi_json (~> 1.10)
fog-local (0.5.0)
fog-core (>= 1.27, < 3.0)
fog-openstack (0.1.25)
fog-core (~> 1.40)
fog-openstack (0.3.7)
fog-core (>= 1.45, <= 2.1.0)
fog-json (>= 1.0) fog-json (>= 1.0)
ipaddress (>= 0.8) ipaddress (>= 0.8)
formatador (0.2.5) formatador (0.2.5)
fuubar (2.3.1)
fugit (1.1.6)
et-orbi (~> 1.1, >= 1.1.6)
raabro (~> 1.1)
fuubar (2.3.2)
rspec-core (~> 3.0) rspec-core (~> 3.0)
ruby-progressbar (~> 1.4) ruby-progressbar (~> 1.4)
get_process_mem (0.2.1)
get_process_mem (0.2.3)
globalid (0.4.1) globalid (0.4.1)
activesupport (>= 4.2.0) activesupport (>= 4.2.0)
goldfinger (2.1.0) goldfinger (2.1.0)
@@ -250,45 +251,49 @@ GEM
hamster (3.0.0) hamster (3.0.0)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
hashdiff (0.3.7) hashdiff (0.3.7)
hashie (3.5.7)
heapy (0.1.3)
highline (1.7.10)
hiredis (0.6.1)
hitimes (1.2.6)
hashie (3.6.0)
heapy (0.1.4)
highline (2.0.0)
hiredis (0.6.3)
hkdf (0.3.0) hkdf (0.3.0)
htmlentities (4.3.4) htmlentities (4.3.4)
http (3.2.0)
http (3.3.0)
addressable (~> 2.3) addressable (~> 2.3)
http-cookie (~> 1.0) http-cookie (~> 1.0)
http-form_data (~> 2.0) http-form_data (~> 2.0)
http_parser.rb (~> 0.6.0) http_parser.rb (~> 0.6.0)
http-cookie (1.0.3) http-cookie (1.0.3)
domain_name (~> 0.5) domain_name (~> 0.5)
http-form_data (2.1.0)
http-form_data (2.1.1)
http_accept_language (2.1.1) http_accept_language (2.1.1)
httplog (1.0.2)
colorize (~> 0.8)
httplog (1.2.0)
rack (>= 1.0) rack (>= 1.0)
i18n (1.1.0)
rainbow (>= 2.0.0)
i18n (1.5.2)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
i18n-tasks (0.9.21)
i18n-tasks (0.9.28)
activesupport (>= 4.0.2) activesupport (>= 4.0.2)
ast (>= 2.1.0) ast (>= 2.1.0)
easy_translate (>= 0.5.1)
erubi erubi
highline (>= 1.7.3)
highline (>= 2.0.0)
i18n i18n
parser (>= 2.2.3.0) parser (>= 2.2.3.0)
rails-i18n
rainbow (>= 2.2.2, < 4.0) rainbow (>= 2.2.2, < 4.0)
terminal-table (>= 1.5.1) terminal-table (>= 1.5.1)
idn-ruby (0.1.0) idn-ruby (0.1.0)
ipaddress (0.8.3) ipaddress (0.8.3)
iso-639 (0.2.8) iso-639 (0.2.8)
jaro_winkler (1.5.2)
jmespath (1.4.0) jmespath (1.4.0)
json (2.1.0) json (2.1.0)
json-ld (2.2.1)
json-ld (3.0.2)
multi_json (~> 1.12) multi_json (~> 1.12)
rdf (>= 2.2.8, < 4.0) rdf (>= 2.2.8, < 4.0)
json-ld-preloaded (3.0.0)
json-ld (>= 2.2, < 4.0)
multi_json (~> 1.12)
rdf (~> 3.0)
jsonapi-renderer (0.2.0) jsonapi-renderer (0.2.0)
jwt (2.1.0) jwt (2.1.0)
kaminari (1.1.1) kaminari (1.1.1)
@@ -305,7 +310,7 @@ GEM
kaminari-core (1.1.1) kaminari-core (1.1.1)
launchy (2.4.3) launchy (2.4.3)
addressable (~> 2.3) addressable (~> 2.3)
letter_opener (1.6.0)
letter_opener (1.7.0)
launchy (~> 2.2) launchy (~> 2.2)
letter_opener_web (1.3.4) letter_opener_web (1.3.4)
actionmailer (>= 3.2) actionmailer (>= 3.2)
@@ -317,26 +322,28 @@ GEM
activesupport (>= 4) activesupport (>= 4)
railties (>= 4) railties (>= 4)
request_store (~> 1.0) request_store (~> 1.0)
loofah (2.2.2)
loofah (2.2.3)
crass (~> 1.0.2) crass (~> 1.0.2)
nokogiri (>= 1.5.9) nokogiri (>= 1.5.9)
mail (2.7.0)
mail (2.7.1)
mini_mime (>= 0.1.1) mini_mime (>= 0.1.1)
marcel (0.3.2)
makara (0.4.0)
activerecord (>= 3.0.0)
marcel (0.3.3)
mimemagic (~> 0.3.2) mimemagic (~> 0.3.2)
mario-redis-lock (1.2.1) mario-redis-lock (1.2.1)
redis (>= 3.0.5) redis (>= 3.0.5)
memory_profiler (0.9.10)
method_source (0.9.0)
memory_profiler (0.9.12)
method_source (0.9.2)
microformats (4.0.7) microformats (4.0.7)
json json
nokogiri nokogiri
mime-types (3.1)
mime-types (3.2.2)
mime-types-data (~> 3.2015) mime-types-data (~> 3.2015)
mime-types-data (3.2016.0521)
mime-types-data (3.2018.0812)
mimemagic (0.3.2) mimemagic (0.3.2)
mini_mime (1.0.0)
mini_portile2 (2.3.0)
mini_mime (1.0.1)
mini_portile2 (2.4.0)
minitest (5.11.3) minitest (5.11.3)
msgpack (1.2.4) msgpack (1.2.4)
multi_json (1.13.1) multi_json (1.13.1)
@@ -345,26 +352,26 @@ GEM
net-ldap (0.16.1) net-ldap (0.16.1)
net-scp (1.2.1) net-scp (1.2.1)
net-ssh (>= 2.6.5) net-ssh (>= 2.6.5)
net-ssh (4.2.0)
nio4r (2.3.0)
nokogiri (1.8.4)
mini_portile2 (~> 2.3.0)
nokogumbo (1.5.0)
nokogiri
nsa (0.2.4)
net-ssh (5.0.2)
nio4r (2.3.1)
nokogiri (1.10.1)
mini_portile2 (~> 2.4.0)
nokogumbo (2.0.0)
nokogiri (~> 1.8, >= 1.8.4)
nsa (0.2.7)
activesupport (>= 4.2, < 6) activesupport (>= 4.2, < 6)
concurrent-ruby (~> 1.0.0)
sidekiq (>= 3.5.0)
statsd-ruby (~> 1.2.0)
oj (3.5.1)
omniauth (1.8.1)
hashie (>= 3.4.6, < 3.6.0)
concurrent-ruby (~> 1.0, >= 1.0.2)
sidekiq (>= 3.5)
statsd-ruby (~> 1.4, >= 1.4.0)
oj (3.7.7)
omniauth (1.9.0)
hashie (>= 3.4.6, < 3.7.0)
rack (>= 1.6.2, < 3) rack (>= 1.6.2, < 3)
omniauth-cas (1.1.1) omniauth-cas (1.1.1)
addressable (~> 2.3) addressable (~> 2.3)
nokogiri (~> 1.5) nokogiri (~> 1.5)
omniauth (~> 1.2) omniauth (~> 1.2)
omniauth-saml (1.10.0)
omniauth-saml (1.10.1)
omniauth (~> 1.3, >= 1.3.2) omniauth (~> 1.3, >= 1.3.2)
ruby-saml (~> 1.7) ruby-saml (~> 1.7)
orm_adapter (0.5.0) orm_adapter (0.5.0)
@@ -372,7 +379,7 @@ GEM
addressable (~> 2.5) addressable (~> 2.5)
http (~> 3.0) http (~> 3.0)
nokogiri (~> 1.8) nokogiri (~> 1.8)
ox (2.9.2)
ox (2.10.0)
paperclip (6.0.0) paperclip (6.0.0)
activemodel (>= 4.2.0) activemodel (>= 4.2.0)
activesupport (>= 4.2.0) activesupport (>= 4.2.0)
@@ -383,18 +390,18 @@ GEM
av (~> 0.9.0) av (~> 0.9.0)
paperclip (>= 2.5.2) paperclip (>= 2.5.2)
parallel (1.12.1) parallel (1.12.1)
parallel_tests (2.21.3)
parallel_tests (2.27.1)
parallel parallel
parser (2.5.1.0)
parser (2.6.0.0)
ast (~> 2.4.0) ast (~> 2.4.0)
pastel (0.7.2) pastel (0.7.2)
equatable (~> 0.5.0) equatable (~> 0.5.0)
tty-color (~> 0.4.0) tty-color (~> 0.4.0)
pg (1.0.0)
pghero (2.1.0)
pg (1.1.4)
pghero (2.2.0)
activerecord activerecord
pkg-config (1.3.0)
powerpack (0.1.1)
pkg-config (1.3.2)
powerpack (0.1.2)
premailer (1.11.1) premailer (1.11.1)
addressable addressable
css_parser (>= 1.6.0) css_parser (>= 1.6.0)
@@ -402,73 +409,74 @@ GEM
premailer-rails (1.10.2) premailer-rails (1.10.2)
actionmailer (>= 3, < 6) actionmailer (>= 3, < 6)
premailer (~> 1.7, >= 1.7.9) premailer (~> 1.7, >= 1.7.9)
private_address_check (0.4.1)
pry (0.11.3)
private_address_check (0.5.0)
pry (0.12.2)
coderay (~> 1.1.0) coderay (~> 1.1.0)
method_source (~> 0.9.0) method_source (~> 0.9.0)
pry-byebug (3.6.0) pry-byebug (3.6.0)
byebug (~> 10.0) byebug (~> 10.0)
pry (~> 0.10) pry (~> 0.10)
pry-rails (0.3.6)
pry-rails (0.3.9)
pry (>= 0.10.4) pry (>= 0.10.4)
public_suffix (3.0.2)
puma (3.11.4)
pundit (1.1.0)
public_suffix (3.0.3)
puma (3.12.0)
pundit (2.0.0)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
rack (2.0.5)
rack-attack (5.2.0)
rack
raabro (1.1.6)
rack (2.0.6)
rack-attack (5.4.2)
rack (>= 1.0, < 3)
rack-cors (1.0.2) rack-cors (1.0.2)
rack-protection (2.0.1)
rack-protection (2.0.5)
rack rack
rack-proxy (0.6.4) rack-proxy (0.6.4)
rack rack
rack-test (1.1.0) rack-test (1.1.0)
rack (>= 1.0, < 3) rack (>= 1.0, < 3)
rails (5.2.0)
actioncable (= 5.2.0)
actionmailer (= 5.2.0)
actionpack (= 5.2.0)
actionview (= 5.2.0)
activejob (= 5.2.0)
activemodel (= 5.2.0)
activerecord (= 5.2.0)
activestorage (= 5.2.0)
activesupport (= 5.2.0)
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) bundler (>= 1.3.0)
railties (= 5.2.0)
railties (= 5.2.2)
sprockets-rails (>= 2.0.0) 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) rails-dom-testing (2.0.3)
activesupport (>= 4.2.0) activesupport (>= 4.2.0)
nokogiri (>= 1.6) nokogiri (>= 1.6)
rails-html-sanitizer (1.0.4) rails-html-sanitizer (1.0.4)
loofah (~> 2.2, >= 2.2.2) loofah (~> 2.2, >= 2.2.2)
rails-i18n (5.1.1)
rails-i18n (5.1.2)
i18n (>= 0.7, < 2) i18n (>= 0.7, < 2)
railties (>= 5.0, < 6) railties (>= 5.0, < 6)
rails-settings-cached (0.6.6) rails-settings-cached (0.6.6)
rails (>= 4.2.0) rails (>= 4.2.0)
railties (5.2.0)
actionpack (= 5.2.0)
activesupport (= 5.2.0)
railties (5.2.2)
actionpack (= 5.2.2)
activesupport (= 5.2.2)
method_source method_source
rake (>= 0.8.7) rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
thor (>= 0.19.0, < 2.0)
rainbow (3.0.0) rainbow (3.0.0)
rake (12.3.1)
rake (12.3.2)
rb-fsevent (0.10.3) rb-fsevent (0.10.3)
rb-inotify (0.9.10) rb-inotify (0.9.10)
ffi (>= 0.5.0, < 2) ffi (>= 0.5.0, < 2)
rdf (3.0.2)
rdf (3.0.9)
hamster (~> 3.0) hamster (~> 3.0)
link_header (~> 0.0, >= 0.0.8) link_header (~> 0.0, >= 0.0.8)
rdf-normalize (0.3.3) rdf-normalize (0.3.3)
rdf (>= 2.2, < 4.0) rdf (>= 2.2, < 4.0)
redis (4.0.1)
redis (4.1.0)
redis-actionpack (5.0.2) redis-actionpack (5.0.2)
actionpack (>= 4.0, < 6) actionpack (>= 4.0, < 6)
redis-rack (>= 1, < 3) redis-rack (>= 1, < 3)
@@ -487,6 +495,7 @@ GEM
redis-store (>= 1.2, < 2) redis-store (>= 1.2, < 2)
redis-store (1.5.0) redis-store (1.5.0)
redis (>= 2.2, < 5) redis (>= 2.2, < 5)
regexp_parser (1.3.0)
request_store (1.4.1) request_store (1.4.1)
rack (>= 1.4) rack (>= 1.4)
responders (2.4.0) responders (2.4.0)
@@ -496,72 +505,73 @@ GEM
rpam2 (4.0.2) rpam2 (4.0.2)
rqrcode (0.10.1) rqrcode (0.10.1)
chunky_png (~> 1.0) chunky_png (~> 1.0)
rspec-core (3.7.1)
rspec-support (~> 3.7.0)
rspec-expectations (3.7.0)
rspec-core (3.8.0)
rspec-support (~> 3.8.0)
rspec-expectations (3.8.2)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.7.0)
rspec-mocks (3.7.0)
rspec-support (~> 3.8.0)
rspec-mocks (3.8.0)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.7.0)
rspec-rails (3.7.2)
rspec-support (~> 3.8.0)
rspec-rails (3.8.1)
actionpack (>= 3.0) actionpack (>= 3.0)
activesupport (>= 3.0) activesupport (>= 3.0)
railties (>= 3.0) railties (>= 3.0)
rspec-core (~> 3.7.0)
rspec-expectations (~> 3.7.0)
rspec-mocks (~> 3.7.0)
rspec-support (~> 3.7.0)
rspec-core (~> 3.8.0)
rspec-expectations (~> 3.8.0)
rspec-mocks (~> 3.8.0)
rspec-support (~> 3.8.0)
rspec-sidekiq (3.0.3) rspec-sidekiq (3.0.3)
rspec-core (~> 3.0, >= 3.0.0) rspec-core (~> 3.0, >= 3.0.0)
sidekiq (>= 2.4.0) sidekiq (>= 2.4.0)
rspec-support (3.7.1)
rubocop (0.55.0)
rspec-support (3.8.0)
rubocop (0.63.0)
jaro_winkler (~> 1.5.1)
parallel (~> 1.10) parallel (~> 1.10)
parser (>= 2.5)
parser (>= 2.5, != 2.5.1.1)
powerpack (~> 0.1) powerpack (~> 0.1)
rainbow (>= 2.2.2, < 4.0) rainbow (>= 2.2.2, < 4.0)
ruby-progressbar (~> 1.7) ruby-progressbar (~> 1.7)
unicode-display_width (~> 1.0, >= 1.0.1)
ruby-progressbar (1.9.0)
ruby-saml (1.7.2)
unicode-display_width (~> 1.4.0)
ruby-progressbar (1.10.0)
ruby-saml (1.9.0)
nokogiri (>= 1.5.10) nokogiri (>= 1.5.10)
rufus-scheduler (3.4.2)
et-orbi (~> 1.0)
rufus-scheduler (3.5.2)
fugit (~> 1.1, >= 1.1.5)
safe_yaml (1.0.4) safe_yaml (1.0.4)
sanitize (4.6.4)
sanitize (5.0.0)
crass (~> 1.0.2) crass (~> 1.0.2)
nokogiri (>= 1.4.4)
nokogumbo (~> 1.4)
sass (3.5.6)
nokogiri (>= 1.8.0)
nokogumbo (~> 2.0)
sass (3.6.0)
sass-listen (~> 4.0.0) sass-listen (~> 4.0.0)
sass-listen (4.0.0) sass-listen (4.0.0)
rb-fsevent (~> 0.9, >= 0.9.4) rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7) rb-inotify (~> 0.9, >= 0.9.7)
scss_lint (0.57.0)
scss_lint (0.57.1)
rake (>= 0.9, < 13) rake (>= 0.9, < 13)
sass (~> 3.5.5)
sidekiq (5.1.3)
concurrent-ruby (~> 1.0)
connection_pool (~> 2.2, >= 2.2.0)
sass (~> 3.5, >= 3.5.5)
sidekiq (5.2.5)
connection_pool (~> 2.2, >= 2.2.2)
rack (>= 1.5.0)
rack-protection (>= 1.5.0) rack-protection (>= 1.5.0)
redis (>= 3.3.5, < 5) redis (>= 3.3.5, < 5)
sidekiq-bulk (0.1.1)
activesupport
sidekiq-bulk (0.2.0)
sidekiq sidekiq
sidekiq-scheduler (2.2.1)
sidekiq-scheduler (3.0.0)
redis (>= 3, < 5) redis (>= 3, < 5)
rufus-scheduler (~> 3.2) rufus-scheduler (~> 3.2)
sidekiq (>= 3) sidekiq (>= 3)
tilt (>= 1.4.0) tilt (>= 1.4.0)
sidekiq-unique-jobs (5.0.10)
sidekiq (>= 4.0, <= 6.0)
sidekiq-unique-jobs (6.0.8)
concurrent-ruby (~> 1.0, >= 1.0.5)
sidekiq (>= 4.0, < 6.0)
thor (~> 0) thor (~> 0)
simple-navigation (4.0.5) simple-navigation (4.0.5)
activesupport (>= 2.3.2) activesupport (>= 2.3.2)
simple_form (4.0.0)
actionpack (> 4)
activemodel (> 4)
simple_form (4.1.0)
actionpack (>= 5.0)
activemodel (>= 5.0)
simplecov (0.16.1) simplecov (0.16.1)
docile (~> 1.1) docile (~> 1.1)
json (>= 1.8, < 3) json (>= 1.8, < 3)
@@ -574,71 +584,69 @@ GEM
actionpack (>= 4.0) actionpack (>= 4.0)
activesupport (>= 4.0) activesupport (>= 4.0)
sprockets (>= 3.0.0) sprockets (>= 3.0.0)
sshkit (1.16.0)
sshkit (1.17.0)
net-scp (>= 1.1.2) net-scp (>= 1.1.2)
net-ssh (>= 2.8.0) net-ssh (>= 2.8.0)
stackprof (0.2.11)
statsd-ruby (1.2.1)
stackprof (0.2.12)
statsd-ruby (1.4.0)
stoplight (2.1.3) stoplight (2.1.3)
streamio-ffmpeg (3.0.2) streamio-ffmpeg (3.0.2)
multi_json (~> 1.8) multi_json (~> 1.8)
strong_migrations (0.2.2)
strong_migrations (0.3.1)
activerecord (>= 3.2.0) activerecord (>= 3.2.0)
temple (0.8.0) temple (0.8.0)
terminal-table (1.8.0) terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1) unicode-display_width (~> 1.1, >= 1.1.1)
terrapin (0.6.0) terrapin (0.6.0)
climate_control (>= 0.0.3, < 1.0) climate_control (>= 0.0.3, < 1.0)
thor (0.20.0)
thread (0.2.2)
thor (0.20.3)
thread_safe (0.3.6) thread_safe (0.3.6)
tilt (2.0.8) tilt (2.0.8)
timers (4.1.2)
hitimes
tty-color (0.4.2)
tty-command (0.8.0)
timers (4.2.0)
tty-color (0.4.3)
tty-command (0.8.2)
pastel (~> 0.7.0) pastel (~> 0.7.0)
tty-cursor (0.5.0)
tty-prompt (0.16.0)
tty-cursor (0.6.0)
tty-prompt (0.18.1)
necromancer (~> 0.4.0) necromancer (~> 0.4.0)
pastel (~> 0.7.0) pastel (~> 0.7.0)
timers (~> 4.0) timers (~> 4.0)
tty-cursor (~> 0.5.0)
tty-reader (~> 0.2.0)
tty-reader (0.2.0)
tty-cursor (~> 0.5.0)
tty-cursor (~> 0.6.0)
tty-reader (~> 0.5.0)
tty-reader (0.5.0)
tty-cursor (~> 0.6.0)
tty-screen (~> 0.6.4) tty-screen (~> 0.6.4)
wisper (~> 2.0.0) wisper (~> 2.0.0)
tty-screen (0.6.4)
tty-screen (0.6.5)
twitter-text (1.14.7) twitter-text (1.14.7)
unf (~> 0.1.0) unf (~> 0.1.0)
tzinfo (1.2.5) tzinfo (1.2.5)
thread_safe (~> 0.1) thread_safe (~> 0.1)
tzinfo-data (1.2018.4)
tzinfo-data (1.2018.9)
tzinfo (>= 1.0.0) tzinfo (>= 1.0.0)
unf (0.1.4) unf (0.1.4)
unf_ext unf_ext
unf_ext (0.0.7.5) unf_ext (0.0.7.5)
unicode-display_width (1.3.2)
uniform_notifier (1.11.0)
unicode-display_width (1.4.1)
uniform_notifier (1.12.1)
warden (1.2.7) warden (1.2.7)
rack (>= 1.0) rack (>= 1.0)
webmock (3.3.0)
webmock (3.5.1)
addressable (>= 2.3.6) addressable (>= 2.3.6)
crack (>= 0.3.2) crack (>= 0.3.2)
hashdiff hashdiff
webpacker (3.4.3)
webpacker (3.5.5)
activesupport (>= 4.2) activesupport (>= 4.2)
rack-proxy (>= 0.6.1) rack-proxy (>= 0.6.1)
railties (>= 4.2) railties (>= 4.2)
webpush (0.3.3)
webpush (0.3.6)
hkdf (~> 0.2) hkdf (~> 0.2)
jwt (~> 2.0) jwt (~> 2.0)
websocket-driver (0.7.0) websocket-driver (0.7.0)
websocket-extensions (>= 0.1.0) websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.3) websocket-extensions (0.1.3)
wisper (2.0.0) wisper (2.0.0)
xpath (3.0.0)
xpath (3.2.0)
nokogiri (~> 1.8) nokogiri (~> 1.8)


PLATFORMS PLATFORMS
@@ -649,119 +657,121 @@ DEPENDENCIES
active_record_query_trace (~> 1.5) active_record_query_trace (~> 1.5)
addressable (~> 2.5) addressable (~> 2.5)
annotate (~> 2.7) annotate (~> 2.7)
aws-sdk-s3 (~> 1.9)
better_errors (~> 2.4)
aws-sdk-s3 (~> 1.30)
better_errors (~> 2.5)
binding_of_caller (~> 0.7) binding_of_caller (~> 0.7)
bootsnap (~> 1.3) bootsnap (~> 1.3)
brakeman (~> 4.2)
brakeman (~> 4.4)
browser browser
bullet (~> 5.7)
bullet (~> 5.9)
bundler-audit (~> 0.6) bundler-audit (~> 0.6)
capistrano (~> 3.10)
capistrano-rails (~> 1.3)
capistrano (~> 3.11)
capistrano-rails (~> 1.4)
capistrano-rbenv (~> 2.1) capistrano-rbenv (~> 2.1)
capistrano-yarn (~> 2.0) capistrano-yarn (~> 2.0)
capybara (~> 2.18)
capybara (~> 3.12)
charlock_holmes (~> 0.7.6) charlock_holmes (~> 0.7.6)
chewy (~> 5.0) chewy (~> 5.0)
cld3 (~> 3.2.0)
cld3 (~> 3.2.3)
climate_control (~> 0.2) climate_control (~> 0.2)
concurrent-ruby
derailed_benchmarks derailed_benchmarks
devise (~> 4.4)
devise (~> 4.5)
devise-two-factor (~> 3.0) devise-two-factor (~> 3.0)
devise_pam_authenticatable2 (~> 9.1)
doorkeeper (~> 4.4)
dotenv-rails (~> 2.2, < 2.3)
devise_pam_authenticatable2 (~> 9.2)
doorkeeper (~> 5.0)
dotenv-rails (~> 2.6)
fabrication (~> 2.20) fabrication (~> 2.20)
faker (~> 1.8)
faker (~> 1.9)
fast_blank (~> 1.0) fast_blank (~> 1.0)
fastimage fastimage
fog-core (~> 1.45)
fog-local (~> 0.5)
fog-openstack (~> 0.1)
fuubar (~> 2.2)
fog-core (<= 2.1.0)
fog-openstack (~> 0.3)
fuubar (~> 2.3)
goldfinger (~> 2.1) goldfinger (~> 2.1)
hamlit-rails (~> 0.2) hamlit-rails (~> 0.2)
hiredis (~> 0.6) hiredis (~> 0.6)
htmlentities (~> 4.3) htmlentities (~> 4.3)
http (~> 3.2)
http (~> 3.3)
http_accept_language (~> 2.1) http_accept_language (~> 2.1)
http_parser.rb (~> 0.6)! http_parser.rb (~> 0.6)!
httplog (~> 1.0)
httplog (~> 1.2)
i18n-tasks (~> 0.9) i18n-tasks (~> 0.9)
idn-ruby idn-ruby
iso-639 iso-639
json-ld (~> 2.2)
json-ld (~> 3.0)
json-ld-preloaded (~> 3.0)
kaminari (~> 1.1) kaminari (~> 1.1)
letter_opener (~> 1.4)
letter_opener (~> 1.7)
letter_opener_web (~> 1.3) letter_opener_web (~> 1.3)
link_header (~> 0.0) link_header (~> 0.0)
lograge (~> 0.10) lograge (~> 0.10)
makara (~> 0.4)
mario-redis-lock (~> 1.2) mario-redis-lock (~> 1.2)
memory_profiler memory_profiler
microformats (~> 4.0) microformats (~> 4.0)
mime-types (~> 3.1)
mime-types (~> 3.2)
net-ldap (~> 0.10) net-ldap (~> 0.10)
nokogiri (~> 1.8)
nokogiri (~> 1.10)
nsa (~> 0.2) nsa (~> 0.2)
oj (~> 3.5)
omniauth (~> 1.2)
oj (~> 3.7)
omniauth (~> 1.9)
omniauth-cas (~> 1.1) omniauth-cas (~> 1.1)
omniauth-saml (~> 1.10) omniauth-saml (~> 1.10)
ostatus2 (~> 2.0) ostatus2 (~> 2.0)
ox (~> 2.9)
ox (~> 2.10)
paperclip (~> 6.0) paperclip (~> 6.0)
paperclip-av-transcoder (~> 0.6) paperclip-av-transcoder (~> 0.6)
parallel_tests (~> 2.21)
pg (~> 1.0)
pghero (~> 2.1)
parallel_tests (~> 2.27)
pg (~> 1.1)
pghero (~> 2.2)
pkg-config (~> 1.3) pkg-config (~> 1.3)
posix-spawn! posix-spawn!
premailer-rails premailer-rails
private_address_check (~> 0.4.1)
private_address_check (~> 0.5)
pry-byebug (~> 3.6) pry-byebug (~> 3.6)
pry-rails (~> 0.3) pry-rails (~> 0.3)
puma (~> 3.11)
pundit (~> 1.1)
rack-attack (~> 5.2)
puma (~> 3.12)
pundit (~> 2.0)
rack-attack (~> 5.4)
rack-cors (~> 1.0) rack-cors (~> 1.0)
rails (~> 5.2.0)
rails (~> 5.2.2)
rails-controller-testing (~> 1.0) rails-controller-testing (~> 1.0)
rails-i18n (~> 5.1) rails-i18n (~> 5.1)
rails-settings-cached (~> 0.6) rails-settings-cached (~> 0.6)
rdf-normalize (~> 0.3) rdf-normalize (~> 0.3)
redis (~> 4.0)
redis (~> 4.1)
redis-namespace (~> 1.5) redis-namespace (~> 1.5)
redis-rails (~> 5.0) redis-rails (~> 5.0)
rqrcode (~> 0.10) rqrcode (~> 0.10)
rspec-rails (~> 3.7)
rspec-rails (~> 3.8)
rspec-sidekiq (~> 3.0) rspec-sidekiq (~> 3.0)
rubocop (~> 0.55)
ruby-progressbar (~> 1.4)
sanitize (~> 4.6)
rubocop (~> 0.63)
sanitize (~> 5.0)
scss_lint (~> 0.57) scss_lint (~> 0.57)
sidekiq (~> 5.1)
sidekiq-bulk (~> 0.1.1)
sidekiq-scheduler (~> 2.2)
sidekiq-unique-jobs (~> 5.0)
sidekiq (~> 5.2)
sidekiq-bulk (~> 0.2.0)
sidekiq-scheduler (~> 3.0)
sidekiq-unique-jobs (~> 6.0)
simple-navigation (~> 4.0) simple-navigation (~> 4.0)
simple_form (~> 4.0)
simple_form (~> 4.1)
simplecov (~> 0.16) simplecov (~> 0.16)
sprockets-rails (~> 3.2) sprockets-rails (~> 3.2)
stackprof stackprof
stoplight (~> 2.1.3) stoplight (~> 2.1.3)
streamio-ffmpeg (~> 3.0) streamio-ffmpeg (~> 3.0)
strong_migrations (~> 0.2)
strong_migrations (~> 0.3)
thor (~> 0.20)
tty-command (~> 0.8) tty-command (~> 0.8)
tty-prompt (~> 0.16)
tty-prompt (~> 0.18)
twitter-text (~> 1.14) twitter-text (~> 1.14)
tzinfo-data (~> 1.2018) tzinfo-data (~> 1.2018)
webmock (~> 3.3)
webpacker (~> 3.4)
webmock (~> 3.5)
webpacker (~> 3.5)
webpush webpush


RUBY VERSION RUBY VERSION
ruby 2.5.0p0
ruby 2.6.0p0


BUNDLED WITH BUNDLED WITH
1.16.3
1.17.3

+ 42
- 43
README.md View File

@@ -1,96 +1,95 @@
![Mastodon](https://i.imgur.com/NhZc40l.png) ![Mastodon](https://i.imgur.com/NhZc40l.png)
======== ========


[![GitHub release](https://img.shields.io/github/release/tootsuite/mastodon.svg)][releases]
[![Build Status](https://img.shields.io/circleci/project/github/tootsuite/mastodon.svg)][circleci] [![Build Status](https://img.shields.io/circleci/project/github/tootsuite/mastodon.svg)][circleci]
[![Code Climate](https://img.shields.io/codeclimate/maintainability/tootsuite/mastodon.svg)][code_climate] [![Code Climate](https://img.shields.io/codeclimate/maintainability/tootsuite/mastodon.svg)][code_climate]
[![Translation status](https://weblate.joinmastodon.org/widgets/mastodon/-/svg-badge.svg)][weblate]
[![Docker Pulls](https://img.shields.io/docker/pulls/tootsuite/mastodon.svg)][docker]


[releases]: https://github.com/tootsuite/mastodon/releases
[circleci]: https://circleci.com/gh/tootsuite/mastodon [circleci]: https://circleci.com/gh/tootsuite/mastodon
[code_climate]: https://codeclimate.com/github/tootsuite/mastodon [code_climate]: https://codeclimate.com/github/tootsuite/mastodon
[weblate]: https://weblate.joinmastodon.org/engage/mastodon/
[docker]: https://hub.docker.com/r/tootsuite/mastodon/


Mastodon is a **free, open-source social network server** based on **open web protocols** like ActivityPub and OStatus. The social focus of the project is a viable decentralized alternative to commercial social media silos that returns the control of the content distribution channels to the people. The technical focus of the project is a good user interface, a clean REST API for 3rd party apps and robust anti-abuse tools.
Mastodon is a **free, open-source social network server** based on ActivityPub. Follow friends and discover new ones. Publish anything you want: links, pictures, text, video. All servers of Mastodon are interoperable as a federated network, i.e. users on one server can seamlessly communicate with users from another one. This includes non-Mastodon software that also implements ActivityPub!


Click on the screenshot below to watch a demo of the UI:
Click below to **learn more** in a video:


[![Screenshot](https://i.imgur.com/qrNOiSp.png)][youtube_demo]
[![Screenshot](https://blog.joinmastodon.org/2018/06/why-activitypub-is-the-future/ezgif-2-60f1b00403.gif)][youtube_demo]


[youtube_demo]: https://www.youtube.com/watch?v=IPSbNdBmWKE [youtube_demo]: https://www.youtube.com/watch?v=IPSbNdBmWKE


**Ruby on Rails** is used for the back-end, while **React.js** and Redux are used for the dynamic front-end. A static front-end for public resources (profiles and statuses) is also provided.
## Navigation


If you would like, you can [support the development of this project on Patreon][patreon] or [Liberapay][liberapay].
- [Project homepage 🐘](https://joinmastodon.org)
- [Support the development via Patreon][patreon]
- [View sponsors](https://joinmastodon.org/sponsors)
- [Blog](https://blog.joinmastodon.org)
- [Documentation](https://docs.joinmastodon.org)
- [Browse Mastodon servers](https://joinmastodon.org/#getting-started)
- [Browse Mastodon apps](https://joinmastodon.org/apps)


[patreon]: https://www.patreon.com/user?u=619786
[liberapay]: https://liberapay.com/Mastodon/

---

## Resources

- [Frequently Asked Questions](https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/FAQ.md)
- [Use this tool to find Twitter friends on Mastodon](https://bridge.joinmastodon.org)
- [API overview](https://github.com/tootsuite/documentation/blob/master/Using-the-API/API.md)
- [List of Mastodon instances](https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/List-of-Mastodon-instances.md)
- [List of apps](https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/Apps.md)
- [List of sponsors](https://joinmastodon.org/sponsors)
[patreon]: https://www.patreon.com/mastodon


## Features ## Features


<img src="https://docs.joinmastodon.org/elephant.svg" align="right" width="30%" />

**No vendor lock-in: Fully interoperable with any conforming platform** **No vendor lock-in: Fully interoperable with any conforming platform**


It doesn't have to be Mastodon, whatever implements ActivityPub or OStatus is part of the social network!
It doesn't have to be Mastodon, whatever implements ActivityPub is part of the social network! [Learn more](https://blog.joinmastodon.org/2018/06/why-activitypub-is-the-future/)


**Real-time timeline updates**
**Real-time, chronological timeline updates**


See the updates of people you're following appear in real-time in the UI via WebSockets. There's a firehose view as well! See the updates of people you're following appear in real-time in the UI via WebSockets. There's a firehose view as well!


**Federated thread resolving**

If someone you follow replies to a user unknown to the server, the server fetches the full thread so you can view it without leaving the UI

**Media attachments like images and short videos** **Media attachments like images and short videos**


Upload and view images and WebM/MP4 videos attached to the updates. Videos with no audio track are treated like GIFs; normal videos are looped - like vines! Upload and view images and WebM/MP4 videos attached to the updates. Videos with no audio track are treated like GIFs; normal videos are looped - like vines!


**OAuth2 and a straightforward REST API**
**Safety and moderation tools**


Mastodon acts as an OAuth2 provider so 3rd party apps can use the API
Private posts, locked accounts, phrase filtering, muting, blocking and all sorts of other features, along with a reporting and moderation system. [Learn more](https://blog.joinmastodon.org/2018/07/cage-the-mastodon/)


**Fast response times**
**OAuth2 and a straightforward REST API**


Mastodon tries to be as fast and responsive as possible, so all long-running tasks are delegated to background processing
Mastodon acts as an OAuth2 provider so 3rd party apps can use the REST and Streaming APIs, resulting in a rich app ecosystem with a lot of choice!


**Deployable via Docker**
## Deployment


You don't need to mess with dependencies and configuration if you want to try Mastodon, if you have Docker and Docker Compose the deployment is extremely easy
**Tech stack:**


---
- **Ruby on Rails** powers the REST API and other web pages
- **React.js** and Redux are used for the dynamic parts of the interface
- **Node.js** powers the streaming API


## Development
**Requirements:**


Please follow the [development guide](https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/Development-guide.md) from the documentation repository.
- **PostgreSQL** 9.5+
- **Redis**
- **Ruby** 2.4+
- **Node.js** 8+


## Deployment
The repository includes deployment configurations for **Docker and docker-compose**, but also a few specific platforms like **Heroku**, **Scalingo**, and **Nanobox**. The [**stand-alone** installation guide](https://docs.joinmastodon.org/administration/installation/) is available in the documentation.


There are guides in the documentation repository for [deploying on various platforms](https://github.com/tootsuite/documentation#running-mastodon).
A **Vagrant** configuration is included for development purposes.


## Contributing ## Contributing


You can open issues for bugs you've found or features you think are missing. You can also submit pull requests to this repository. [Here are the guidelines for code contributions](CONTRIBUTING.md)
Mastodon is **free, open source software** licensed under **AGPLv3**.

You can open issues for bugs you've found or features you think are missing. You can also submit pull requests to this repository, or submit translations using Weblate. To get started, take a look at [CONTRIBUTING.md](CONTRIBUTING.md)


**IRC channel**: #mastodon on irc.freenode.net **IRC channel**: #mastodon on irc.freenode.net


## License ## License


Copyright (C) 2016-2018 Eugen Rochko & other Mastodon contributors (see AUTHORS.md)
Copyright (C) 2016-2018 Eugen Rochko & other Mastodon contributors (see [AUTHORS.md](AUTHORS.md))


This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.


This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.


You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.

---

## Extra credits

The elephant friend illustrations are created by [Dopatwo](https://mastodon.social/@dopatwo)

+ 12
- 6
Vagrantfile View File

@@ -12,7 +12,7 @@ curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
sudo apt-add-repository 'deb https://dl.yarnpkg.com/debian/ stable main' sudo apt-add-repository 'deb https://dl.yarnpkg.com/debian/ stable main'


# Add repo for NodeJS # Add repo for NodeJS
curl -sL https://deb.nodesource.com/setup_6.x | sudo bash -
curl -sL https://deb.nodesource.com/setup_8.x | sudo bash -


# Add firewall rule to redirect 80 to PORT and save # Add firewall rule to redirect 80 to PORT and save
sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port #{ENV["PORT"]} sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port #{ENV["PORT"]}
@@ -44,7 +44,7 @@ sudo apt-get install \


# Install rvm # Install rvm
read RUBY_VERSION < .ruby-version 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 curl -sSL https://raw.githubusercontent.com/rvm/rvm/stable/binscripts/rvm-installer | bash -s stable --ruby=$RUBY_VERSION
source /home/vagrant/.rvm/scripts/rvm source /home/vagrant/.rvm/scripts/rvm


@@ -85,6 +85,9 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.provider :virtualbox do |vb| config.vm.provider :virtualbox do |vb|
vb.name = "mastodon" vb.name = "mastodon"
vb.customize ["modifyvm", :id, "--memory", "2048"] vb.customize ["modifyvm", :id, "--memory", "2048"]
# Increase the number of CPUs. Uncomment and adjust to
# increase performance
# vb.customize ["modifyvm", :id, "--cpus", "3"]


# Disable VirtualBox DNS proxy to skip long-delay IPv6 resolutions. # Disable VirtualBox DNS proxy to skip long-delay IPv6 resolutions.
# https://github.com/mitchellh/vagrant/issues/1172 # https://github.com/mitchellh/vagrant/issues/1172
@@ -97,19 +100,22 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|


end end


config.vm.hostname = "mastodon.dev"

# This uses the vagrant-hostsupdater plugin, and lets you # This uses the vagrant-hostsupdater plugin, and lets you
# access the development site at http://mastodon.dev.
# access the development site at http://mastodon.local.
# If you change it, also change it in .env.vagrant before provisioning
# the vagrant server to update the development build.
#
# To install: # To install:
# $ vagrant plugin install vagrant-hostsupdater # $ vagrant plugin install vagrant-hostsupdater
config.vm.hostname = "mastodon.local"

if defined?(VagrantPlugins::HostsUpdater) if defined?(VagrantPlugins::HostsUpdater)
config.vm.network :private_network, ip: "192.168.42.42", nictype: "virtio" config.vm.network :private_network, ip: "192.168.42.42", nictype: "virtio"
config.hostsupdater.remove_on_suspend = false config.hostsupdater.remove_on_suspend = false
end end


if config.vm.networks.any? { |type, options| type == :private_network } if config.vm.networks.any? { |type, options| type == :private_network }
config.vm.synced_folder ".", "/vagrant", type: "nfs", mount_options: ['rw', 'vers=3', 'tcp']
config.vm.synced_folder ".", "/vagrant", type: "nfs", mount_options: ['rw', 'vers=3', 'tcp', 'actimeo=1']
else else
config.vm.synced_folder ".", "/vagrant" config.vm.synced_folder ".", "/vagrant"
end end


+ 1
- 1
app/chewy/statuses_index.rb View File

@@ -31,7 +31,7 @@ class StatusesIndex < Chewy::Index
}, },
} }


define_type ::Status.without_reblogs do
define_type ::Status.unscoped.without_reblogs do
crutch :mentions do |collection| crutch :mentions do |collection|
data = ::Mention.where(status_id: collection.map(&:id)).pluck(:status_id, :account_id) data = ::Mention.where(status_id: collection.map(&:id)).pluck(:status_id, :account_id)
data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) } data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) }


+ 7
- 3
app/controllers/about_controller.rb View File

@@ -9,9 +9,13 @@ class AboutController < ApplicationController
@initial_state_json = serializable_resource.to_json @initial_state_json = serializable_resource.to_json
end end


def more; end
def more
render layout: 'public'
end


def terms; end
def terms
render layout: 'public'
end


private private


@@ -26,7 +30,7 @@ class AboutController < ApplicationController
end end


def set_body_classes def set_body_classes
@body_classes = 'about-body'
@body_classes = 'with-modals'
end end


def initial_state_params def initial_state_params


+ 5
- 3
app/controllers/accounts_controller.rb View File

@@ -10,7 +10,9 @@ class AccountsController < ApplicationController
def show def show
respond_to do |format| respond_to do |format|
format.html do format.html do
@pinned_statuses = []
@body_classes = 'with-modals'
@pinned_statuses = []
@endorsed_accounts = @account.endorsed_accounts.to_a.sample(4)


if current_account && @account.blocking?(current_account) if current_account && @account.blocking?(current_account)
@statuses = [] @statuses = []
@@ -40,7 +42,7 @@ class AccountsController < ApplicationController
format.json do format.json do
skip_session! skip_session!


render_cached_json(['activitypub', 'actor', @account.cache_key], content_type: 'application/activity+json') do
render_cached_json(['activitypub', 'actor', @account], content_type: 'application/activity+json') do
ActiveModelSerializers::SerializableResource.new(@account, serializer: ActivityPub::ActorSerializer, adapter: ActivityPub::Adapter) ActiveModelSerializers::SerializableResource.new(@account, serializer: ActivityPub::ActorSerializer, adapter: ActivityPub::Adapter)
end end
end end
@@ -50,7 +52,7 @@ class AccountsController < ApplicationController
private private


def show_pinned_statuses? def show_pinned_statuses?
[replies_requested?, media_requested?, params[:max_id].present?, params[:since_id].present?].none?
[replies_requested?, media_requested?, params[:max_id].present?, params[:min_id].present?].none?
end end


def filtered_statuses def filtered_statuses


+ 2
- 2
app/controllers/activitypub/collections_controller.rb View File

@@ -31,7 +31,7 @@ class ActivityPub::CollectionsController < Api::BaseController
when 'featured' when 'featured'
@account.pinned_statuses.count @account.pinned_statuses.count
else else
raise ActiveRecord::NotFound
raise ActiveRecord::RecordNotFound
end end
end end


@@ -42,7 +42,7 @@ class ActivityPub::CollectionsController < Api::BaseController
scope.merge!(@account.pinned_statuses) scope.merge!(@account.pinned_statuses)
end end
else else
raise ActiveRecord::NotFound
raise ActiveRecord::RecordNotFound
end end
end end




+ 1
- 1
app/controllers/activitypub/inboxes_controller.rb View File

@@ -36,6 +36,6 @@ class ActivityPub::InboxesController < Api::BaseController
end end


def process_payload def process_payload
ActivityPub::ProcessingWorker.perform_async(signed_request_account.id, body.force_encoding('UTF-8'))
ActivityPub::ProcessingWorker.perform_async(signed_request_account.id, body.force_encoding('UTF-8'), @account&.id)
end end
end end

+ 36
- 0
app/controllers/admin/account_actions_controller.rb View 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

+ 1
- 0
app/controllers/admin/account_moderation_notes_controller.rb View File

@@ -14,6 +14,7 @@ module Admin
else else
@account = @account_moderation_note.target_account @account = @account_moderation_note.target_account
@moderation_notes = @account.targeted_moderation_notes.latest @moderation_notes = @account.targeted_moderation_notes.latest
@warnings = @account.targeted_account_warnings.latest.custom


render template: 'admin/accounts/show' render template: 'admin/accounts/show'
end end


+ 30
- 11
app/controllers/admin/accounts_controller.rb View File

@@ -2,9 +2,9 @@


module Admin module Admin
class AccountsController < BaseController 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_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 def index
authorize :account, :index? authorize :account, :index?
@@ -13,8 +13,10 @@ module Admin


def show def show
authorize @account, :show? authorize @account, :show?

@account_moderation_note = current_account.account_moderation_notes.new(target_account: @account) @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 end


def subscribe def subscribe
@@ -43,19 +45,25 @@ module Admin
redirect_to admin_account_path(@account.id) redirect_to admin_account_path(@account.id)
end 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) redirect_to admin_account_path(@account.id)
end end


def redownload def redownload
authorize @account, :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) redirect_to admin_account_path(@account.id)
end end
@@ -71,6 +79,17 @@ module Admin
redirect_to admin_account_path(@account.id) redirect_to admin_account_path(@account.id)
end 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 private


def set_account def set_account
@@ -94,8 +113,8 @@ module Admin
:local, :local,
:remote, :remote,
:by_domain, :by_domain,
:active,
:silenced, :silenced,
:recent,
:suspended, :suspended,
:username, :username,
:display_name, :display_name,


+ 12
- 1
app/controllers/admin/base_controller.rb View File

@@ -5,8 +5,19 @@ module Admin
include Authorization include Authorization
include AccountableConcern include AccountableConcern


layout 'admin'

before_action :require_staff! before_action :require_staff!
before_action :set_body_classes


layout 'admin'
private

def set_body_classes
@body_classes = 'admin'
end

def set_user
@user = Account.find(params[:account_id]).user || raise(ActiveRecord::RecordNotFound)
end
end end
end end

+ 0
- 4
app/controllers/admin/confirmations_controller.rb View File

@@ -25,10 +25,6 @@ module Admin


private private


def set_user
@user = Account.find(params[:account_id]).user || raise(ActiveRecord::RecordNotFound)
end

def check_confirmation def check_confirmation
if @user.confirmed? if @user.confirmed?
flash[:error] = I18n.t('admin.accounts.resend_confirmation.already_confirmed') flash[:error] = I18n.t('admin.accounts.resend_confirmation.already_confirmed')


+ 44
- 0
app/controllers/admin/dashboard_controller.rb View File

@@ -0,0 +1,44 @@
# frozen_string_literal: true
require 'sidekiq/api'

module Admin
class DashboardController < BaseController
def index
@users_count = User.count
@registrations_week = Redis.current.get("activity:accounts:local:#{current_week}") || 0
@logins_week = Redis.current.pfcount("activity:logins:#{current_week}")
@interactions_week = Redis.current.get("activity:interactions:#{current_week}") || 0
@relay_enabled = Relay.enabled.exists?
@single_user_mode = Rails.configuration.x.single_user_mode
@registrations_enabled = Setting.open_registrations
@deletions_enabled = Setting.open_deletion
@invites_enabled = Setting.min_invite_role == 'user'
@search_enabled = Chewy.enabled?
@version = Mastodon::Version.to_s
@database_version = ActiveRecord::Base.connection.execute('SELECT VERSION()').first['version'].match(/\A(?:PostgreSQL |)([^\s]+).*\z/)[1]
@redis_version = redis_info['redis_version']
@reports_count = Report.unresolved.count
@queue_backlog = Sidekiq::Stats.new.enqueued
@recent_users = User.confirmed.recent.includes(:account).limit(4)
@database_size = ActiveRecord::Base.connection.execute('SELECT pg_database_size(current_database())').first['pg_database_size']
@redis_size = redis_info['used_memory']
@ldap_enabled = ENV['LDAP_ENABLED'] == 'true'
@cas_enabled = ENV['CAS_ENABLED'] == 'true'
@saml_enabled = ENV['SAML_ENABLED'] == 'true'
@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

def current_week
@current_week ||= Time.now.utc.to_date.cweek
end

def redis_info
@redis_info ||= Redis.current.info
end
end
end

+ 4
- 9
app/controllers/admin/domain_blocks_controller.rb View File

@@ -4,14 +4,9 @@ module Admin
class DomainBlocksController < BaseController class DomainBlocksController < BaseController
before_action :set_domain_block, only: [:show, :destroy] before_action :set_domain_block, only: [:show, :destroy]


def index
authorize :domain_block, :index?
@domain_blocks = DomainBlock.page(params[:page])
end

def new def new
authorize :domain_block, :create? authorize :domain_block, :create?
@domain_block = DomainBlock.new
@domain_block = DomainBlock.new(domain: params[:_domain])
end end


def create def create
@@ -22,7 +17,7 @@ module Admin
if @domain_block.save if @domain_block.save
DomainBlockWorker.perform_async(@domain_block.id) DomainBlockWorker.perform_async(@domain_block.id)
log_action :create, @domain_block 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 else
render :new render :new
end end
@@ -36,7 +31,7 @@ module Admin
authorize @domain_block, :destroy? authorize @domain_block, :destroy?
UnblockDomainService.new.call(@domain_block, retroactive_unblock?) UnblockDomainService.new.call(@domain_block, retroactive_unblock?)
log_action :destroy, @domain_block 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 end


private private
@@ -46,7 +41,7 @@ module Admin
end end


def resource_params def resource_params
params.require(:domain_block).permit(:domain, :severity, :reject_media, :retroactive)
params.require(:domain_block).permit(:domain, :severity, :reject_media, :reject_reports, :retroactive)
end end


def retroactive_unblock? def retroactive_unblock?


+ 18
- 0
app/controllers/admin/followers_controller.rb View 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

+ 14
- 13
app/controllers/admin/instances_controller.rb View File

@@ -4,14 +4,21 @@ module Admin
class InstancesController < BaseController class InstancesController < BaseController
def index def index
authorize :instance, :index? authorize :instance, :index?

@instances = ordered_instances @instances = ordered_instances
end 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 end


private private
@@ -27,17 +34,11 @@ module Admin
helper_method :paginated_instances helper_method :paginated_instances


def ordered_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 end


def filter_params def filter_params
params.permit(
:domain_name
)
params.permit(:limited)
end end
end end
end end

+ 6
- 0
app/controllers/admin/invites_controller.rb View File

@@ -30,6 +30,12 @@ module Admin
redirect_to admin_invites_path redirect_to admin_invites_path
end end


def deactivate_all
authorize :invite, :deactivate_all?
Invite.available.in_batches.update_all(expires_at: Time.now.utc)
redirect_to admin_invites_path
end

private private


def resource_params def resource_params


+ 58
- 0
app/controllers/admin/relays_controller.rb View File

@@ -0,0 +1,58 @@
# frozen_string_literal: true

module Admin
class RelaysController < BaseController
before_action :set_relay, except: [:index, :new, :create]

def index
authorize :relay, :update?
@relays = Relay.all
end

def new
authorize :relay, :update?
@relay = Relay.new(inbox_url: Relay::PRESET_RELAY)
end

def create
authorize :relay, :update?

@relay = Relay.new(resource_params)

if @relay.save
@relay.enable!
redirect_to admin_relays_path
else
render action: :new
end
end

def destroy
authorize :relay, :update?
@relay.destroy
redirect_to admin_relays_path
end

def enable
authorize :relay, :update?
@relay.enable!
redirect_to admin_relays_path
end

def disable
authorize :relay, :update?
@relay.disable!
redirect_to admin_relays_path
end

private

def set_relay
@relay = Relay.find(params[:id])
end

def resource_params
params.require(:relay).permit(:inbox_url)
end
end
end

+ 23
- 53
app/controllers/admin/reports_controller.rb View File

@@ -13,72 +13,42 @@ module Admin
authorize @report, :show? authorize @report, :show?


@report_note = @report.notes.new @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 @form = Form::StatusBatch.new
end end


def update
def assign_to_self
authorize @report, :update? authorize @report, :update?
process_report

if @report.action_taken?
redirect_to admin_reports_path, notice: I18n.t('admin.reports.resolved_msg')
else
redirect_to admin_report_path(@report)
end
@report.update!(assigned_account_id: current_account.id)
log_action :assigned_to_self, @report
redirect_to admin_report_path(@report)
end 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 'suspend'
Admin::SuspensionWorker.perform_async(@report.target_account.id)

log_action :resolve, @report
log_action :suspend, @report.target_account

resolve_all_target_account_reports
when 'silence'
@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
def unassign
authorize @report, :update?
@report.update!(assigned_account_id: nil)
log_action :unassigned, @report
redirect_to admin_report_path(@report)
end 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)
def reopen
authorize @report, :update?
@report.unresolve!
log_action :reopen, @report
redirect_to admin_report_path(@report)
end end


def unresolved_reports_for_target_account
Report.where(
target_account: @report.target_account
).unresolved
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 end


private

def filtered_reports 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 end


def filter_params def filter_params


+ 0
- 6
app/controllers/admin/resets_controller.rb View File

@@ -10,11 +10,5 @@ module Admin
log_action :reset_password, @user log_action :reset_password, @user
redirect_to admin_accounts_path redirect_to admin_accounts_path
end end

private

def set_user
@user = Account.find(params[:account_id]).user || raise(ActiveRecord::RecordNotFound)
end
end end
end end

+ 0
- 6
app/controllers/admin/roles_controller.rb View File

@@ -17,11 +17,5 @@ module Admin
log_action :demote, @user log_action :demote, @user
redirect_to admin_account_path(@user.account_id) redirect_to admin_account_path(@user.account_id)
end end

private

def set_user
@user = Account.find(params[:account_id]).user || raise(ActiveRecord::RecordNotFound)
end
end end
end end

+ 7
- 0
app/controllers/admin/settings_controller.rb View File

@@ -6,6 +6,7 @@ module Admin
site_contact_username site_contact_username
site_contact_email site_contact_email
site_title site_title
site_short_description
site_description site_description
site_extended_description site_extended_description
site_terms site_terms
@@ -15,13 +16,17 @@ module Admin
timeline_preview timeline_preview
show_staff_badge show_staff_badge
bootstrap_timeline_accounts bootstrap_timeline_accounts
theme
thumbnail thumbnail
hero hero
mascot
min_invite_role min_invite_role
activity_api_enabled activity_api_enabled
peers_api_enabled peers_api_enabled
show_known_fediverse_at_about_page show_known_fediverse_at_about_page
preview_sensitive_media preview_sensitive_media
custom_css
profile_directory
).freeze ).freeze


BOOLEAN_SETTINGS = %w( BOOLEAN_SETTINGS = %w(
@@ -33,11 +38,13 @@ module Admin
peers_api_enabled peers_api_enabled
show_known_fediverse_at_about_page show_known_fediverse_at_about_page
preview_sensitive_media preview_sensitive_media
profile_directory
).freeze ).freeze


UPLOAD_SETTINGS = %w( UPLOAD_SETTINGS = %w(
thumbnail thumbnail
hero hero
mascot
).freeze ).freeze


def edit def edit


+ 0
- 27
app/controllers/admin/silences_controller.rb View File

@@ -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

+ 13
- 0
app/controllers/admin/statuses_controller.rb View File

@@ -22,6 +22,15 @@ module Admin
@form = Form::StatusBatch.new @form = Form::StatusBatch.new
end end


def show
authorize :status, :index?

@statuses = @account.statuses.where(id: params[:id])
authorize @statuses.first, :show?

@form = Form::StatusBatch.new
end

def create def create
authorize :status, :update? authorize :status, :update?


@@ -29,6 +38,10 @@ module Admin
flash[:alert] = I18n.t('admin.statuses.failed_to_execute') unless @form.save flash[:alert] = I18n.t('admin.statuses.failed_to_execute') unless @form.save


redirect_to admin_account_statuses_path(@account.id, current_params) redirect_to admin_account_statuses_path(@account.id, current_params)
rescue ActionController::ParameterMissing
flash[:alert] = I18n.t('admin.statuses.no_status_selected')

redirect_to admin_account_statuses_path(@account.id, current_params)
end end


private private


+ 0
- 27
app/controllers/admin/suspensions_controller.rb View File

@@ -1,27 +0,0 @@
# frozen_string_literal: true

module Admin
class SuspensionsController < BaseController
before_action :set_account

def create
authorize @account, :suspend?
Admin::SuspensionWorker.perform_async(@account.id)
log_action :suspend, @account
redirect_to admin_accounts_path
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
end
end

+ 44
- 0
app/controllers/admin/tags_controller.rb View 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
- 2
app/controllers/admin/two_factor_authentications_controller.rb View File

@@ -2,7 +2,7 @@


module Admin module Admin
class TwoFactorAuthenticationsController < BaseController class TwoFactorAuthenticationsController < BaseController
before_action :set_user
before_action :set_target_user


def destroy def destroy
authorize @user, :disable_2fa? authorize @user, :disable_2fa?
@@ -13,7 +13,7 @@ module Admin


private private


def set_user
def set_target_user
@user = User.find(params[:user_id]) @user = User.find(params[:user_id])
end end
end end


+ 58
- 0
app/controllers/admin/warning_presets_controller.rb View 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

+ 10
- 6
app/controllers/api/base_controller.rb View File

@@ -7,6 +7,8 @@ class Api::BaseController < ApplicationController
include RateLimitHeaders include RateLimitHeaders


skip_before_action :store_current_location skip_before_action :store_current_location
skip_before_action :check_user_permissions

protect_from_forgery with: :null_session protect_from_forgery with: :null_session


rescue_from ActiveRecord::RecordInvalid, Mastodon::ValidationError do |e| rescue_from ActiveRecord::RecordInvalid, Mastodon::ValidationError do |e|
@@ -51,8 +53,8 @@ class Api::BaseController < ApplicationController
[params[:limit].to_i.abs, default_limit * 2].min [params[:limit].to_i.abs, default_limit * 2].min
end end


def truthy_param?(key)
ActiveModel::Type::Boolean.new.cast(params[key])
def params_slice(*keys)
params.slice(*keys).permit(*keys)
end end


def current_resource_owner def current_resource_owner
@@ -66,12 +68,14 @@ class Api::BaseController < ApplicationController
end end


def require_user! def require_user!
if current_user && !current_user.disabled?
set_user_activity
elsif current_user
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 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 else
render json: { error: 'This method requires an authenticated user' }, status: 422
set_user_activity
end end
end end




+ 1
- 1
app/controllers/api/v1/accounts/credentials_controller.rb View File

@@ -21,7 +21,7 @@ class Api::V1::Accounts::CredentialsController < Api::BaseController
private private


def account_params 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 end


def user_settings_params def user_settings_params


+ 1
- 1
app/controllers/api/v1/accounts/follower_accounts_controller.rb View File

@@ -25,7 +25,7 @@ class Api::V1::Accounts::FollowerAccountsController < Api::BaseController
end end


def default_accounts def default_accounts
Account.includes(:active_relationships).references(:active_relationships)
Account.includes(:active_relationships, :account_stat).references(:active_relationships)
end end


def paginated_follows def paginated_follows


+ 1
- 1
app/controllers/api/v1/accounts/following_accounts_controller.rb View File

@@ -25,7 +25,7 @@ class Api::V1::Accounts::FollowingAccountsController < Api::BaseController
end end


def default_accounts def default_accounts
Account.includes(:passive_relationships).references(:passive_relationships)
Account.includes(:passive_relationships, :account_stat).references(:passive_relationships)
end end


def paginated_follows def paginated_follows


+ 32
- 0
app/controllers/api/v1/accounts/pins_controller.rb View File

@@ -0,0 +1,32 @@
# frozen_string_literal: true

class Api::V1::Accounts::PinsController < Api::BaseController
include Authorization

before_action -> { doorkeeper_authorize! :write, :'write:accounts' }
before_action :require_user!
before_action :set_account

respond_to :json

def create
AccountPin.create!(account: current_account, target_account: @account)
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships_presenter
end

def destroy
pin = AccountPin.find_by(account: current_account, target_account: @account)
pin&.destroy!
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships_presenter
end

private

def set_account
@account = Account.find(params[:account_id])
end

def relationships_presenter
AccountRelationshipsPresenter.new([@account.id], current_user.account_id)
end
end

+ 8
- 7
app/controllers/api/v1/accounts/statuses_controller.rb View File

@@ -1,7 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true


class Api::V1::Accounts::StatusesController < Api::BaseController 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 before_action :set_account
after_action :insert_pagination_headers after_action :insert_pagination_headers


@@ -28,14 +28,11 @@ class Api::V1::Accounts::StatusesController < Api::BaseController


def account_statuses def account_statuses
statuses = truthy_param?(:pinned) ? pinned_scope : permitted_account_statuses statuses = truthy_param?(:pinned) ? pinned_scope : permitted_account_statuses
statuses = statuses.paginate_by_max_id(
limit_param(DEFAULT_STATUSES_LIMIT),
params[:max_id],
params[:since_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!(only_media_scope) if truthy_param?(:only_media)
statuses.merge!(no_replies_scope) if truthy_param?(:exclude_replies) statuses.merge!(no_replies_scope) if truthy_param?(:exclude_replies)
statuses.merge!(no_reblogs_scope) if truthy_param?(:exclude_reblogs)


statuses statuses
end end
@@ -66,6 +63,10 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
Status.without_replies Status.without_replies
end end


def no_reblogs_scope
Status.without_reblogs
end

def pagination_params(core_params) def pagination_params(core_params)
params.slice(:limit, :only_media, :exclude_replies).permit(:limit, :only_media, :exclude_replies).merge(core_params) params.slice(:limit, :only_media, :exclude_replies).permit(:limit, :only_media, :exclude_replies).merge(core_params)
end end
@@ -82,7 +83,7 @@ class Api::V1::Accounts::StatusesController < Api::BaseController


def prev_path def prev_path
unless @statuses.empty? unless @statuses.empty?
api_v1_account_statuses_url pagination_params(since_id: pagination_since_id)
api_v1_account_statuses_url pagination_params(min_id: pagination_since_id)
end end
end end




+ 24
- 4
app/controllers/api/v1/accounts_controller.rb View File

@@ -1,14 +1,16 @@
# frozen_string_literal: true # frozen_string_literal: true


class Api::V1::AccountsController < Api::BaseController 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:follows' }, only: [:follow, :unfollow]
before_action -> { doorkeeper_authorize! :follow, :'write:mutes' }, only: [:mute, :unmute] before_action -> { doorkeeper_authorize! :follow, :'write:mutes' }, only: [:mute, :unmute]
before_action -> { doorkeeper_authorize! :follow, :'write:blocks' }, only: [:block, :unblock] 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_account_suspension, only: [:show]
before_action :check_enabled_registrations, only: [:create]


respond_to :json respond_to :json


@@ -16,8 +18,18 @@ class Api::V1::AccountsController < Api::BaseController
render json: @account, serializer: REST::AccountSerializer render json: @account, serializer: REST::AccountSerializer
end 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 def follow
FollowService.new.call(current_user.account, @account.acct, reblogs: truthy_param?(:reblogs))
FollowService.new.call(current_user.account, @account, reblogs: truthy_param?(:reblogs))


options = @account.locked? ? {} : { following_map: { @account.id => { reblogs: truthy_param?(:reblogs) } }, requested_map: { @account.id => false } } options = @account.locked? ? {} : { following_map: { @account.id => { reblogs: truthy_param?(:reblogs) } }, requested_map: { @account.id => false } }


@@ -62,4 +74,12 @@ class Api::V1::AccountsController < Api::BaseController
def check_account_suspension def check_account_suspension
gone if @account.suspended? gone if @account.suspended?
end 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 end

+ 1
- 1
app/controllers/api/v1/blocks_controller.rb View File

@@ -19,7 +19,7 @@ class Api::V1::BlocksController < Api::BaseController
end end


def paginated_blocks def paginated_blocks
@paginated_blocks ||= Block.eager_load(:target_account)
@paginated_blocks ||= Block.eager_load(target_account: :account_stat)
.where(account: current_account) .where(account: current_account)
.paginate_by_max_id( .paginate_by_max_id(
limit_param(DEFAULT_ACCOUNTS_LIMIT), limit_param(DEFAULT_ACCOUNTS_LIMIT),


+ 71
- 0
app/controllers/api/v1/conversations_controller.rb View File

@@ -0,0 +1,71 @@
# frozen_string_literal: true

class Api::V1::ConversationsController < Api::BaseController
LIMIT = 20

before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, only: :index
before_action -> { doorkeeper_authorize! :write, :'write:conversations' }, except: :index
before_action :require_user!
before_action :set_conversation, except: :index
after_action :insert_pagination_headers, only: :index

respond_to :json

def index
@conversations = paginated_conversations
render json: @conversations, each_serializer: REST::ConversationSerializer
end

def read
@conversation.update!(unread: false)
render json: @conversation, serializer: REST::ConversationSerializer
end

def destroy
@conversation.destroy!
render_empty
end

private

def set_conversation
@conversation = AccountConversation.where(account: current_account).find(params[:id])
end

def paginated_conversations
AccountConversation.where(account: current_account)
.paginate_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
end

def insert_pagination_headers
set_pagination_headers(next_path, prev_path)
end

def next_path
if records_continue?
api_v1_conversations_url pagination_params(max_id: pagination_max_id)
end
end

def prev_path
unless @conversations.empty?
api_v1_conversations_url pagination_params(min_id: pagination_since_id)
end
end

def pagination_max_id
@conversations.last.last_status_id
end

def pagination_since_id
@conversations.first.last_status_id
end

def records_continue?
@conversations.size == limit_param(LIMIT)
end

def pagination_params(core_params)
params.slice(:limit).permit(:limit).merge(core_params)
end
end

+ 3
- 1
app/controllers/api/v1/custom_emojis_controller.rb View File

@@ -4,6 +4,8 @@ class Api::V1::CustomEmojisController < Api::BaseController
respond_to :json respond_to :json


def index 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
end end

+ 72
- 0
app/controllers/api/v1/endorsements_controller.rb View File

@@ -0,0 +1,72 @@
# frozen_string_literal: true

class Api::V1::EndorsementsController < Api::BaseController
before_action -> { doorkeeper_authorize! :read, :'read:accounts' }
before_action :require_user!
after_action :insert_pagination_headers

respond_to :json

def index
@accounts = load_accounts
render json: @accounts, each_serializer: REST::AccountSerializer
end

private

def load_accounts
if unlimited?
endorsed_accounts.all
else
endorsed_accounts.paginate_by_max_id(
limit_param(DEFAULT_ACCOUNTS_LIMIT),
params[:max_id],
params[:since_id]
)
end
end

def endorsed_accounts
current_account.endorsed_accounts.includes(:account_stat)
end

def insert_pagination_headers
set_pagination_headers(next_path, prev_path)
end

def next_path
return if unlimited?

if records_continue?
api_v1_endorsements_url pagination_params(max_id: pagination_max_id)
end
end

def prev_path
return if unlimited?

unless @accounts.empty?
api_v1_endorsements_url pagination_params(since_id: pagination_since_id)
end
end

def pagination_max_id
@accounts.last.id
end

def pagination_since_id
@accounts.first.id
end

def records_continue?
@accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
end

def pagination_params(core_params)
params.slice(:limit).permit(:limit).merge(core_params)
end

def unlimited?
params[:limit] == '0'
end
end

+ 3
- 4
app/controllers/api/v1/favourites_controller.rb View File

@@ -26,10 +26,9 @@ class Api::V1::FavouritesController < Api::BaseController
end end


def results def results
@_results ||= account_favourites.paginate_by_max_id(
@_results ||= account_favourites.paginate_by_id(
limit_param(DEFAULT_STATUSES_LIMIT), limit_param(DEFAULT_STATUSES_LIMIT),
params[:max_id],
params[:since_id]
params_slice(:max_id, :since_id, :min_id)
) )
end end


@@ -49,7 +48,7 @@ class Api::V1::FavouritesController < Api::BaseController


def prev_path def prev_path
unless results.empty? unless results.empty?
api_v1_favourites_url pagination_params(since_id: pagination_since_id)
api_v1_favourites_url pagination_params(min_id: pagination_since_id)
end end
end end




+ 2
- 1
app/controllers/api/v1/follow_requests_controller.rb View File

@@ -13,6 +13,7 @@ class Api::V1::FollowRequestsController < Api::BaseController


def authorize def authorize
AuthorizeFollowService.new.call(account, current_account) AuthorizeFollowService.new.call(account, current_account)
NotifyService.new.call(current_account, Follow.find_by(account: account, target_account: current_account))
render_empty render_empty
end end


@@ -32,7 +33,7 @@ class Api::V1::FollowRequestsController < Api::BaseController
end end


def default_accounts def default_accounts
Account.includes(:follow_requests).references(:follow_requests)
Account.includes(:follow_requests, :account_stat).references(:follow_requests)
end end


def paginated_follow_requests def paginated_follow_requests


+ 3
- 1
app/controllers/api/v1/instances_controller.rb View File

@@ -4,6 +4,8 @@ class Api::V1::InstancesController < Api::BaseController
respond_to :json respond_to :json


def show def show
render json: {}, serializer: REST::InstanceSerializer
render_cached_json('api:v1:instances', expires_in: 5.minutes) do
ActiveModelSerializers::SerializableResource.new({}, serializer: REST::InstanceSerializer)
end
end end
end end

+ 3
- 3
app/controllers/api/v1/lists/accounts_controller.rb View File

@@ -1,7 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true


class Api::V1::Lists::AccountsController < Api::BaseController class Api::V1::Lists::AccountsController < Api::BaseController
before_action -> { doorkeeper_authorize! :read, :'read:lists' }, only: [:show]
before_action -> { doorkeeper_authorize! :read, :'read:lists' }, only: [:show]
before_action -> { doorkeeper_authorize! :write, :'write:lists' }, except: [:show] before_action -> { doorkeeper_authorize! :write, :'write:lists' }, except: [:show]


before_action :require_user! before_action :require_user!
@@ -37,9 +37,9 @@ class Api::V1::Lists::AccountsController < Api::BaseController


def load_accounts def load_accounts
if unlimited? if unlimited?
@list.accounts.all
@list.accounts.includes(:account_stat).all
else 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
end end




+ 1
- 1
app/controllers/api/v1/lists_controller.rb View File

@@ -1,7 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true


class Api::V1::ListsController < Api::BaseController class Api::V1::ListsController < Api::BaseController
before_action -> { doorkeeper_authorize! :read, :'read:lists' }, only: [:index, :show]
before_action -> { doorkeeper_authorize! :read, :'read:lists' }, only: [:index, :show]
before_action -> { doorkeeper_authorize! :write, :'write:lists' }, except: [:index, :show] before_action -> { doorkeeper_authorize! :write, :'write:lists' }, except: [:index, :show]


before_action :require_user! before_action :require_user!


+ 12
- 14
app/controllers/api/v1/mutes_controller.rb View File

@@ -15,19 +15,17 @@ class Api::V1::MutesController < Api::BaseController
private private


def load_accounts def load_accounts
default_accounts.merge(paginated_mutes).to_a
end

def default_accounts
Account.includes(:muted_by).references(:muted_by)
paginated_mutes.map(&:target_account)
end end


def paginated_mutes def paginated_mutes
Mute.where(account: current_account).paginate_by_max_id(
limit_param(DEFAULT_ACCOUNTS_LIMIT),
params[:max_id],
params[:since_id]
)
@paginated_mutes ||= Mute.eager_load(:target_account)
.where(account: current_account)
.paginate_by_max_id(
limit_param(DEFAULT_ACCOUNTS_LIMIT),
params[:max_id],
params[:since_id]
)
end end


def insert_pagination_headers def insert_pagination_headers
@@ -41,21 +39,21 @@ class Api::V1::MutesController < Api::BaseController
end end


def prev_path def prev_path
unless @accounts.empty?
unless paginated_mutes.empty?
api_v1_mutes_url pagination_params(since_id: pagination_since_id) api_v1_mutes_url pagination_params(since_id: pagination_since_id)
end end
end end


def pagination_max_id def pagination_max_id
@accounts.last.muted_by_ids.last
paginated_mutes.last.id
end end


def pagination_since_id def pagination_since_id
@accounts.first.muted_by_ids.first
paginated_mutes.first.id
end end


def records_continue? def records_continue?
@accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
paginated_mutes.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
end end


def pagination_params(core_params) def pagination_params(core_params)


+ 3
- 4
app/controllers/api/v1/notifications_controller.rb View File

@@ -37,10 +37,9 @@ class Api::V1::NotificationsController < Api::BaseController
end end


def paginated_notifications def paginated_notifications
browserable_account_notifications.paginate_by_max_id(
browserable_account_notifications.paginate_by_id(
limit_param(DEFAULT_NOTIFICATIONS_LIMIT), limit_param(DEFAULT_NOTIFICATIONS_LIMIT),
params[:max_id],
params[:since_id]
params_slice(:max_id, :since_id, :min_id)
) )
end end


@@ -64,7 +63,7 @@ class Api::V1::NotificationsController < Api::BaseController


def prev_path def prev_path
unless @notifications.empty? unless @notifications.empty?
api_v1_notifications_url pagination_params(since_id: pagination_since_id)
api_v1_notifications_url pagination_params(min_id: pagination_since_id)
end end
end end




+ 1
- 7
app/controllers/api/v1/reports_controller.rb View File

@@ -1,17 +1,11 @@
# frozen_string_literal: true # frozen_string_literal: true


class Api::V1::ReportsController < Api::BaseController class Api::V1::ReportsController < Api::BaseController
before_action -> { doorkeeper_authorize! :read, :'read:reports' }, except: [:create]
before_action -> { doorkeeper_authorize! :write, :'write:reports' }, only: [:create] before_action -> { doorkeeper_authorize! :write, :'write:reports' }, only: [:create]
before_action :require_user! before_action :require_user!


respond_to :json respond_to :json


def index
@reports = current_account.reports
render json: @reports, each_serializer: REST::ReportSerializer
end

def create def create
@report = ReportService.new.call( @report = ReportService.new.call(
current_account, current_account,
@@ -27,7 +21,7 @@ class Api::V1::ReportsController < Api::BaseController
private private


def reported_status_ids def reported_status_ids
Status.find(status_ids).pluck(:id)
reported_account.statuses.find(status_ids).pluck(:id)
end end


def status_ids def status_ids


+ 77
- 0
app/controllers/api/v1/scheduled_statuses_controller.rb View 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

+ 1
- 1
app/controllers/api/v1/statuses/favourited_by_accounts_controller.rb View File

@@ -22,7 +22,7 @@ class Api::V1::Statuses::FavouritedByAccountsController < Api::BaseController


def default_accounts def default_accounts
Account Account
.includes(:favourites)
.includes(:favourites, :account_stat)
.references(:favourites) .references(:favourites)
.where(favourites: { status_id: @status.id }) .where(favourites: { status_id: @status.id })
end end


+ 2
- 2
app/controllers/api/v1/statuses/reblogged_by_accounts_controller.rb View File

@@ -21,11 +21,11 @@ class Api::V1::Statuses::RebloggedByAccountsController < Api::BaseController
end end


def default_accounts def default_accounts
Account.includes(:statuses).references(:statuses)
Account.includes(:statuses, :account_stat).references(:statuses)
end end


def paginated_statuses def paginated_statuses
Status.where(reblog_of_id: @status.id).paginate_by_max_id(
Status.where(reblog_of_id: @status.id).where(visibility: [:public, :unlisted]).paginate_by_max_id(
limit_param(DEFAULT_ACCOUNTS_LIMIT), limit_param(DEFAULT_ACCOUNTS_LIMIT),
params[:max_id], params[:max_id],
params[:since_id] params[:since_id]


+ 6
- 6
app/controllers/api/v1/statuses_controller.rb View File

@@ -17,8 +17,7 @@ class Api::V1::StatusesController < Api::BaseController
CONTEXT_LIMIT = 4_096 CONTEXT_LIMIT = 4_096


def show def show
cached = Rails.cache.read(@status.cache_key)
@status = cached unless cached.nil?
@status = cache_collection([@status], Status).first
render json: @status, serializer: REST::StatusSerializer render json: @status, serializer: REST::StatusSerializer
end end


@@ -46,16 +45,17 @@ class Api::V1::StatusesController < Api::BaseController


def create def create
@status = PostStatusService.new.call(current_user.account, @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], media_ids: status_params[:media_ids],
sensitive: status_params[:sensitive], sensitive: status_params[:sensitive],
spoiler_text: status_params[:spoiler_text], spoiler_text: status_params[:spoiler_text],
visibility: status_params[:visibility], visibility: status_params[:visibility],
scheduled_at: status_params[:scheduled_at],
application: doorkeeper_token.application, application: doorkeeper_token.application,
idempotency: request.headers['Idempotency-Key']) idempotency: request.headers['Idempotency-Key'])


render json: @status, serializer: REST::StatusSerializer
render json: @status, serializer: @status.is_a?(ScheduledStatus) ? REST::ScheduledStatusSerializer : REST::StatusSerializer
end end


def destroy def destroy
@@ -78,7 +78,7 @@ class Api::V1::StatusesController < Api::BaseController
end end


def status_params def status_params
params.permit(:status, :in_reply_to_id, :sensitive, :spoiler_text, :visibility, media_ids: [])
params.permit(:status, :in_reply_to_id, :sensitive, :spoiler_text, :visibility, :scheduled_at, media_ids: [])
end end


def pagination_params(core_params) def pagination_params(core_params)


+ 3
- 2
app/controllers/api/v1/timelines/home_controller.rb View File

@@ -30,7 +30,8 @@ class Api::V1::Timelines::HomeController < Api::BaseController
account_home_feed.get( account_home_feed.get(
limit_param(DEFAULT_STATUSES_LIMIT), limit_param(DEFAULT_STATUSES_LIMIT),
params[:max_id], params[:max_id],
params[:since_id]
params[:since_id],
params[:min_id]
) )
end end


@@ -51,7 +52,7 @@ class Api::V1::Timelines::HomeController < Api::BaseController
end end


def prev_path def prev_path
api_v1_timelines_home_url pagination_params(since_id: pagination_since_id)
api_v1_timelines_home_url pagination_params(min_id: pagination_since_id)
end end


def pagination_max_id def pagination_max_id


+ 3
- 2
app/controllers/api/v1/timelines/list_controller.rb View File

@@ -32,7 +32,8 @@ class Api::V1::Timelines::ListController < Api::BaseController
list_feed.get( list_feed.get(
limit_param(DEFAULT_STATUSES_LIMIT), limit_param(DEFAULT_STATUSES_LIMIT),
params[:max_id], params[:max_id],
params[:since_id]
params[:since_id],
params[:min_id]
) )
end end


@@ -53,7 +54,7 @@ class Api::V1::Timelines::ListController < Api::BaseController
end end


def prev_path def prev_path
api_v1_timelines_list_url params[:id], pagination_params(since_id: pagination_since_id)
api_v1_timelines_list_url params[:id], pagination_params(min_id: pagination_since_id)
end end


def pagination_max_id def pagination_max_id


+ 3
- 4
app/controllers/api/v1/timelines/public_controller.rb View File

@@ -21,10 +21,9 @@ class Api::V1::Timelines::PublicController < Api::BaseController
end end


def public_statuses def public_statuses
statuses = public_timeline_statuses.paginate_by_max_id(
statuses = public_timeline_statuses.paginate_by_id(
limit_param(DEFAULT_STATUSES_LIMIT), limit_param(DEFAULT_STATUSES_LIMIT),
params[:max_id],
params[:since_id]
params_slice(:max_id, :since_id, :min_id)
) )


if truthy_param?(:only_media) if truthy_param?(:only_media)
@@ -53,7 +52,7 @@ class Api::V1::Timelines::PublicController < Api::BaseController
end end


def prev_path def prev_path
api_v1_timelines_public_url pagination_params(since_id: pagination_since_id)
api_v1_timelines_public_url pagination_params(min_id: pagination_since_id)
end end


def pagination_max_id def pagination_max_id


+ 4
- 5
app/controllers/api/v1/timelines/tag_controller.rb View File

@@ -29,10 +29,9 @@ class Api::V1::Timelines::TagController < Api::BaseController
if @tag.nil? if @tag.nil?
[] []
else else
statuses = tag_timeline_statuses.paginate_by_max_id(
statuses = tag_timeline_statuses.paginate_by_id(
limit_param(DEFAULT_STATUSES_LIMIT), limit_param(DEFAULT_STATUSES_LIMIT),
params[:max_id],
params[:since_id]
params_slice(:max_id, :since_id, :min_id)
) )


if truthy_param?(:only_media) if truthy_param?(:only_media)
@@ -46,7 +45,7 @@ class Api::V1::Timelines::TagController < Api::BaseController
end end


def tag_timeline_statuses 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 end


def insert_pagination_headers def insert_pagination_headers
@@ -62,7 +61,7 @@ class Api::V1::Timelines::TagController < Api::BaseController
end end


def prev_path def prev_path
api_v1_timelines_tag_url params[:id], pagination_params(since_id: pagination_since_id)
api_v1_timelines_tag_url params[:id], pagination_params(min_id: pagination_since_id)
end end


def pagination_max_id def pagination_max_id


+ 1
- 0
app/controllers/api/web/embeds_controller.rb View File

@@ -10,6 +10,7 @@ class Api::Web::EmbedsController < Api::Web::BaseController
render json: status, serializer: OEmbedSerializer, width: 400 render json: status, serializer: OEmbedSerializer, width: 400
rescue ActiveRecord::RecordNotFound rescue ActiveRecord::RecordNotFound
oembed = FetchOEmbedService.new.call(params[:url]) oembed = FetchOEmbedService.new.call(params[:url])
oembed[:html] = Formatter.instance.sanitize(oembed[:html], Sanitize::Config::MASTODON_OEMBED) if oembed[:html].present?


if oembed if oembed
render json: oembed render json: oembed


+ 14
- 14
app/controllers/application_controller.rb View File

@@ -24,7 +24,7 @@ class ApplicationController < ActionController::Base
rescue_from Mastodon::NotPermittedError, with: :forbidden rescue_from Mastodon::NotPermittedError, with: :forbidden


before_action :store_current_location, except: :raise_not_found, unless: :devise_controller? before_action :store_current_location, except: :raise_not_found, unless: :devise_controller?
before_action :check_suspension, if: :user_signed_in?
before_action :check_user_permissions, if: :user_signed_in?


def raise_not_found def raise_not_found
raise ActionController::RoutingError, "No route matches #{params[:unmatched_route]}" raise ActionController::RoutingError, "No route matches #{params[:unmatched_route]}"
@@ -48,8 +48,8 @@ class ApplicationController < ActionController::Base
forbidden unless current_user&.staff? forbidden unless current_user&.staff?
end end


def check_suspension
forbidden if current_user.account.suspended?
def check_user_permissions
forbidden if current_user.disabled? || current_user.account.suspended?
end end


def after_sign_out_path_for(_resource_or_scope) def after_sign_out_path_for(_resource_or_scope)
@@ -58,6 +58,10 @@ class ApplicationController < ActionController::Base


protected protected


def truthy_param?(key)
ActiveModel::Type::Boolean.new.cast(params[key])
end

def forbidden def forbidden
respond_with_error(403) respond_with_error(403)
end end
@@ -95,7 +99,7 @@ class ApplicationController < ActionController::Base
end end


def current_theme def current_theme
return Setting.default_settings['theme'] unless Themes.instance.names.include? current_user&.setting_theme
return Setting.theme unless Themes.instance.names.include? current_user&.setting_theme
current_user.setting_theme current_user.setting_theme
end end


@@ -103,29 +107,26 @@ class ApplicationController < ActionController::Base
return raw unless klass.respond_to?(:with_includes) return raw unless klass.respond_to?(:with_includes)


raw = raw.cache_ids.to_a if raw.is_a?(ActiveRecord::Relation) raw = raw.cache_ids.to_a if raw.is_a?(ActiveRecord::Relation)
uncached_ids = []
cached_keys_with_value = Rails.cache.read_multi(*raw.map(&:cache_key))

raw.each do |item|
uncached_ids << item.id unless cached_keys_with_value.key?(item.cache_key)
end
cached_keys_with_value = Rails.cache.read_multi(*raw).transform_keys(&:id)
uncached_ids = raw.map(&:id) - cached_keys_with_value.keys


klass.reload_stale_associations!(cached_keys_with_value.values) if klass.respond_to?(:reload_stale_associations!) klass.reload_stale_associations!(cached_keys_with_value.values) if klass.respond_to?(:reload_stale_associations!)


unless uncached_ids.empty? unless uncached_ids.empty?
uncached = klass.where(id: uncached_ids).with_includes.map { |item| [item.id, item] }.to_h
uncached = klass.where(id: uncached_ids).with_includes.each_with_object({}) { |item, h| h[item.id] = item }


uncached.each_value do |item| uncached.each_value do |item|
Rails.cache.write(item.cache_key, item)
Rails.cache.write(item, item)
end end
end end


raw.map { |item| cached_keys_with_value[item.cache_key] || uncached[item.id] }.compact
raw.map { |item| cached_keys_with_value[item.id] || uncached[item.id] }.compact
end end


def respond_with_error(code) def respond_with_error(code)
respond_to do |format| respond_to do |format|
format.any { head code } format.any { head code }

format.html do format.html do
set_locale set_locale
render "errors/#{code}", layout: 'error', status: code render "errors/#{code}", layout: 'error', status: code
@@ -135,7 +136,6 @@ class ApplicationController < ActionController::Base


def render_cached_json(cache_key, **options) def render_cached_json(cache_key, **options)
options[:expires_in] ||= 3.minutes options[:expires_in] ||= 3.minutes
cache_key = cache_key.join(':') if cache_key.is_a?(Enumerable)
cache_public = options.key?(:public) ? options.delete(:public) : true cache_public = options.key?(:public) ? options.delete(:public) : true
content_type = options.delete(:content_type) || 'application/json' content_type = options.delete(:content_type) || 'application/json'




+ 14
- 1
app/controllers/auth/confirmations_controller.rb View File

@@ -3,11 +3,12 @@
class Auth::ConfirmationsController < Devise::ConfirmationsController class Auth::ConfirmationsController < Devise::ConfirmationsController
layout 'auth' layout 'auth'


before_action :set_body_classes
before_action :set_user, only: [:finish_signup] before_action :set_user, only: [:finish_signup]


# GET/PATCH /users/:id/finish_signup
def finish_signup def finish_signup
return unless request.patch? && params[:user] return unless request.patch? && params[:user]

if @user.update(user_params) if @user.update(user_params)
@user.skip_reconfirmation! @user.skip_reconfirmation!
bypass_sign_in(@user) bypass_sign_in(@user)
@@ -23,7 +24,19 @@ class Auth::ConfirmationsController < Devise::ConfirmationsController
@user = current_user @user = current_user
end end


def set_body_classes
@body_classes = 'lighter'
end

def user_params def user_params
params.require(:user).permit(:email) params.require(:user).permit(:email)
end 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 end

+ 5
- 0
app/controllers/auth/passwords_controller.rb View File

@@ -2,6 +2,7 @@


class Auth::PasswordsController < Devise::PasswordsController class Auth::PasswordsController < Devise::PasswordsController
before_action :check_validity_of_reset_password_token, only: :edit before_action :check_validity_of_reset_password_token, only: :edit
before_action :set_body_classes


layout 'auth' layout 'auth'


@@ -14,6 +15,10 @@ class Auth::PasswordsController < Devise::PasswordsController
end end
end end


def set_body_classes
@body_classes = 'lighter'
end

def reset_password_token_is_valid? def reset_password_token_is_valid?
resource_class.with_reset_password_token(params[:reset_password_token]).present? resource_class.with_reset_password_token(params[:reset_password_token]).present?
end end


+ 6
- 0
app/controllers/auth/registrations_controller.rb View File

@@ -8,6 +8,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController
before_action :configure_sign_up_params, only: [:create] before_action :configure_sign_up_params, only: [:create]
before_action :set_sessions, only: [:edit, :update] before_action :set_sessions, only: [:edit, :update]
before_action :set_instance_presenter, only: [:new, :create, :update] before_action :set_instance_presenter, only: [:new, :create, :update]
before_action :set_body_classes, only: [:new, :create, :edit, :update]


def destroy def destroy
not_found not_found
@@ -25,6 +26,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController


resource.locale = I18n.locale resource.locale = I18n.locale
resource.invite_code = params[:invite_code] if resource.invite_code.blank? resource.invite_code = params[:invite_code] if resource.invite_code.blank?
resource.agreement = true


resource.build_account if resource.account.nil? resource.build_account if resource.account.nil?
end end
@@ -79,6 +81,10 @@ class Auth::RegistrationsController < Devise::RegistrationsController
@instance_presenter = InstancePresenter.new @instance_presenter = InstancePresenter.new
end end


def set_body_classes
@body_classes = %w(edit update).include?(action_name) ? 'admin' : 'lighter'
end

def set_invite def set_invite
@invite = invite_code.present? ? Invite.find_by(code: invite_code) : nil @invite = invite_code.present? ? Invite.find_by(code: invite_code) : nil
end end


+ 12
- 1
app/controllers/auth/sessions_controller.rb View File

@@ -6,9 +6,10 @@ class Auth::SessionsController < Devise::SessionsController
layout 'auth' layout 'auth'


skip_before_action :require_no_authentication, only: [:create] skip_before_action :require_no_authentication, only: [:create]
skip_before_action :check_suspension, only: [:destroy]
skip_before_action :check_user_permissions, only: [:destroy]
prepend_before_action :authenticate_with_two_factor, if: :two_factor_enabled?, only: [:create] prepend_before_action :authenticate_with_two_factor, if: :two_factor_enabled?, only: [:create]
before_action :set_instance_presenter, only: [:new] before_action :set_instance_presenter, only: [:new]
before_action :set_body_classes


def new def new
Devise.omniauth_configs.each do |provider, config| Devise.omniauth_configs.each do |provider, config|
@@ -26,8 +27,10 @@ class Auth::SessionsController < Devise::SessionsController
end end


def destroy def destroy
tmp_stored_location = stored_location_for(:user)
super super
flash.delete(:notice) flash.delete(:notice)
store_location_for(:user, tmp_stored_location) if continue_after?
end end


protected protected
@@ -109,6 +112,10 @@ class Auth::SessionsController < Devise::SessionsController
@instance_presenter = InstancePresenter.new @instance_presenter = InstancePresenter.new
end end


def set_body_classes
@body_classes = 'lighter'
end

def home_paths(resource) def home_paths(resource)
paths = [about_path] paths = [about_path]
if single_user_mode? && resource.is_a?(User) if single_user_mode? && resource.is_a?(User)
@@ -116,4 +123,8 @@ class Auth::SessionsController < Devise::SessionsController
end end
paths paths
end end

def continue_after?
truthy_param?(:continue)
end
end end

+ 0
- 66
app/controllers/authorize_follows_controller.rb View File

@@ -1,66 +0,0 @@
# frozen_string_literal: true

class AuthorizeFollowsController < ApplicationController
layout 'modal'

before_action :authenticate_user!
before_action :set_body_classes

def show
@account = located_account || render(:error)
end

def create
@account = follow_attempt.try(:target_account)

if @account.nil?
render :error
else
render :success
end
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
render :error
end

private

def follow_attempt
FollowService.new.call(current_account, acct_without_prefix)
end

def located_account
if acct_param_is_url?
account_from_remote_fetch
else
account_from_remote_follow
end
end

def account_from_remote_fetch
FetchRemoteAccountService.new.call(acct_without_prefix)
end

def account_from_remote_follow
ResolveAccountService.new.call(acct_without_prefix)
end

def acct_param_is_url?
parsed_uri.path && %w(http https).include?(parsed_uri.scheme)
end

def parsed_uri
Addressable::URI.parse(acct_without_prefix).normalize
end

def acct_without_prefix
acct_params.gsub(/\Aacct:/, '')
end

def acct_params
params.fetch(:acct, '')
end

def set_body_classes
@body_classes = 'modal-layout'
end
end

+ 66
- 0
app/controllers/authorize_interactions_controller.rb View File

@@ -0,0 +1,66 @@
# frozen_string_literal: true

class AuthorizeInteractionsController < ApplicationController
include Authorization

layout 'modal'

before_action :authenticate_user!
before_action :set_body_classes
before_action :set_resource

def show
if @resource.is_a?(Account)
render :show
elsif @resource.is_a?(Status)
redirect_to web_url("statuses/#{@resource.id}")
else
render :error
end
end

def create
if @resource.is_a?(Account) && FollowService.new.call(current_account, @resource)
render :success
else
render :error
end
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
render :error
end

private

def set_resource
@resource = located_resource || render(:error)
authorize(@resource, :show?) if @resource.is_a?(Status)
end

def located_resource
if uri_param_is_url?
ResolveURLService.new.call(uri_param)
else
account_from_remote_follow
end
end

def account_from_remote_follow
ResolveAccountService.new.call(uri_param)
end

def uri_param_is_url?
parsed_uri.path && %w(http https).include?(parsed_uri.scheme)
end

def parsed_uri
Addressable::URI.parse(uri_param).normalize
end

def uri_param
params[:uri] || params.fetch(:acct, '').gsub(/\Aacct:/, '')
end

def set_body_classes
@body_classes = 'modal-layout'
end
end

+ 5
- 0
app/controllers/concerns/account_controller_concern.rb View File

@@ -8,6 +8,7 @@ module AccountControllerConcern
included do included do
layout 'public' layout 'public'
before_action :set_account before_action :set_account
before_action :set_instance_presenter
before_action :set_link_headers before_action :set_link_headers
before_action :check_account_suspension before_action :check_account_suspension
end end
@@ -18,6 +19,10 @@ module AccountControllerConcern
@account = Account.find_local!(params[:account_username]) @account = Account.find_local!(params[:account_username])
end end


def set_instance_presenter
@instance_presenter = InstancePresenter.new
end

def set_link_headers def set_link_headers
response.headers['Link'] = LinkHeader.new( response.headers['Link'] = LinkHeader.new(
[ [


+ 0
- 21
app/controllers/concerns/remote_account_controller_concern.rb View File

@@ -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

+ 47
- 18
app/controllers/concerns/signature_verification.rb View File

@@ -22,6 +22,12 @@ module SignatureVerification
return return
end end


if request.headers['Date'].present? && !matches_time_window?
@signature_verification_failure_reason = 'Signed request date outside acceptable time window'
@signed_request_account = nil
return
end

raw_signature = request.headers['Signature'] raw_signature = request.headers['Signature']
signature_params = {} signature_params = {}


@@ -37,7 +43,13 @@ module SignatureVerification
return return
end 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? if account.nil?
@signature_verification_failure_reason = "Public key not found for key #{signature_params['keyId']}" @signature_verification_failure_reason = "Public key not found for key #{signature_params['keyId']}"
@@ -48,23 +60,26 @@ module SignatureVerification
signature = Base64.decode64(signature_params['signature']) signature = Base64.decode64(signature_params['signature'])
compare_signed_string = build_signed_string(signature_params['headers']) 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 @signed_request_account = nil
return
end 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 end


def request_body def request_body
@@ -73,10 +88,19 @@ module SignatureVerification


private 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) def build_signed_string(signed_headers)
signed_headers = 'date' if signed_headers.blank? signed_headers = 'date' if signed_headers.blank?


signed_headers.split(' ').map do |signed_header|
signed_headers.downcase.split(' ').map do |signed_header|
if signed_header == Request::REQUEST_TARGET if signed_header == Request::REQUEST_TARGET
"#{Request::REQUEST_TARGET}: #{request.method.downcase} #{request.path}" "#{Request::REQUEST_TARGET}: #{request.method.downcase} #{request.path}"
elsif signed_header == 'digest' elsif signed_header == 'digest'
@@ -89,12 +113,12 @@ module SignatureVerification


def matches_time_window? def matches_time_window?
begin begin
time_sent = DateTime.httpdate(request.headers['Date'])
time_sent = Time.httpdate(request.headers['Date'])
rescue ArgumentError rescue ArgumentError
return false return false
end end


(Time.now.utc - time_sent).abs <= 30
(Time.now.utc - time_sent).abs <= 12.hours
end end


def body_digest def body_digest
@@ -119,4 +143,9 @@ module SignatureVerification
account account
end end
end end

def account_refresh_key(account)
return if account.local? || !account.activitypub?
ActivityPub::FetchRemoteAccountService.new.call(account.uri, only_key: true)
end
end end

+ 10
- 0
app/controllers/custom_css_controller.rb View File

@@ -0,0 +1,10 @@
# frozen_string_literal: true

class CustomCssController < ApplicationController
before_action :set_cache_headers

def show
skip_session!
render plain: Setting.custom_css || '', content_type: 'text/css'
end
end

+ 43
- 0
app/controllers/directories_controller.rb View 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).reject { |tag| tag.cached_sample_accounts.empty? }
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

+ 1
- 1
app/controllers/emojis_controller.rb View File

@@ -9,7 +9,7 @@ class EmojisController < ApplicationController
format.json do format.json do
skip_session! skip_session!


render_cached_json(['activitypub', 'emoji', @emoji.cache_key], content_type: 'application/activity+json') do
render_cached_json(['activitypub', 'emoji', @emoji], content_type: 'application/activity+json') do
ActiveModelSerializers::SerializableResource.new(@emoji, serializer: ActivityPub::EmojiSerializer, adapter: ActivityPub::Adapter) ActiveModelSerializers::SerializableResource.new(@emoji, serializer: ActivityPub::EmojiSerializer, adapter: ActivityPub::Adapter)
end end
end end


+ 6
- 1
app/controllers/filters_controller.rb View File

@@ -7,6 +7,7 @@ class FiltersController < ApplicationController


before_action :set_filters, only: :index before_action :set_filters, only: :index
before_action :set_filter, only: [:edit, :update, :destroy] before_action :set_filter, only: [:edit, :update, :destroy]
before_action :set_body_classes


def index def index
@filters = current_account.custom_filters @filters = current_account.custom_filters
@@ -52,6 +53,10 @@ class FiltersController < ApplicationController
end end


def resource_params def resource_params
params.require(:custom_filter).permit(:phrase, :expires_in, :irreversible, context: [])
params.require(:custom_filter).permit(:phrase, :expires_in, :irreversible, :whole_word, context: [])
end

def set_body_classes
@body_classes = 'admin'
end end
end end

+ 1
- 1
app/controllers/home_controller.rb View File

@@ -58,7 +58,7 @@ class HomeController < ApplicationController
if request.path.start_with?('/web') if request.path.start_with?('/web')
new_user_session_path new_user_session_path
elsif single_user_mode? elsif single_user_mode?
short_account_path(Account.first)
short_account_path(Account.local.where(suspended: false).first)
else else
about_path about_path
end end


+ 1
- 1
app/controllers/intents_controller.rb View File

@@ -8,7 +8,7 @@ class IntentsController < ApplicationController
if uri.scheme == 'web+mastodon' if uri.scheme == 'web+mastodon'
case uri.host case uri.host
when 'follow' when 'follow'
return redirect_to authorize_follow_path(acct: uri.query_values['uri'].gsub(/\Aacct:/, ''))
return redirect_to authorize_interaction_path(uri: uri.query_values['uri'].gsub(/\Aacct:/, ''))
when 'share' when 'share'
return redirect_to share_path(text: uri.query_values['text']) return redirect_to share_path(text: uri.query_values['text'])
end end


+ 6
- 1
app/controllers/invites_controller.rb View File

@@ -6,6 +6,7 @@ class InvitesController < ApplicationController
layout 'admin' layout 'admin'


before_action :authenticate_user! before_action :authenticate_user!
before_action :set_body_classes


def index def index
authorize :invite, :create? authorize :invite, :create?
@@ -38,10 +39,14 @@ class InvitesController < ApplicationController
private private


def invites def invites
Invite.where(user: current_user)
Invite.where(user: current_user).order(id: :desc)
end end


def resource_params def resource_params
params.require(:invite).permit(:max_uses, :expires_in, :autofollow) params.require(:invite).permit(:max_uses, :expires_in, :autofollow)
end end

def set_body_classes
@body_classes = 'admin'
end
end end

+ 5
- 0
app/controllers/media_controller.rb View File

@@ -6,12 +6,17 @@ class MediaController < ApplicationController
before_action :set_media_attachment before_action :set_media_attachment
before_action :verify_permitted_status! before_action :verify_permitted_status!


content_security_policy only: :player do |p|
p.frame_ancestors(false)
end

def show def show
redirect_to @media_attachment.file.url(:original) redirect_to @media_attachment.file.url(:original)
end end


def player def player
@body_classes = 'player' @body_classes = 'player'
response.headers['X-Frame-Options'] = 'ALLOWALL'
raise ActiveRecord::RecordNotFound unless @media_attachment.video? || @media_attachment.gifv? raise ActiveRecord::RecordNotFound unless @media_attachment.video? || @media_attachment.gifv?
end end




+ 14
- 0
app/controllers/oauth/authorizations_controller.rb View File

@@ -13,4 +13,18 @@ class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
def store_current_location def store_current_location
store_location_for(:user, request.url) store_location_for(:user, request.url)
end end

def render_success
if skip_authorization? || (matching_token? && !truthy_param?('force_login'))
redirect_or_render authorize_response
elsif Doorkeeper.configuration.api_only
render json: pre_auth
else
render :new
end
end

def truthy_param?(key)
ActiveModel::Type::Boolean.new.cast(params[key])
end
end end

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save