Merge branch 'master' of https://github.com/glitch-soc/mastodon
commit
10733881f3
|
@ -6,6 +6,7 @@ aliases:
|
|||
- image: circleci/ruby:2.7-buster-node
|
||||
environment: &ruby_environment
|
||||
BUNDLE_APP_CONFIG: ./.bundle/
|
||||
BUNDLE_PATH: ./vendor/bundle/
|
||||
DB_HOST: localhost
|
||||
DB_USER: root
|
||||
RAILS_ENV: test
|
||||
|
@ -138,9 +139,10 @@ jobs:
|
|||
docker:
|
||||
- image: circleci/ruby:2.7-buster-node
|
||||
environment: *ruby_environment
|
||||
- image: circleci/postgres:10.6-alpine
|
||||
- image: circleci/postgres:12.2
|
||||
environment:
|
||||
POSTGRES_USER: root
|
||||
POSTGRES_HOST_AUTH_METHOD: trust
|
||||
- image: circleci/redis:5-alpine
|
||||
steps:
|
||||
- *attach_workspace
|
||||
|
@ -157,9 +159,10 @@ jobs:
|
|||
docker:
|
||||
- image: circleci/ruby:2.7-buster-node
|
||||
environment: *ruby_environment
|
||||
- image: circleci/postgres:10.6-alpine
|
||||
- image: circleci/postgres:12.2
|
||||
environment:
|
||||
POSTGRES_USER: root
|
||||
POSTGRES_HOST_AUTH_METHOD: trust
|
||||
- image: circleci/redis:5-alpine
|
||||
<<: *test_steps
|
||||
|
||||
|
@ -168,9 +171,10 @@ jobs:
|
|||
docker:
|
||||
- image: circleci/ruby:2.6-buster-node
|
||||
environment: *ruby_environment
|
||||
- image: circleci/postgres:10.6-alpine
|
||||
- image: circleci/postgres:12.2
|
||||
environment:
|
||||
POSTGRES_USER: root
|
||||
POSTGRES_HOST_AUTH_METHOD: trust
|
||||
- image: circleci/redis:5-alpine
|
||||
<<: *test_steps
|
||||
|
||||
|
@ -179,9 +183,10 @@ jobs:
|
|||
docker:
|
||||
- image: circleci/ruby:2.5-buster-node
|
||||
environment: *ruby_environment
|
||||
- image: circleci/postgres:10.6-alpine
|
||||
- image: circleci/postgres:12.2
|
||||
environment:
|
||||
POSTGRES_USER: root
|
||||
POSTGRES_HOST_AUTH_METHOD: trust
|
||||
- image: circleci/redis:5-alpine
|
||||
<<: *test_steps
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
2.6.5
|
||||
2.6.6
|
||||
|
|
73
CHANGELOG.md
73
CHANGELOG.md
|
@ -3,6 +3,79 @@ Changelog
|
|||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [v3.1.3] - 2020-04-05
|
||||
### Added
|
||||
|
||||
- Add ability to filter audit log in admin UI ([Gargron](https://github.com/tootsuite/mastodon/pull/13381))
|
||||
- Add titles to warning presets in admin UI ([Gargron](https://github.com/tootsuite/mastodon/pull/13252))
|
||||
- Add option to include resolved DNS records when blacklisting e-mail domains in admin UI ([Gargron](https://github.com/tootsuite/mastodon/pull/13254))
|
||||
- Add ability to delete files uploaded for settings in admin UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13192))
|
||||
- Add sorting by username, creation and last activity in admin UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13076))
|
||||
- Add explanation as to why unlocked accounts may have follow requests in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13385))
|
||||
- Add link to bookmarks to dropdown in web UI ([mayaeh](https://github.com/tootsuite/mastodon/pull/13273))
|
||||
- Add support for links to statuses in announcements to be opened in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13212), [ThibG](https://github.com/tootsuite/mastodon/pull/13250))
|
||||
- Add tooltips to audio/video player buttons in web UI ([ariasuni](https://github.com/tootsuite/mastodon/pull/13203))
|
||||
- Add submit button to the top of preferences pages ([guigeekz](https://github.com/tootsuite/mastodon/pull/13068))
|
||||
- Add specific rate limits for posting, following and reporting ([Gargron](https://github.com/tootsuite/mastodon/pull/13172), [Gargron](https://github.com/tootsuite/mastodon/pull/13390))
|
||||
- 300 posts every 3 hours
|
||||
- 400 follows or follow requests every 24 hours
|
||||
- 400 reports every 24 hours
|
||||
- Add federation support for the "hide network" preference ([ThibG](https://github.com/tootsuite/mastodon/pull/11673))
|
||||
- Add `--skip-media-remove` option to `tootctl statuses remove` ([tateisu](https://github.com/tootsuite/mastodon/pull/13080))
|
||||
|
||||
### Changed
|
||||
|
||||
- **Change design of polls in web UI** ([Sasha-Sorokin](https://github.com/tootsuite/mastodon/pull/13257), [ThibG](https://github.com/tootsuite/mastodon/pull/13313))
|
||||
- Change status click areas in web UI to be bigger ([ariasuni](https://github.com/tootsuite/mastodon/pull/13327))
|
||||
- **Change `tootctl media remove-orphans` to work for all classes** ([Gargron](https://github.com/tootsuite/mastodon/pull/13316))
|
||||
- **Change local media attachments to perform heavy processing asynchronously** ([Gargron](https://github.com/tootsuite/mastodon/pull/13210))
|
||||
- Change video uploads to always be converted to H264/MP4 ([Gargron](https://github.com/tootsuite/mastodon/pull/13220), [ThibG](https://github.com/tootsuite/mastodon/pull/13239), [ThibG](https://github.com/tootsuite/mastodon/pull/13242))
|
||||
- Change video uploads to enforce certain limits ([Gargron](https://github.com/tootsuite/mastodon/pull/13218))
|
||||
- Dimensions smaller than 1920x1200px
|
||||
- Frame rate at most 60fps
|
||||
- Change the tooltip "Toggle visibility" to "Hide media" in web UI ([ariasuni](https://github.com/tootsuite/mastodon/pull/13199))
|
||||
- Change description of privacy levels to be more intuitive in web UI ([ariasuni](https://github.com/tootsuite/mastodon/pull/13197))
|
||||
- Change GIF label to be displayed even when autoplay is enabled in web UI ([koyuawsmbrtn](https://github.com/tootsuite/mastodon/pull/13209))
|
||||
- Change the string "Hide everything from …" to "Block domain …" in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13178), [mayaeh](https://github.com/tootsuite/mastodon/pull/13221))
|
||||
- Change wording of media display preferences to be more intuitive ([ariasuni](https://github.com/tootsuite/mastodon/pull/13198))
|
||||
|
||||
### Deprecated
|
||||
|
||||
- `POST /api/v1/media` → `POST /api/v2/media` ([Gargron](https://github.com/tootsuite/mastodon/pull/13210))
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix `tootctl media remove-orphans` ignoring `PAPERCLIP_ROOT_PATH` ([Gargron](https://github.com/tootsuite/mastodon/pull/13375))
|
||||
- Fix returning results when searching for URL with non-zero offset ([Gargron](https://github.com/tootsuite/mastodon/pull/13377))
|
||||
- Fix pinning a column in web UI sometimes redirecting out of web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/13376))
|
||||
- Fix background jobs not using locks like they are supposed to ([Gargron](https://github.com/tootsuite/mastodon/pull/13361))
|
||||
- Fix content warning being unnecessarily cleared when hiding content warning input in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13348))
|
||||
- Fix "Show more" not switching to "Show less" on public pages ([ThibG](https://github.com/tootsuite/mastodon/pull/13174))
|
||||
- Fix import overwrite option not being selectable ([noellabo](https://github.com/tootsuite/mastodon/pull/13347))
|
||||
- Fix wrong color for ellipsis in boost confirmation dialog in web UI ([ariasuni](https://github.com/tootsuite/mastodon/pull/13355))
|
||||
- Fix unnecessary unfollowing when importing follows with overwrite option ([noellabo](https://github.com/tootsuite/mastodon/pull/13350))
|
||||
- Fix 404 and 410 API errors being silently discarded in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13279))
|
||||
- Fix OCR not working on Safari because of unsupported worker-src CSP ([ThibG](https://github.com/tootsuite/mastodon/pull/13323))
|
||||
- Fix media not being marked sensitive when a content warning is set with no text ([ThibG](https://github.com/tootsuite/mastodon/pull/13277))
|
||||
- Fix crash after deleting announcements in web UI ([codesections](https://github.com/tootsuite/mastodon/pull/13283), [ThibG](https://github.com/tootsuite/mastodon/pull/13312))
|
||||
- Fix bookmarks not being searchable ([Kjwon15](https://github.com/tootsuite/mastodon/pull/13271), [noellabo](https://github.com/tootsuite/mastodon/pull/13293))
|
||||
- Fix reported accounts not being whitelisted from further spam checks when resolving a spam check report ([ThibG](https://github.com/tootsuite/mastodon/pull/13289))
|
||||
- Fix web UI crash in single-column mode on prehistoric browsers ([ThibG](https://github.com/tootsuite/mastodon/pull/13267))
|
||||
- Fix some timeouts when searching for URLs ([ThibG](https://github.com/tootsuite/mastodon/pull/13253))
|
||||
- Fix detailed view of direct messages displaying a 0 boost count in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13244))
|
||||
- Fix regression in “Edit media” modal in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13243))
|
||||
- Fix public posts from silenced accounts not being changed to unlisted visibility ([ThibG](https://github.com/tootsuite/mastodon/pull/13096))
|
||||
- Fix error when searching for URLs that contain the mention syntax ([ThibG](https://github.com/tootsuite/mastodon/pull/13151))
|
||||
- Fix text area above/right of emoji picker being accidentally clickable in web UI ([ariasuni](https://github.com/tootsuite/mastodon/pull/13148))
|
||||
- Fix too large announcements not being scrollable in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13211))
|
||||
- Fix `tootctl media remove-orphans` crashing when encountering invalid media ([ThibG](https://github.com/tootsuite/mastodon/pull/13170))
|
||||
- Fix installation failing when Redis password contains special characters ([ThibG](https://github.com/tootsuite/mastodon/pull/13156))
|
||||
- Fix announcements with fully-qualified mentions to local users crashing web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13164))
|
||||
|
||||
### Security
|
||||
|
||||
- Fix re-sending of e-mail confirmation not being rate limited ([Gargron](https://github.com/tootsuite/mastodon/pull/13360))
|
||||
|
||||
## [v3.1.2] - 2020-02-27
|
||||
### Added
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ FROM ubuntu:18.04 as build-dep
|
|||
SHELL ["bash", "-c"]
|
||||
|
||||
# Install Node v12 (LTS)
|
||||
ENV NODE_VER="12.14.0"
|
||||
ENV NODE_VER="12.16.1"
|
||||
RUN ARCH= && \
|
||||
dpkgArch="$(dpkg --print-architecture)" && \
|
||||
case "${dpkgArch##*-}" in \
|
||||
|
@ -38,8 +38,8 @@ RUN apt update && \
|
|||
make -j$(nproc) > /dev/null && \
|
||||
make install_bin install_include install_lib
|
||||
|
||||
# Install ruby
|
||||
ENV RUBY_VER="2.6.5"
|
||||
# Install Ruby
|
||||
ENV RUBY_VER="2.6.6"
|
||||
ENV CPPFLAGS="-I/opt/jemalloc/include"
|
||||
ENV LDFLAGS="-L/opt/jemalloc/lib/"
|
||||
RUN apt update && \
|
||||
|
|
32
Gemfile
32
Gemfile
|
@ -1,12 +1,12 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
source 'https://rubygems.org'
|
||||
ruby '>= 2.4.0', '< 3.0.0'
|
||||
ruby '>= 2.5.0', '< 3.0.0'
|
||||
|
||||
gem 'pkg-config', '~> 1.4'
|
||||
|
||||
gem 'puma', '~> 4.3'
|
||||
gem 'rails', '~> 5.2.4'
|
||||
gem 'rails', '~> 5.2.4.2'
|
||||
gem 'sprockets', '~> 3.7.2'
|
||||
gem 'thor', '~> 0.20'
|
||||
gem 'rack', '~> 2.2.2'
|
||||
|
@ -20,7 +20,7 @@ gem 'makara', '~> 0.4'
|
|||
gem 'pghero', '~> 2.4'
|
||||
gem 'dotenv-rails', '~> 2.7'
|
||||
|
||||
gem 'aws-sdk-s3', '~> 1.60', require: false
|
||||
gem 'aws-sdk-s3', '~> 1.61', require: false
|
||||
gem 'fog-core', '<= 2.1.0'
|
||||
gem 'fog-openstack', '~> 0.3', require: false
|
||||
gem 'paperclip', '~> 6.0'
|
||||
|
@ -35,7 +35,7 @@ gem 'browser'
|
|||
gem 'charlock_holmes', '~> 0.7.7'
|
||||
gem 'iso-639'
|
||||
gem 'chewy', '~> 5.1'
|
||||
gem 'cld3', '~> 3.2.6'
|
||||
gem 'cld3', '~> 3.3.0'
|
||||
gem 'devise', '~> 4.7'
|
||||
gem 'devise-two-factor', '~> 3.1'
|
||||
|
||||
|
@ -48,8 +48,8 @@ gem 'omniauth-cas', '~> 1.1'
|
|||
gem 'omniauth-saml', '~> 1.10'
|
||||
gem 'omniauth', '~> 1.9'
|
||||
|
||||
gem 'discard', '~> 1.1'
|
||||
gem 'doorkeeper', '~> 5.2'
|
||||
gem 'discard', '~> 1.2'
|
||||
gem 'doorkeeper', '~> 5.3'
|
||||
gem 'fast_blank', '~> 1.0'
|
||||
gem 'fastimage'
|
||||
gem 'goldfinger', '~> 2.1'
|
||||
|
@ -69,7 +69,7 @@ gem 'nilsimsa', git: 'https://github.com/witgo/nilsimsa', ref: 'fd184883048b922b
|
|||
gem 'nokogiri', '~> 1.10'
|
||||
gem 'nsa', '~> 0.2'
|
||||
gem 'oj', '~> 3.10'
|
||||
gem 'ox', '~> 2.12'
|
||||
gem 'ox', '~> 2.13'
|
||||
gem 'parslet'
|
||||
gem 'parallel', '~> 1.19'
|
||||
gem 'posix-spawn', git: 'https://github.com/rtomayko/posix-spawn', ref: '58465d2e213991f8afb13b984854a49fcdcc980c'
|
||||
|
@ -84,7 +84,7 @@ gem 'mario-redis-lock', '~> 1.2', require: 'redis_lock'
|
|||
gem 'rqrcode', '~> 1.1'
|
||||
gem 'ruby-progressbar', '~> 1.10'
|
||||
gem 'sanitize', '~> 5.1'
|
||||
gem 'sidekiq', '~> 5.2'
|
||||
gem 'sidekiq', '~> 6.0'
|
||||
gem 'sidekiq-scheduler', '~> 3.0'
|
||||
gem 'sidekiq-unique-jobs', '~> 6.0'
|
||||
gem 'sidekiq-bulk', '~>0.2.0'
|
||||
|
@ -92,9 +92,9 @@ gem 'simple-navigation', '~> 4.1'
|
|||
gem 'simple_form', '~> 5.0'
|
||||
gem 'sprockets-rails', '~> 3.2', require: 'sprockets/railtie'
|
||||
gem 'stoplight', '~> 2.2.0'
|
||||
gem 'strong_migrations', '~> 0.5'
|
||||
gem 'strong_migrations', '~> 0.6'
|
||||
gem 'tty-command', '~> 0.9', require: false
|
||||
gem 'tty-prompt', '~> 0.20', require: false
|
||||
gem 'tty-prompt', '~> 0.21', require: false
|
||||
gem 'twitter-text', '~> 1.14'
|
||||
gem 'tzinfo-data', '~> 1.2019'
|
||||
gem 'webpacker', '~> 4.2'
|
||||
|
@ -112,7 +112,7 @@ group :development, :test do
|
|||
gem 'i18n-tasks', '~> 0.9', require: false
|
||||
gem 'pry-byebug', '~> 3.8'
|
||||
gem 'pry-rails', '~> 0.3'
|
||||
gem 'rspec-rails', '~> 3.9'
|
||||
gem 'rspec-rails', '~> 4.0'
|
||||
end
|
||||
|
||||
group :production, :test do
|
||||
|
@ -122,19 +122,19 @@ end
|
|||
group :test do
|
||||
gem 'capybara', '~> 3.31'
|
||||
gem 'climate_control', '~> 0.2'
|
||||
gem 'faker', '~> 2.10'
|
||||
gem 'faker', '~> 2.11'
|
||||
gem 'microformats', '~> 4.2'
|
||||
gem 'rails-controller-testing', '~> 1.0'
|
||||
gem 'rspec-sidekiq', '~> 3.0'
|
||||
gem 'simplecov', '~> 0.18', require: false
|
||||
gem 'webmock', '~> 3.8'
|
||||
gem 'parallel_tests', '~> 2.30'
|
||||
gem 'parallel_tests', '~> 2.32'
|
||||
end
|
||||
|
||||
group :development do
|
||||
gem 'active_record_query_trace', '~> 1.7'
|
||||
gem 'annotate', '~> 3.0'
|
||||
gem 'better_errors', '~> 2.5'
|
||||
gem 'better_errors', '~> 2.6'
|
||||
gem 'binding_of_caller', '~> 0.7'
|
||||
gem 'bullet', '~> 6.1'
|
||||
gem 'letter_opener', '~> 1.7'
|
||||
|
@ -142,10 +142,10 @@ group :development do
|
|||
gem 'memory_profiler'
|
||||
gem 'rubocop', '~> 0.79', require: false
|
||||
gem 'rubocop-rails', '~> 2.4', require: false
|
||||
gem 'brakeman', '~> 4.7', require: false
|
||||
gem 'brakeman', '~> 4.8', require: false
|
||||
gem 'bundler-audit', '~> 0.6', require: false
|
||||
|
||||
gem 'capistrano', '~> 3.11'
|
||||
gem 'capistrano', '~> 3.12'
|
||||
gem 'capistrano-rails', '~> 1.4'
|
||||
gem 'capistrano-rbenv', '~> 2.1'
|
||||
gem 'capistrano-yarn', '~> 2.0'
|
||||
|
|
242
Gemfile.lock
242
Gemfile.lock
|
@ -31,25 +31,25 @@ GIT
|
|||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
actioncable (5.2.4.1)
|
||||
actionpack (= 5.2.4.1)
|
||||
actioncable (5.2.4.2)
|
||||
actionpack (= 5.2.4.2)
|
||||
nio4r (~> 2.0)
|
||||
websocket-driver (>= 0.6.1)
|
||||
actionmailer (5.2.4.1)
|
||||
actionpack (= 5.2.4.1)
|
||||
actionview (= 5.2.4.1)
|
||||
activejob (= 5.2.4.1)
|
||||
actionmailer (5.2.4.2)
|
||||
actionpack (= 5.2.4.2)
|
||||
actionview (= 5.2.4.2)
|
||||
activejob (= 5.2.4.2)
|
||||
mail (~> 2.5, >= 2.5.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
actionpack (5.2.4.1)
|
||||
actionview (= 5.2.4.1)
|
||||
activesupport (= 5.2.4.1)
|
||||
actionpack (5.2.4.2)
|
||||
actionview (= 5.2.4.2)
|
||||
activesupport (= 5.2.4.2)
|
||||
rack (~> 2.0, >= 2.0.8)
|
||||
rack-test (>= 0.6.3)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
||||
actionview (5.2.4.1)
|
||||
activesupport (= 5.2.4.1)
|
||||
actionview (5.2.4.2)
|
||||
activesupport (= 5.2.4.2)
|
||||
builder (~> 3.1)
|
||||
erubi (~> 1.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
|
@ -60,20 +60,20 @@ GEM
|
|||
case_transform (>= 0.2)
|
||||
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
|
||||
active_record_query_trace (1.7)
|
||||
activejob (5.2.4.1)
|
||||
activesupport (= 5.2.4.1)
|
||||
activejob (5.2.4.2)
|
||||
activesupport (= 5.2.4.2)
|
||||
globalid (>= 0.3.6)
|
||||
activemodel (5.2.4.1)
|
||||
activesupport (= 5.2.4.1)
|
||||
activerecord (5.2.4.1)
|
||||
activemodel (= 5.2.4.1)
|
||||
activesupport (= 5.2.4.1)
|
||||
activemodel (5.2.4.2)
|
||||
activesupport (= 5.2.4.2)
|
||||
activerecord (5.2.4.2)
|
||||
activemodel (= 5.2.4.2)
|
||||
activesupport (= 5.2.4.2)
|
||||
arel (>= 9.0)
|
||||
activestorage (5.2.4.1)
|
||||
actionpack (= 5.2.4.1)
|
||||
activerecord (= 5.2.4.1)
|
||||
activestorage (5.2.4.2)
|
||||
actionpack (= 5.2.4.2)
|
||||
activerecord (= 5.2.4.2)
|
||||
marcel (~> 0.3.1)
|
||||
activesupport (5.2.4.1)
|
||||
activesupport (5.2.4.2)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 0.7, < 2)
|
||||
minitest (~> 5.1)
|
||||
|
@ -92,23 +92,23 @@ GEM
|
|||
av (0.9.0)
|
||||
cocaine (~> 0.5.3)
|
||||
aws-eventstream (1.0.3)
|
||||
aws-partitions (1.261.0)
|
||||
aws-sdk-core (3.86.0)
|
||||
aws-partitions (1.286.0)
|
||||
aws-sdk-core (3.92.0)
|
||||
aws-eventstream (~> 1.0, >= 1.0.2)
|
||||
aws-partitions (~> 1, >= 1.239.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
jmespath (~> 1.0)
|
||||
aws-sdk-kms (1.27.0)
|
||||
aws-sdk-kms (1.30.0)
|
||||
aws-sdk-core (~> 3, >= 3.71.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sdk-s3 (1.60.1)
|
||||
aws-sdk-s3 (1.61.1)
|
||||
aws-sdk-core (~> 3, >= 3.83.0)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sigv4 (1.1.0)
|
||||
aws-sigv4 (1.1.1)
|
||||
aws-eventstream (~> 1.0, >= 1.0.2)
|
||||
bcrypt (3.1.12)
|
||||
better_errors (2.5.1)
|
||||
better_errors (2.6.0)
|
||||
coderay (>= 1.0.0)
|
||||
erubi (>= 1.0.0)
|
||||
rack (>= 0.9.0)
|
||||
|
@ -116,10 +116,10 @@ GEM
|
|||
debug_inspector (>= 0.0.1)
|
||||
blurhash (0.1.4)
|
||||
ffi (~> 1.10.0)
|
||||
bootsnap (1.4.5)
|
||||
bootsnap (1.4.6)
|
||||
msgpack (~> 1.0)
|
||||
brakeman (4.7.2)
|
||||
browser (3.0.3)
|
||||
brakeman (4.8.0)
|
||||
browser (4.0.0)
|
||||
builder (3.2.4)
|
||||
bullet (6.1.0)
|
||||
activesupport (>= 3.0.0)
|
||||
|
@ -128,7 +128,7 @@ GEM
|
|||
bundler (>= 1.2.0, < 3)
|
||||
thor (~> 0.18)
|
||||
byebug (11.1.1)
|
||||
capistrano (3.11.2)
|
||||
capistrano (3.12.1)
|
||||
airbrussh (>= 1.0.0)
|
||||
i18n
|
||||
rake (>= 10.0.0)
|
||||
|
@ -160,13 +160,13 @@ GEM
|
|||
elasticsearch (>= 2.0.0)
|
||||
elasticsearch-dsl
|
||||
chunky_png (1.3.11)
|
||||
cld3 (3.2.6)
|
||||
cld3 (3.3.0)
|
||||
ffi (>= 1.1.0, < 1.12.0)
|
||||
climate_control (0.2.0)
|
||||
cocaine (0.5.8)
|
||||
climate_control (>= 0.0.3, < 1.0)
|
||||
coderay (1.1.2)
|
||||
concurrent-ruby (1.1.5)
|
||||
concurrent-ruby (1.1.6)
|
||||
connection_pool (2.2.2)
|
||||
crack (0.4.3)
|
||||
safe_yaml (~> 1.0.0)
|
||||
|
@ -190,12 +190,12 @@ GEM
|
|||
devise (>= 4.0.0)
|
||||
rpam2 (~> 4.0)
|
||||
diff-lcs (1.3)
|
||||
discard (1.1.0)
|
||||
discard (1.2.0)
|
||||
activerecord (>= 4.2, < 7)
|
||||
docile (1.3.2)
|
||||
domain_name (0.5.20190701)
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
doorkeeper (5.2.3)
|
||||
doorkeeper (5.3.1)
|
||||
railties (>= 5)
|
||||
dotenv (2.7.5)
|
||||
dotenv-rails (2.7.5)
|
||||
|
@ -214,11 +214,11 @@ GEM
|
|||
encryptor (3.0.0)
|
||||
equatable (0.6.1)
|
||||
erubi (1.9.0)
|
||||
et-orbi (1.1.6)
|
||||
et-orbi (1.2.3)
|
||||
tzinfo
|
||||
excon (0.71.0)
|
||||
fabrication (2.21.0)
|
||||
faker (2.10.1)
|
||||
faker (2.11.0)
|
||||
i18n (>= 1.6, < 2)
|
||||
faraday (0.17.3)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
|
@ -241,8 +241,8 @@ GEM
|
|||
fog-json (>= 1.0)
|
||||
ipaddress (>= 0.8)
|
||||
formatador (0.2.5)
|
||||
fugit (1.1.6)
|
||||
et-orbi (~> 1.1, >= 1.1.6)
|
||||
fugit (1.3.3)
|
||||
et-orbi (~> 1.1, >= 1.1.8)
|
||||
raabro (~> 1.1)
|
||||
fuubar (2.5.0)
|
||||
rspec-core (~> 3.0)
|
||||
|
@ -265,8 +265,8 @@ GEM
|
|||
railties (>= 4.0.1)
|
||||
hamster (3.0.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
hashdiff (1.0.0)
|
||||
hashie (3.6.0)
|
||||
hashdiff (1.0.1)
|
||||
hashie (4.1.0)
|
||||
highline (2.0.3)
|
||||
hiredis (0.6.3)
|
||||
hkdf (0.3.0)
|
||||
|
@ -287,7 +287,7 @@ GEM
|
|||
rainbow (>= 2.0.0)
|
||||
i18n (1.8.2)
|
||||
concurrent-ruby (~> 1.0)
|
||||
i18n-tasks (0.9.30)
|
||||
i18n-tasks (0.9.31)
|
||||
activesupport (>= 4.0.2)
|
||||
ast (>= 2.1.0)
|
||||
erubi
|
||||
|
@ -299,19 +299,19 @@ GEM
|
|||
terminal-table (>= 1.5.1)
|
||||
idn-ruby (0.1.0)
|
||||
ipaddress (0.8.3)
|
||||
iso-639 (0.2.8)
|
||||
iso-639 (0.3.5)
|
||||
jaro_winkler (1.5.4)
|
||||
jmespath (1.4.0)
|
||||
json (2.3.0)
|
||||
json-canonicalization (0.2.0)
|
||||
json-ld (3.1.0)
|
||||
json-ld (3.1.2)
|
||||
htmlentities (~> 4.3)
|
||||
json-canonicalization (~> 0.1)
|
||||
json-canonicalization (~> 0.2)
|
||||
link_header (~> 0.0, >= 0.0.8)
|
||||
multi_json (~> 1.14)
|
||||
rack (~> 2.0)
|
||||
rdf (~> 3.1)
|
||||
json-ld-preloaded (3.1.0)
|
||||
json-ld-preloaded (3.1.2)
|
||||
json-ld (~> 3.1)
|
||||
rdf (~> 3.1)
|
||||
jsonapi-renderer (0.2.2)
|
||||
|
@ -361,11 +361,11 @@ GEM
|
|||
mime-types (3.3.1)
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2019.1009)
|
||||
mimemagic (0.3.3)
|
||||
mimemagic (0.3.4)
|
||||
mini_mime (1.0.2)
|
||||
mini_portile2 (2.4.0)
|
||||
minitest (5.14.0)
|
||||
msgpack (1.3.1)
|
||||
msgpack (1.3.3)
|
||||
multi_json (1.14.1)
|
||||
multipart-post (2.1.1)
|
||||
necromancer (0.5.1)
|
||||
|
@ -374,7 +374,7 @@ GEM
|
|||
net-ssh (>= 2.6.5, < 6.0.0)
|
||||
net-ssh (5.2.0)
|
||||
nio4r (2.5.2)
|
||||
nokogiri (1.10.8)
|
||||
nokogiri (1.10.9)
|
||||
mini_portile2 (~> 2.4.0)
|
||||
nokogumbo (2.0.1)
|
||||
nokogiri (~> 1.8, >= 1.8.4)
|
||||
|
@ -383,9 +383,9 @@ GEM
|
|||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
sidekiq (>= 3.5)
|
||||
statsd-ruby (~> 1.4, >= 1.4.0)
|
||||
oj (3.10.1)
|
||||
omniauth (1.9.0)
|
||||
hashie (>= 3.4.6, < 3.7.0)
|
||||
oj (3.10.5)
|
||||
omniauth (1.9.1)
|
||||
hashie (>= 3.4.6)
|
||||
rack (>= 1.6.2, < 3)
|
||||
omniauth-cas (1.1.1)
|
||||
addressable (~> 2.3)
|
||||
|
@ -395,7 +395,7 @@ GEM
|
|||
omniauth (~> 1.3, >= 1.3.2)
|
||||
ruby-saml (~> 1.7)
|
||||
orm_adapter (0.5.0)
|
||||
ox (2.12.1)
|
||||
ox (2.13.2)
|
||||
paperclip (6.0.0)
|
||||
activemodel (>= 4.2.0)
|
||||
activesupport (>= 4.2.0)
|
||||
|
@ -406,15 +406,15 @@ GEM
|
|||
av (~> 0.9.0)
|
||||
paperclip (>= 2.5.2)
|
||||
parallel (1.19.1)
|
||||
parallel_tests (2.30.1)
|
||||
parallel_tests (2.32.0)
|
||||
parallel
|
||||
parser (2.7.0.2)
|
||||
parser (2.7.0.5)
|
||||
ast (~> 2.4.0)
|
||||
parslet (1.8.2)
|
||||
pastel (0.7.3)
|
||||
equatable (~> 0.6)
|
||||
tty-color (~> 0.5)
|
||||
pg (1.2.2)
|
||||
pg (1.2.3)
|
||||
pghero (2.4.1)
|
||||
activerecord (>= 5)
|
||||
pkg-config (1.4.1)
|
||||
|
@ -445,24 +445,24 @@ GEM
|
|||
rack (>= 1.0, < 3)
|
||||
rack-cors (1.1.1)
|
||||
rack (>= 2.0.0)
|
||||
rack-protection (2.0.7)
|
||||
rack-protection (2.0.8.1)
|
||||
rack
|
||||
rack-proxy (0.6.5)
|
||||
rack
|
||||
rack-test (1.1.0)
|
||||
rack (>= 1.0, < 3)
|
||||
rails (5.2.4.1)
|
||||
actioncable (= 5.2.4.1)
|
||||
actionmailer (= 5.2.4.1)
|
||||
actionpack (= 5.2.4.1)
|
||||
actionview (= 5.2.4.1)
|
||||
activejob (= 5.2.4.1)
|
||||
activemodel (= 5.2.4.1)
|
||||
activerecord (= 5.2.4.1)
|
||||
activestorage (= 5.2.4.1)
|
||||
activesupport (= 5.2.4.1)
|
||||
rails (5.2.4.2)
|
||||
actioncable (= 5.2.4.2)
|
||||
actionmailer (= 5.2.4.2)
|
||||
actionpack (= 5.2.4.2)
|
||||
actionview (= 5.2.4.2)
|
||||
activejob (= 5.2.4.2)
|
||||
activemodel (= 5.2.4.2)
|
||||
activerecord (= 5.2.4.2)
|
||||
activestorage (= 5.2.4.2)
|
||||
activesupport (= 5.2.4.2)
|
||||
bundler (>= 1.3.0)
|
||||
railties (= 5.2.4.1)
|
||||
railties (= 5.2.4.2)
|
||||
sprockets-rails (>= 2.0.0)
|
||||
rails-controller-testing (1.0.4)
|
||||
actionpack (>= 5.0.1.x)
|
||||
|
@ -478,9 +478,9 @@ GEM
|
|||
railties (>= 5.0, < 6)
|
||||
rails-settings-cached (0.6.6)
|
||||
rails (>= 4.2.0)
|
||||
railties (5.2.4.1)
|
||||
actionpack (= 5.2.4.1)
|
||||
activesupport (= 5.2.4.1)
|
||||
railties (5.2.4.2)
|
||||
actionpack (= 5.2.4.2)
|
||||
activesupport (= 5.2.4.2)
|
||||
method_source
|
||||
rake (>= 0.8.7)
|
||||
thor (>= 0.19.0, < 2.0)
|
||||
|
@ -523,26 +523,26 @@ GEM
|
|||
chunky_png (~> 1.0)
|
||||
rqrcode_core (~> 0.1)
|
||||
rqrcode_core (0.1.1)
|
||||
rspec-core (3.9.0)
|
||||
rspec-support (~> 3.9.0)
|
||||
rspec-expectations (3.9.0)
|
||||
rspec-core (3.9.1)
|
||||
rspec-support (~> 3.9.1)
|
||||
rspec-expectations (3.9.1)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.9.0)
|
||||
rspec-mocks (3.9.0)
|
||||
rspec-mocks (3.9.1)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.9.0)
|
||||
rspec-rails (3.9.0)
|
||||
actionpack (>= 3.0)
|
||||
activesupport (>= 3.0)
|
||||
railties (>= 3.0)
|
||||
rspec-core (~> 3.9.0)
|
||||
rspec-expectations (~> 3.9.0)
|
||||
rspec-mocks (~> 3.9.0)
|
||||
rspec-support (~> 3.9.0)
|
||||
rspec-rails (4.0.0)
|
||||
actionpack (>= 4.2)
|
||||
activesupport (>= 4.2)
|
||||
railties (>= 4.2)
|
||||
rspec-core (~> 3.9)
|
||||
rspec-expectations (~> 3.9)
|
||||
rspec-mocks (~> 3.9)
|
||||
rspec-support (~> 3.9)
|
||||
rspec-sidekiq (3.0.3)
|
||||
rspec-core (~> 3.0, >= 3.0.0)
|
||||
sidekiq (>= 2.4.0)
|
||||
rspec-support (3.9.0)
|
||||
rspec-support (3.9.2)
|
||||
rubocop (0.79.0)
|
||||
jaro_winkler (~> 1.5.1)
|
||||
parallel (~> 1.10)
|
||||
|
@ -556,38 +556,40 @@ GEM
|
|||
ruby-progressbar (1.10.1)
|
||||
ruby-saml (1.9.0)
|
||||
nokogiri (>= 1.5.10)
|
||||
rufus-scheduler (3.5.2)
|
||||
fugit (~> 1.1, >= 1.1.5)
|
||||
rufus-scheduler (3.6.0)
|
||||
fugit (~> 1.1, >= 1.1.6)
|
||||
safe_yaml (1.0.5)
|
||||
sanitize (5.1.0)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.8.0)
|
||||
nokogumbo (~> 2.0)
|
||||
sidekiq (5.2.7)
|
||||
connection_pool (~> 2.2, >= 2.2.2)
|
||||
rack (>= 1.5.0)
|
||||
rack-protection (>= 1.5.0)
|
||||
redis (>= 3.3.5, < 5)
|
||||
sidekiq (6.0.4)
|
||||
connection_pool (>= 2.2.2)
|
||||
rack (>= 2.0.0)
|
||||
rack-protection (>= 2.0.0)
|
||||
redis (>= 4.1.0)
|
||||
sidekiq-bulk (0.2.0)
|
||||
sidekiq
|
||||
sidekiq-scheduler (3.0.0)
|
||||
sidekiq-scheduler (3.0.1)
|
||||
e2mmap
|
||||
redis (>= 3, < 5)
|
||||
rufus-scheduler (~> 3.2)
|
||||
sidekiq (>= 3)
|
||||
thwait
|
||||
tilt (>= 1.4.0)
|
||||
sidekiq-unique-jobs (6.0.18)
|
||||
sidekiq-unique-jobs (6.0.21)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.5)
|
||||
sidekiq (>= 4.0, < 7.0)
|
||||
thor (~> 0)
|
||||
simple-navigation (4.1.0)
|
||||
activesupport (>= 2.3.2)
|
||||
simple_form (5.0.1)
|
||||
simple_form (5.0.2)
|
||||
actionpack (>= 5.0)
|
||||
activemodel (>= 5.0)
|
||||
simplecov (0.18.2)
|
||||
simplecov (0.18.5)
|
||||
docile (~> 1.1)
|
||||
simplecov-html (~> 0.11)
|
||||
simplecov-html (0.12.0)
|
||||
simplecov-html (0.12.2)
|
||||
sprockets (3.7.2)
|
||||
concurrent-ruby (~> 1.0)
|
||||
rack (> 1, < 3)
|
||||
|
@ -595,7 +597,7 @@ GEM
|
|||
actionpack (>= 4.0)
|
||||
activesupport (>= 4.0)
|
||||
sprockets (>= 3.0.0)
|
||||
sshkit (1.20.0)
|
||||
sshkit (1.21.0)
|
||||
net-scp (>= 1.1.2)
|
||||
net-ssh (>= 2.8.0)
|
||||
stackprof (0.2.15)
|
||||
|
@ -603,7 +605,7 @@ GEM
|
|||
stoplight (2.2.0)
|
||||
streamio-ffmpeg (3.0.2)
|
||||
multi_json (~> 1.8)
|
||||
strong_migrations (0.5.1)
|
||||
strong_migrations (0.6.2)
|
||||
activerecord (>= 5)
|
||||
temple (0.8.2)
|
||||
terminal-table (1.8.0)
|
||||
|
@ -614,11 +616,11 @@ GEM
|
|||
thread_safe (0.3.6)
|
||||
thwait (0.1.0)
|
||||
tilt (2.0.10)
|
||||
tty-color (0.5.0)
|
||||
tty-color (0.5.1)
|
||||
tty-command (0.9.0)
|
||||
pastel (~> 0.7.0)
|
||||
tty-cursor (0.7.0)
|
||||
tty-prompt (0.20.0)
|
||||
tty-cursor (0.7.1)
|
||||
tty-prompt (0.21.0)
|
||||
necromancer (~> 0.5.0)
|
||||
pastel (~> 0.7.0)
|
||||
tty-reader (~> 0.7.0)
|
||||
|
@ -626,10 +628,10 @@ GEM
|
|||
tty-cursor (~> 0.7)
|
||||
tty-screen (~> 0.7)
|
||||
wisper (~> 2.0.0)
|
||||
tty-screen (0.7.0)
|
||||
tty-screen (0.7.1)
|
||||
twitter-text (1.14.7)
|
||||
unf (~> 0.1.0)
|
||||
tzinfo (1.2.6)
|
||||
tzinfo (1.2.7)
|
||||
thread_safe (~> 0.1)
|
||||
tzinfo-data (1.2019.3)
|
||||
tzinfo (>= 1.0.0)
|
||||
|
@ -640,7 +642,7 @@ GEM
|
|||
uniform_notifier (1.13.0)
|
||||
warden (1.2.8)
|
||||
rack (>= 2.0.6)
|
||||
webmock (3.8.0)
|
||||
webmock (3.8.3)
|
||||
addressable (>= 2.3.6)
|
||||
crack (>= 0.3.2)
|
||||
hashdiff (>= 0.4.0, < 2.0.0)
|
||||
|
@ -666,35 +668,35 @@ DEPENDENCIES
|
|||
active_record_query_trace (~> 1.7)
|
||||
addressable (~> 2.7)
|
||||
annotate (~> 3.0)
|
||||
aws-sdk-s3 (~> 1.60)
|
||||
better_errors (~> 2.5)
|
||||
aws-sdk-s3 (~> 1.61)
|
||||
better_errors (~> 2.6)
|
||||
binding_of_caller (~> 0.7)
|
||||
blurhash (~> 0.1)
|
||||
bootsnap (~> 1.4)
|
||||
brakeman (~> 4.7)
|
||||
brakeman (~> 4.8)
|
||||
browser
|
||||
bullet (~> 6.1)
|
||||
bundler-audit (~> 0.6)
|
||||
capistrano (~> 3.11)
|
||||
capistrano (~> 3.12)
|
||||
capistrano-rails (~> 1.4)
|
||||
capistrano-rbenv (~> 2.1)
|
||||
capistrano-yarn (~> 2.0)
|
||||
capybara (~> 3.31)
|
||||
charlock_holmes (~> 0.7.7)
|
||||
chewy (~> 5.1)
|
||||
cld3 (~> 3.2.6)
|
||||
cld3 (~> 3.3.0)
|
||||
climate_control (~> 0.2)
|
||||
concurrent-ruby
|
||||
connection_pool
|
||||
devise (~> 4.7)
|
||||
devise-two-factor (~> 3.1)
|
||||
devise_pam_authenticatable2 (~> 9.2)
|
||||
discard (~> 1.1)
|
||||
doorkeeper (~> 5.2)
|
||||
discard (~> 1.2)
|
||||
doorkeeper (~> 5.3)
|
||||
dotenv-rails (~> 2.7)
|
||||
e2mmap (~> 0.1.0)
|
||||
fabrication (~> 2.21)
|
||||
faker (~> 2.10)
|
||||
faker (~> 2.11)
|
||||
fast_blank (~> 1.0)
|
||||
fastimage
|
||||
fog-core (<= 2.1.0)
|
||||
|
@ -732,11 +734,11 @@ DEPENDENCIES
|
|||
omniauth (~> 1.9)
|
||||
omniauth-cas (~> 1.1)
|
||||
omniauth-saml (~> 1.10)
|
||||
ox (~> 2.12)
|
||||
ox (~> 2.13)
|
||||
paperclip (~> 6.0)
|
||||
paperclip-av-transcoder (~> 0.6)
|
||||
parallel (~> 1.19)
|
||||
parallel_tests (~> 2.30)
|
||||
parallel_tests (~> 2.32)
|
||||
parslet
|
||||
pg (~> 1.2)
|
||||
pghero (~> 2.4)
|
||||
|
@ -751,7 +753,7 @@ DEPENDENCIES
|
|||
rack (~> 2.2.2)
|
||||
rack-attack (~> 6.2)
|
||||
rack-cors (~> 1.1)
|
||||
rails (~> 5.2.4)
|
||||
rails (~> 5.2.4.2)
|
||||
rails-controller-testing (~> 1.0)
|
||||
rails-i18n (~> 5.1)
|
||||
rails-settings-cached (~> 0.6)
|
||||
|
@ -761,13 +763,13 @@ DEPENDENCIES
|
|||
redis-namespace (~> 1.7)
|
||||
redis-rails (~> 5.0)
|
||||
rqrcode (~> 1.1)
|
||||
rspec-rails (~> 3.9)
|
||||
rspec-rails (~> 4.0)
|
||||
rspec-sidekiq (~> 3.0)
|
||||
rubocop (~> 0.79)
|
||||
rubocop-rails (~> 2.4)
|
||||
ruby-progressbar (~> 1.10)
|
||||
sanitize (~> 5.1)
|
||||
sidekiq (~> 5.2)
|
||||
sidekiq (~> 6.0)
|
||||
sidekiq-bulk (~> 0.2.0)
|
||||
sidekiq-scheduler (~> 3.0)
|
||||
sidekiq-unique-jobs (~> 6.0)
|
||||
|
@ -779,11 +781,11 @@ DEPENDENCIES
|
|||
stackprof
|
||||
stoplight (~> 2.2.0)
|
||||
streamio-ffmpeg (~> 3.0)
|
||||
strong_migrations (~> 0.5)
|
||||
strong_migrations (~> 0.6)
|
||||
thor (~> 0.20)
|
||||
thwait (~> 0.1.0)
|
||||
tty-command (~> 0.9)
|
||||
tty-prompt (~> 0.20)
|
||||
tty-prompt (~> 0.21)
|
||||
twitter-text (~> 1.14)
|
||||
tzinfo-data (~> 1.2019)
|
||||
webmock (~> 3.8)
|
||||
|
|
|
@ -91,7 +91,7 @@ VAGRANTFILE_API_VERSION = "2"
|
|||
|
||||
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
||||
|
||||
config.vm.box = "ubuntu/xenial64"
|
||||
config.vm.box = "ubuntu/bionic64"
|
||||
|
||||
config.vm.provider :virtualbox do |vb|
|
||||
vb.name = "mastodon"
|
||||
|
|
|
@ -47,6 +47,11 @@ class StatusesIndex < Chewy::Index
|
|||
data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) }
|
||||
end
|
||||
|
||||
crutch :bookmarks do |collection|
|
||||
data = ::Bookmark.where(status_id: collection.map(&:id)).where(account: Account.local).pluck(:status_id, :account_id)
|
||||
data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) }
|
||||
end
|
||||
|
||||
root date_detection: false do
|
||||
field :id, type: 'long'
|
||||
field :account_id, type: 'long'
|
||||
|
|
|
@ -6,7 +6,7 @@ class AccountFollowController < ApplicationController
|
|||
before_action :authenticate_user!
|
||||
|
||||
def create
|
||||
FollowService.new.call(current_user.account, @account.acct)
|
||||
FollowService.new.call(current_user.account, @account, with_rate_limit: true)
|
||||
redirect_to account_path(@account)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,8 +2,18 @@
|
|||
|
||||
module Admin
|
||||
class ActionLogsController < BaseController
|
||||
def index
|
||||
@action_logs = Admin::ActionLog.page(params[:page])
|
||||
before_action :set_action_logs
|
||||
|
||||
def index; end
|
||||
|
||||
private
|
||||
|
||||
def set_action_logs
|
||||
@action_logs = Admin::ActionLogFilter.new(filter_params).results.page(params[:page])
|
||||
end
|
||||
|
||||
def filter_params
|
||||
params.slice(:page, *Admin::ActionLogFilter::KEYS).permit(:page, *Admin::ActionLogFilter::KEYS)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,12 +6,12 @@ module Admin
|
|||
|
||||
def index
|
||||
authorize :email_domain_block, :index?
|
||||
@email_domain_blocks = EmailDomainBlock.page(params[:page])
|
||||
@email_domain_blocks = EmailDomainBlock.where(parent_id: nil).includes(:children).order(id: :desc).page(params[:page])
|
||||
end
|
||||
|
||||
def new
|
||||
authorize :email_domain_block, :create?
|
||||
@email_domain_block = EmailDomainBlock.new
|
||||
@email_domain_block = EmailDomainBlock.new(domain: params[:_domain])
|
||||
end
|
||||
|
||||
def create
|
||||
|
@ -21,6 +21,28 @@ module Admin
|
|||
|
||||
if @email_domain_block.save
|
||||
log_action :create, @email_domain_block
|
||||
|
||||
if @email_domain_block.with_dns_records?
|
||||
hostnames = []
|
||||
ips = []
|
||||
|
||||
Resolv::DNS.open do |dns|
|
||||
dns.timeouts = 1
|
||||
|
||||
hostnames = dns.getresources(@email_domain_block.domain, Resolv::DNS::Resource::IN::MX).to_a.map { |e| e.exchange.to_s }
|
||||
|
||||
([@email_domain_block.domain] + hostnames).uniq.each do |hostname|
|
||||
ips.concat(dns.getresources(hostname, Resolv::DNS::Resource::IN::A).to_a.map { |e| e.address.to_s })
|
||||
ips.concat(dns.getresources(hostname, Resolv::DNS::Resource::IN::AAAA).to_a.map { |e| e.address.to_s })
|
||||
end
|
||||
end
|
||||
|
||||
(hostnames + ips).each do |hostname|
|
||||
another_email_domain_block = EmailDomainBlock.new(domain: hostname, parent: @email_domain_block)
|
||||
log_action :create, another_email_domain_block if another_email_domain_block.save
|
||||
end
|
||||
end
|
||||
|
||||
redirect_to admin_email_domain_blocks_path, notice: I18n.t('admin.email_domain_blocks.created_msg')
|
||||
else
|
||||
render :new
|
||||
|
@ -41,7 +63,7 @@ module Admin
|
|||
end
|
||||
|
||||
def resource_params
|
||||
params.require(:email_domain_block).permit(:domain)
|
||||
params.require(:email_domain_block).permit(:domain, :with_dns_records)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Admin
|
||||
class SiteUploadsController < BaseController
|
||||
before_action :set_site_upload
|
||||
|
||||
def destroy
|
||||
authorize :settings, :destroy?
|
||||
|
||||
@site_upload.destroy!
|
||||
|
||||
redirect_to edit_admin_settings_path, notice: I18n.t('admin.site_uploads.destroyed_msg')
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_site_upload
|
||||
@site_upload = SiteUpload.find(params[:id])
|
||||
end
|
||||
end
|
||||
end
|
|
@ -7,7 +7,7 @@ module Admin
|
|||
def index
|
||||
authorize :account_warning_preset, :index?
|
||||
|
||||
@warning_presets = AccountWarningPreset.all
|
||||
@warning_presets = AccountWarningPreset.alphabetic
|
||||
@warning_preset = AccountWarningPreset.new
|
||||
end
|
||||
|
||||
|
@ -19,7 +19,7 @@ module Admin
|
|||
if @warning_preset.save
|
||||
redirect_to admin_warning_presets_path
|
||||
else
|
||||
@warning_presets = AccountWarningPreset.all
|
||||
@warning_presets = AccountWarningPreset.alphabetic
|
||||
render :index
|
||||
end
|
||||
end
|
||||
|
@ -52,7 +52,7 @@ module Admin
|
|||
end
|
||||
|
||||
def warning_preset_params
|
||||
params.require(:account_warning_preset).permit(:text)
|
||||
params.require(:account_warning_preset).permit(:title, :text)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -44,6 +44,10 @@ class Api::BaseController < ApplicationController
|
|||
render json: { error: 'There was a temporary problem serving your request, please try again' }, status: 503
|
||||
end
|
||||
|
||||
rescue_from Mastodon::RateLimitExceededError do
|
||||
render json: { error: I18n.t('errors.429') }, status: 429
|
||||
end
|
||||
|
||||
rescue_from ActionController::ParameterMissing do |e|
|
||||
render json: { error: e.to_s }, status: 400
|
||||
end
|
||||
|
|
|
@ -5,8 +5,6 @@ class Api::V1::Accounts::FollowerAccountsController < Api::BaseController
|
|||
before_action :set_account
|
||||
after_action :insert_pagination_headers
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
@accounts = load_accounts
|
||||
render json: @accounts, each_serializer: REST::AccountSerializer
|
||||
|
@ -27,7 +25,7 @@ class Api::V1::Accounts::FollowerAccountsController < Api::BaseController
|
|||
end
|
||||
|
||||
def hide_results?
|
||||
(@account.user_hides_network? && current_account&.id != @account.id) || (current_account && @account.blocking?(current_account))
|
||||
(@account.hides_followers? && current_account&.id != @account.id) || (current_account && @account.blocking?(current_account))
|
||||
end
|
||||
|
||||
def default_accounts
|
||||
|
|
|
@ -5,8 +5,6 @@ class Api::V1::Accounts::FollowingAccountsController < Api::BaseController
|
|||
before_action :set_account
|
||||
after_action :insert_pagination_headers
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
@accounts = load_accounts
|
||||
render json: @accounts, each_serializer: REST::AccountSerializer
|
||||
|
@ -27,7 +25,7 @@ class Api::V1::Accounts::FollowingAccountsController < Api::BaseController
|
|||
end
|
||||
|
||||
def hide_results?
|
||||
(@account.user_hides_network? && current_account&.id != @account.id) || (current_account && @account.blocking?(current_account))
|
||||
(@account.hides_following? && current_account&.id != @account.id) || (current_account && @account.blocking?(current_account))
|
||||
end
|
||||
|
||||
def default_accounts
|
||||
|
|
|
@ -4,8 +4,6 @@ class Api::V1::Accounts::IdentityProofsController < Api::BaseController
|
|||
before_action :require_user!
|
||||
before_action :set_account
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
@proofs = @account.identity_proofs.active
|
||||
render json: @proofs, each_serializer: REST::IdentityProofSerializer
|
||||
|
|
|
@ -5,8 +5,6 @@ class Api::V1::Accounts::ListsController < Api::BaseController
|
|||
before_action :require_user!
|
||||
before_action :set_account
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
@lists = @account.lists.where(account: current_account)
|
||||
render json: @lists, each_serializer: REST::ListSerializer
|
||||
|
|
|
@ -7,8 +7,6 @@ class Api::V1::Accounts::PinsController < Api::BaseController
|
|||
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
|
||||
|
|
|
@ -4,8 +4,6 @@ class Api::V1::Accounts::RelationshipsController < Api::BaseController
|
|||
before_action -> { doorkeeper_authorize! :read, :'read:follows' }
|
||||
before_action :require_user!
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
accounts = Account.where(id: account_ids).select('id')
|
||||
# .where doesn't guarantee that our results are in the same order
|
||||
|
|
|
@ -4,8 +4,6 @@ class Api::V1::Accounts::SearchController < Api::BaseController
|
|||
before_action -> { doorkeeper_authorize! :read, :'read:accounts' }
|
||||
before_action :require_user!
|
||||
|
||||
respond_to :json
|
||||
|
||||
def show
|
||||
@accounts = account_search
|
||||
render json: @accounts, each_serializer: REST::AccountSerializer
|
||||
|
|
|
@ -6,8 +6,6 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
|
|||
|
||||
after_action :insert_pagination_headers, unless: -> { truthy_param?(:pinned) }
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
@statuses = load_statuses
|
||||
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
|
||||
|
|
|
@ -14,7 +14,7 @@ class Api::V1::AccountsController < Api::BaseController
|
|||
|
||||
skip_before_action :require_authenticated_user!, only: :create
|
||||
|
||||
respond_to :json
|
||||
override_rate_limit_headers :follow, family: :follows
|
||||
|
||||
def show
|
||||
render json: @account, serializer: REST::AccountSerializer
|
||||
|
@ -31,7 +31,7 @@ class Api::V1::AccountsController < Api::BaseController
|
|||
end
|
||||
|
||||
def follow
|
||||
FollowService.new.call(current_user.account, @account, reblogs: truthy_param?(:reblogs))
|
||||
FollowService.new.call(current_user.account, @account, reblogs: truthy_param?(:reblogs), with_rate_limit: true)
|
||||
|
||||
options = @account.locked? || current_user.account.silenced? ? {} : { following_map: { @account.id => { reblogs: truthy_param?(:reblogs) } }, requested_map: { @account.id => false } }
|
||||
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
class Api::V1::Apps::CredentialsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read }
|
||||
|
||||
respond_to :json
|
||||
|
||||
def show
|
||||
render json: doorkeeper_token.application, serializer: REST::ApplicationSerializer, fields: %i(name website vapid_key)
|
||||
end
|
||||
|
|
|
@ -5,8 +5,6 @@ class Api::V1::BlocksController < Api::BaseController
|
|||
before_action :require_user!
|
||||
after_action :insert_pagination_headers
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
@accounts = load_accounts
|
||||
render json: @accounts, each_serializer: REST::AccountSerializer
|
||||
|
|
|
@ -5,8 +5,6 @@ class Api::V1::BookmarksController < Api::BaseController
|
|||
before_action :require_user!
|
||||
after_action :insert_pagination_headers
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
@statuses = load_statuses
|
||||
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
|
||||
|
|
|
@ -9,8 +9,6 @@ class Api::V1::ConversationsController < Api::BaseController
|
|||
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
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::CustomEmojisController < Api::BaseController
|
||||
respond_to :json
|
||||
|
||||
skip_before_action :set_cache_headers
|
||||
|
||||
def index
|
||||
|
|
|
@ -8,8 +8,6 @@ class Api::V1::DomainBlocksController < Api::BaseController
|
|||
before_action :require_user!
|
||||
after_action :insert_pagination_headers, only: :show
|
||||
|
||||
respond_to :json
|
||||
|
||||
def show
|
||||
@blocks = load_domain_blocks
|
||||
render json: @blocks.map(&:domain)
|
||||
|
|
|
@ -5,8 +5,6 @@ class Api::V1::EndorsementsController < Api::BaseController
|
|||
before_action :require_user!
|
||||
after_action :insert_pagination_headers
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
@accounts = load_accounts
|
||||
render json: @accounts, each_serializer: REST::AccountSerializer
|
||||
|
|
|
@ -5,8 +5,6 @@ class Api::V1::FavouritesController < Api::BaseController
|
|||
before_action :require_user!
|
||||
after_action :insert_pagination_headers
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
@statuses = load_statuses
|
||||
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
|
||||
|
|
|
@ -2,12 +2,9 @@
|
|||
|
||||
class Api::V1::FeaturedTags::SuggestionsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:accounts' }, only: :index
|
||||
|
||||
before_action :require_user!
|
||||
before_action :set_most_used_tags, only: :index
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
render json: @most_used_tags, each_serializer: REST::TagSerializer
|
||||
end
|
||||
|
|
|
@ -7,8 +7,6 @@ class Api::V1::FiltersController < Api::BaseController
|
|||
before_action :set_filters, only: :index
|
||||
before_action :set_filter, only: [:show, :update, :destroy]
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
render json: @filters, each_serializer: REST::FilterSerializer
|
||||
end
|
||||
|
|
|
@ -6,8 +6,6 @@ class Api::V1::Instances::ActivityController < Api::BaseController
|
|||
skip_before_action :set_cache_headers
|
||||
skip_before_action :require_authenticated_user!, unless: :whitelist_mode?
|
||||
|
||||
respond_to :json
|
||||
|
||||
def show
|
||||
expires_in 1.day, public: true
|
||||
render_with_cache json: :activity, expires_in: 1.day
|
||||
|
|
|
@ -6,8 +6,6 @@ class Api::V1::Instances::PeersController < Api::BaseController
|
|||
skip_before_action :set_cache_headers
|
||||
skip_before_action :require_authenticated_user!, unless: :whitelist_mode?
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
expires_in 1.day, public: true
|
||||
render_with_cache(expires_in: 1.day) { Account.remote.domains }
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::InstancesController < Api::BaseController
|
||||
respond_to :json
|
||||
|
||||
skip_before_action :set_cache_headers
|
||||
skip_before_action :require_authenticated_user!, unless: :whitelist_mode?
|
||||
|
||||
|
|
|
@ -3,27 +3,42 @@
|
|||
class Api::V1::MediaController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:media' }
|
||||
before_action :require_user!
|
||||
|
||||
respond_to :json
|
||||
before_action :set_media_attachment, except: [:create]
|
||||
before_action :check_processing, except: [:create]
|
||||
|
||||
def create
|
||||
@media = current_account.media_attachments.create!(media_params)
|
||||
render json: @media, serializer: REST::MediaAttachmentSerializer
|
||||
@media_attachment = current_account.media_attachments.create!(media_attachment_params)
|
||||
render json: @media_attachment, serializer: REST::MediaAttachmentSerializer
|
||||
rescue Paperclip::Errors::NotIdentifiedByImageMagickError
|
||||
render json: file_type_error, status: 422
|
||||
rescue Paperclip::Error
|
||||
render json: processing_error, status: 500
|
||||
end
|
||||
|
||||
def show
|
||||
render json: @media_attachment, serializer: REST::MediaAttachmentSerializer, status: status_code_for_media_attachment
|
||||
end
|
||||
|
||||
def update
|
||||
@media = current_account.media_attachments.where(status_id: nil).find(params[:id])
|
||||
@media.update!(media_params)
|
||||
render json: @media, serializer: REST::MediaAttachmentSerializer
|
||||
@media_attachment.update!(media_attachment_params)
|
||||
render json: @media_attachment, serializer: REST::MediaAttachmentSerializer, status: status_code_for_media_attachment
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def media_params
|
||||
def status_code_for_media_attachment
|
||||
@media_attachment.not_processed? ? 206 : 200
|
||||
end
|
||||
|
||||
def set_media_attachment
|
||||
@media_attachment = current_account.media_attachments.unattached.find(params[:id])
|
||||
end
|
||||
|
||||
def check_processing
|
||||
render json: processing_error, status: 422 if @media_attachment.processing_failed?
|
||||
end
|
||||
|
||||
def media_attachment_params
|
||||
params.permit(:file, :description, :focus)
|
||||
end
|
||||
|
||||
|
|
|
@ -5,8 +5,6 @@ class Api::V1::MutesController < Api::BaseController
|
|||
before_action :require_user!
|
||||
after_action :insert_pagination_headers
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
@data = @accounts = load_accounts
|
||||
render json: @accounts, each_serializer: REST::AccountSerializer
|
||||
|
|
|
@ -6,8 +6,6 @@ class Api::V1::NotificationsController < Api::BaseController
|
|||
before_action :require_user!
|
||||
after_action :insert_pagination_headers, only: :index
|
||||
|
||||
respond_to :json
|
||||
|
||||
DEFAULT_NOTIFICATIONS_LIMIT = 15
|
||||
|
||||
def index
|
||||
|
|
|
@ -7,8 +7,6 @@ class Api::V1::Polls::VotesController < Api::BaseController
|
|||
before_action :require_user!
|
||||
before_action :set_poll
|
||||
|
||||
respond_to :json
|
||||
|
||||
def create
|
||||
VoteService.new.call(current_account, @poll, vote_params[:choices])
|
||||
render json: @poll, serializer: REST::PollSerializer
|
||||
|
|
|
@ -7,8 +7,6 @@ class Api::V1::PollsController < Api::BaseController
|
|||
before_action :set_poll
|
||||
before_action :refresh_poll
|
||||
|
||||
respond_to :json
|
||||
|
||||
def show
|
||||
render json: @poll, serializer: REST::PollSerializer, include_results: true
|
||||
end
|
||||
|
|
|
@ -4,8 +4,6 @@ class Api::V1::PreferencesController < Api::BaseController
|
|||
before_action -> { doorkeeper_authorize! :read, :'read:accounts' }
|
||||
before_action :require_user!
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
render json: current_account, serializer: REST::PreferencesSerializer
|
||||
end
|
||||
|
|
|
@ -4,7 +4,7 @@ class Api::V1::ReportsController < Api::BaseController
|
|||
before_action -> { doorkeeper_authorize! :write, :'write:reports' }, only: [:create]
|
||||
before_action :require_user!
|
||||
|
||||
respond_to :json
|
||||
override_rate_limit_headers :create, family: :reports
|
||||
|
||||
def create
|
||||
@report = ReportService.new.call(
|
||||
|
|
|
@ -7,8 +7,6 @@ class Api::V1::Statuses::BookmarksController < Api::BaseController
|
|||
before_action :require_user!
|
||||
before_action :set_status
|
||||
|
||||
respond_to :json
|
||||
|
||||
def create
|
||||
current_account.bookmarks.find_or_create_by!(account: current_account, status: @status)
|
||||
render json: @status, serializer: REST::StatusSerializer
|
||||
|
|
|
@ -7,8 +7,6 @@ class Api::V1::Statuses::FavouritedByAccountsController < Api::BaseController
|
|||
before_action :set_status
|
||||
after_action :insert_pagination_headers
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
@accounts = load_accounts
|
||||
render json: @accounts, each_serializer: REST::AccountSerializer
|
||||
|
|
|
@ -7,8 +7,6 @@ class Api::V1::Statuses::FavouritesController < Api::BaseController
|
|||
before_action :require_user!
|
||||
before_action :set_status
|
||||
|
||||
respond_to :json
|
||||
|
||||
def create
|
||||
FavouriteService.new.call(current_account, @status)
|
||||
render json: @status, serializer: REST::StatusSerializer
|
||||
|
|
|
@ -8,8 +8,6 @@ class Api::V1::Statuses::MutesController < Api::BaseController
|
|||
before_action :set_status
|
||||
before_action :set_conversation
|
||||
|
||||
respond_to :json
|
||||
|
||||
def create
|
||||
current_account.mute_conversation!(@conversation)
|
||||
@mutes_map = { @conversation.id => true }
|
||||
|
|
|
@ -7,8 +7,6 @@ class Api::V1::Statuses::PinsController < Api::BaseController
|
|||
before_action :require_user!
|
||||
before_action :set_status
|
||||
|
||||
respond_to :json
|
||||
|
||||
def create
|
||||
StatusPin.create!(account: current_account, status: @status)
|
||||
distribute_add_activity!
|
||||
|
|
|
@ -7,8 +7,6 @@ class Api::V1::Statuses::RebloggedByAccountsController < Api::BaseController
|
|||
before_action :set_status
|
||||
after_action :insert_pagination_headers
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
@accounts = load_accounts
|
||||
render json: @accounts, each_serializer: REST::AccountSerializer
|
||||
|
|
|
@ -7,10 +7,11 @@ class Api::V1::Statuses::ReblogsController < Api::BaseController
|
|||
before_action :require_user!
|
||||
before_action :set_reblog
|
||||
|
||||
respond_to :json
|
||||
override_rate_limit_headers :create, family: :statuses
|
||||
|
||||
def create
|
||||
@status = ReblogService.new.call(current_account, @reblog, reblog_params)
|
||||
|
||||
render json: @status, serializer: REST::StatusSerializer
|
||||
end
|
||||
|
||||
|
|
|
@ -7,8 +7,9 @@ class Api::V1::StatusesController < Api::BaseController
|
|||
before_action -> { doorkeeper_authorize! :write, :'write:statuses' }, only: [:create, :destroy]
|
||||
before_action :require_user!, except: [:show, :context]
|
||||
before_action :set_status, only: [:show, :context]
|
||||
before_action :set_thread, only: [:create]
|
||||
|
||||
respond_to :json
|
||||
override_rate_limit_headers :create, family: :statuses
|
||||
|
||||
# This API was originally unlimited, pagination cannot be introduced without
|
||||
# breaking backwards-compatibility. Arbitrarily high number to cover most
|
||||
|
@ -36,7 +37,7 @@ class Api::V1::StatusesController < Api::BaseController
|
|||
def create
|
||||
@status = PostStatusService.new.call(current_user.account,
|
||||
text: status_params[:status],
|
||||
thread: status_params[:in_reply_to_id].blank? ? nil : Status.find(status_params[:in_reply_to_id]),
|
||||
thread: @thread,
|
||||
media_ids: status_params[:media_ids],
|
||||
sensitive: status_params[:sensitive],
|
||||
spoiler_text: status_params[:spoiler_text],
|
||||
|
@ -45,7 +46,8 @@ class Api::V1::StatusesController < Api::BaseController
|
|||
application: doorkeeper_token.application,
|
||||
poll: status_params[:poll],
|
||||
content_type: status_params[:content_type],
|
||||
idempotency: request.headers['Idempotency-Key'])
|
||||
idempotency: request.headers['Idempotency-Key'],
|
||||
with_rate_limit: true)
|
||||
|
||||
render json: @status, serializer: @status.is_a?(ScheduledStatus) ? REST::ScheduledStatusSerializer : REST::StatusSerializer
|
||||
end
|
||||
|
@ -69,6 +71,12 @@ class Api::V1::StatusesController < Api::BaseController
|
|||
raise ActiveRecord::RecordNotFound
|
||||
end
|
||||
|
||||
def set_thread
|
||||
@thread = status_params[:in_reply_to_id].blank? ? nil : Status.find(status_params[:in_reply_to_id])
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
render json: { error: I18n.t('statuses.errors.in_reply_not_found') }, status: 404
|
||||
end
|
||||
|
||||
def status_params
|
||||
params.permit(
|
||||
:status,
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::StreamingController < Api::BaseController
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
if Rails.configuration.x.streaming_api_base_url != request.host
|
||||
redirect_to streaming_api_url, status: 301
|
||||
|
|
|
@ -7,8 +7,6 @@ class Api::V1::SuggestionsController < Api::BaseController
|
|||
before_action :require_user!
|
||||
before_action :set_accounts
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
render json: @accounts, each_serializer: REST::AccountSerializer
|
||||
end
|
||||
|
|
|
@ -5,8 +5,6 @@ class Api::V1::Timelines::HomeController < Api::BaseController
|
|||
before_action :require_user!, only: [:show]
|
||||
after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
|
||||
|
||||
respond_to :json
|
||||
|
||||
def show
|
||||
@statuses = load_statuses
|
||||
|
||||
|
|
|
@ -4,8 +4,6 @@ class Api::V1::Timelines::PublicController < Api::BaseController
|
|||
before_action :require_user!, only: [:show], if: :require_auth?
|
||||
after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
|
||||
|
||||
respond_to :json
|
||||
|
||||
def show
|
||||
@statuses = load_statuses
|
||||
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
|
||||
|
|
|
@ -4,8 +4,6 @@ class Api::V1::Timelines::TagController < Api::BaseController
|
|||
before_action :load_tag
|
||||
after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
|
||||
|
||||
respond_to :json
|
||||
|
||||
def show
|
||||
@statuses = load_statuses
|
||||
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
class Api::V1::TrendsController < Api::BaseController
|
||||
before_action :set_tags
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
render json: @tags, each_serializer: REST::TagSerializer
|
||||
end
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V2::MediaController < Api::V1::MediaController
|
||||
def create
|
||||
@media_attachment = current_account.media_attachments.create!({ delay_processing: true }.merge(media_attachment_params))
|
||||
render json: @media_attachment, serializer: REST::MediaAttachmentSerializer, status: 202
|
||||
rescue Paperclip::Errors::NotIdentifiedByImageMagickError
|
||||
render json: file_type_error, status: 422
|
||||
rescue Paperclip::Error
|
||||
render json: processing_error, status: 500
|
||||
end
|
||||
end
|
|
@ -8,8 +8,6 @@ class Api::V2::SearchController < Api::BaseController
|
|||
before_action -> { doorkeeper_authorize! :read, :'read:search' }
|
||||
before_action :require_user!
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
@search = Search.new(search_results)
|
||||
render json: @search, serializer: REST::SearchSerializer
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::Web::EmbedsController < Api::Web::BaseController
|
||||
respond_to :json
|
||||
|
||||
before_action :require_user!
|
||||
|
||||
def create
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::Web::PushSubscriptionsController < Api::Web::BaseController
|
||||
respond_to :json
|
||||
|
||||
before_action :require_user!
|
||||
|
||||
def create
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::Web::SettingsController < Api::Web::BaseController
|
||||
respond_to :json
|
||||
|
||||
before_action :require_user!
|
||||
|
||||
def update
|
||||
|
|
|
@ -30,6 +30,7 @@ class ApplicationController < ActionController::Base
|
|||
rescue_from Mastodon::NotPermittedError, with: :forbidden
|
||||
rescue_from HTTP::Error, OpenSSL::SSL::SSLError, with: :internal_server_error
|
||||
rescue_from Mastodon::RaceConditionError, with: :service_unavailable
|
||||
rescue_from Mastodon::RateLimitExceededError, with: :too_many_requests
|
||||
|
||||
before_action :store_current_location, except: :raise_not_found, unless: :devise_controller?
|
||||
before_action :require_functional!, if: :user_signed_in?
|
||||
|
@ -181,6 +182,10 @@ class ApplicationController < ActionController::Base
|
|||
respond_with_error(503)
|
||||
end
|
||||
|
||||
def too_many_requests
|
||||
respond_with_error(429)
|
||||
end
|
||||
|
||||
def single_user_mode?
|
||||
@single_user_mode ||= Rails.configuration.x.single_user_mode && Account.where('id > 0').exists?
|
||||
end
|
||||
|
|
|
@ -21,7 +21,7 @@ class AuthorizeInteractionsController < ApplicationController
|
|||
end
|
||||
|
||||
def create
|
||||
if @resource.is_a?(Account) && FollowService.new.call(current_account, @resource)
|
||||
if @resource.is_a?(Account) && FollowService.new.call(current_account, @resource, with_rate_limit: true)
|
||||
render :success
|
||||
else
|
||||
render :error
|
||||
|
|
|
@ -3,6 +3,20 @@
|
|||
module RateLimitHeaders
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
class_methods do
|
||||
def override_rate_limit_headers(method_name, options = {})
|
||||
around_action(only: method_name, if: :current_account) do |_controller, block|
|
||||
begin
|
||||
block.call
|
||||
ensure
|
||||
rate_limiter = RateLimiter.new(current_account, options)
|
||||
rate_limit_headers = rate_limiter.to_headers
|
||||
response.headers.merge!(rate_limit_headers) unless response.headers['X-RateLimit-Remaining'].present? && rate_limit_headers['X-RateLimit-Remaining'].to_i > response.headers['X-RateLimit-Remaining'].to_i
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
included do
|
||||
before_action :set_rate_limit_headers, if: :rate_limited_request?
|
||||
end
|
||||
|
@ -44,7 +58,7 @@ module RateLimitHeaders
|
|||
end
|
||||
|
||||
def api_throttle_data
|
||||
most_limited_type, = request.env['rack.attack.throttle_data'].min_by { |_, v| v[:limit] }
|
||||
most_limited_type, = request.env['rack.attack.throttle_data'].min_by { |_, v| v[:limit] - v[:count] }
|
||||
request.env['rack.attack.throttle_data'][most_limited_type]
|
||||
end
|
||||
|
||||
|
|
|
@ -29,7 +29,8 @@ class FollowerAccountsController < ApplicationController
|
|||
render json: collection_presenter,
|
||||
serializer: ActivityPub::CollectionSerializer,
|
||||
adapter: ActivityPub::Adapter,
|
||||
content_type: 'application/activity+json'
|
||||
content_type: 'application/activity+json',
|
||||
fields: restrict_fields_to
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -72,4 +73,12 @@ class FollowerAccountsController < ApplicationController
|
|||
)
|
||||
end
|
||||
end
|
||||
|
||||
def restrict_fields_to
|
||||
if page_requested? || !@account.user_hides_network?
|
||||
# Return all fields
|
||||
else
|
||||
%i(id type totalItems)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -29,7 +29,8 @@ class FollowingAccountsController < ApplicationController
|
|||
render json: collection_presenter,
|
||||
serializer: ActivityPub::CollectionSerializer,
|
||||
adapter: ActivityPub::Adapter,
|
||||
content_type: 'application/activity+json'
|
||||
content_type: 'application/activity+json',
|
||||
fields: restrict_fields_to
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -72,4 +73,12 @@ class FollowingAccountsController < ApplicationController
|
|||
)
|
||||
end
|
||||
end
|
||||
|
||||
def restrict_fields_to
|
||||
if page_requested? || !@account.user_hides_network?
|
||||
# Return all fields
|
||||
else
|
||||
%i(id type totalItems)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -29,6 +29,6 @@ class Settings::ImportsController < Settings::BaseController
|
|||
end
|
||||
|
||||
def import_params
|
||||
params.require(:import).permit(:data, :type)
|
||||
params.require(:import).permit(:data, :type, :mode)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,79 +9,8 @@ module Admin::ActionLogsHelper
|
|||
end
|
||||
end
|
||||
|
||||
def relevant_log_changes(log)
|
||||
if log.target_type == 'CustomEmoji' && [:enable, :disable, :destroy].include?(log.action)
|
||||
log.recorded_changes.slice('domain')
|
||||
elsif log.target_type == 'CustomEmoji' && log.action == :update
|
||||
log.recorded_changes.slice('domain', 'visible_in_picker')
|
||||
elsif log.target_type == 'User' && [:promote, :demote].include?(log.action)
|
||||
log.recorded_changes.slice('moderator', 'admin')
|
||||
elsif log.target_type == 'User' && [:change_email].include?(log.action)
|
||||
log.recorded_changes.slice('email', 'unconfirmed_email')
|
||||
elsif log.target_type == 'DomainBlock'
|
||||
log.recorded_changes.slice('severity', 'reject_media')
|
||||
elsif log.target_type == 'Status' && log.action == :update
|
||||
log.recorded_changes.slice('sensitive')
|
||||
elsif log.target_type == 'Announcement' && log.action == :update
|
||||
log.recorded_changes.slice('text', 'starts_at', 'ends_at', 'all_day')
|
||||
end
|
||||
end
|
||||
|
||||
def log_extra_attributes(hash)
|
||||
safe_join(hash.to_a.map { |key, value| safe_join([content_tag(:span, key, class: 'diff-key'), '=', log_change(value)]) }, ' ')
|
||||
end
|
||||
|
||||
def log_change(val)
|
||||
return content_tag(:span, val, class: 'diff-neutral') unless val.is_a?(Array)
|
||||
safe_join([content_tag(:span, val.first, class: 'diff-old'), content_tag(:span, val.last, class: 'diff-new')], '→')
|
||||
end
|
||||
|
||||
def icon_for_log(log)
|
||||
case log.target_type
|
||||
when 'Account', 'User'
|
||||
'user'
|
||||
when 'CustomEmoji'
|
||||
'file'
|
||||
when 'Report'
|
||||
'flag'
|
||||
when 'DomainBlock'
|
||||
'lock'
|
||||
when 'DomainAllow'
|
||||
'plus-circle'
|
||||
when 'EmailDomainBlock'
|
||||
'envelope'
|
||||
when 'Status'
|
||||
'pencil'
|
||||
when 'AccountWarning'
|
||||
'warning'
|
||||
when 'Announcement'
|
||||
'bullhorn'
|
||||
end
|
||||
end
|
||||
|
||||
def class_for_log_icon(log)
|
||||
case log.action
|
||||
when :enable, :unsuspend, :unsilence, :confirm, :promote, :resolve
|
||||
'positive'
|
||||
when :create
|
||||
opposite_verbs?(log) ? 'negative' : 'positive'
|
||||
when :update, :reset_password, :disable_2fa, :memorialize, :change_email
|
||||
'neutral'
|
||||
when :demote, :silence, :disable, :suspend, :remove_avatar, :remove_header, :reopen
|
||||
'negative'
|
||||
when :destroy
|
||||
opposite_verbs?(log) ? 'positive' : 'negative'
|
||||
else
|
||||
''
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def opposite_verbs?(log)
|
||||
%w(DomainBlock EmailDomainBlock AccountWarning).include?(log.target_type)
|
||||
end
|
||||
|
||||
def linkable_log_target(record)
|
||||
case record.class.name
|
||||
when 'Account'
|
||||
|
@ -99,7 +28,7 @@ module Admin::ActionLogsHelper
|
|||
when 'AccountWarning'
|
||||
link_to record.target_account.acct, admin_account_path(record.target_account_id)
|
||||
when 'Announcement'
|
||||
link_to "##{record.id}", edit_admin_announcement_path(record.id)
|
||||
link_to truncate(record.text), edit_admin_announcement_path(record.id)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -118,7 +47,7 @@ module Admin::ActionLogsHelper
|
|||
I18n.t('admin.action_logs.deleted_status')
|
||||
end
|
||||
when 'Announcement'
|
||||
"##{attributes['id']}"
|
||||
truncate(attributes['text'])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -10,6 +10,7 @@ module Admin::FilterHelper
|
|||
InviteFilter::KEYS,
|
||||
RelationshipFilter::KEYS,
|
||||
AnnouncementFilter::KEYS,
|
||||
Admin::ActionLogFilter::KEYS,
|
||||
].flatten.freeze
|
||||
|
||||
def filter_link_to(text, link_to_params, link_class_params = link_to_params)
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Admin::SettingsHelper
|
||||
def site_upload_delete_hint(hint, var)
|
||||
upload = SiteUpload.find_by(var: var.to_s)
|
||||
return hint unless upload
|
||||
|
||||
link = link_to t('admin.site_uploads.delete'), admin_site_upload_path(upload), data: { method: :delete }
|
||||
safe_join([hint, link], '<br/>'.html_safe)
|
||||
end
|
||||
end
|
|
@ -1,6 +1,6 @@
|
|||
// This file will be loaded on admin pages, regardless of theme.
|
||||
|
||||
import { delegate } from 'rails-ujs';
|
||||
import { delegate } from '@rails/ujs';
|
||||
import ready from '../mastodon/ready';
|
||||
|
||||
const batchCheckboxClassName = '.batch-checkbox input[type="checkbox"]';
|
||||
|
@ -32,6 +32,10 @@ delegate(document, '.media-spoiler-hide-button', 'click', () => {
|
|||
});
|
||||
});
|
||||
|
||||
delegate(document, '.filter-subset--with-select select', 'change', ({ target }) => {
|
||||
target.form.submit();
|
||||
});
|
||||
|
||||
const onDomainBlockSeverityChange = (target) => {
|
||||
const rejectMediaDiv = document.querySelector('.input.with_label.domain_block_reject_media');
|
||||
const rejectReportsDiv = document.querySelector('.input.with_label.domain_block_reject_reports');
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
import createHistory from 'history/createBrowserHistory';
|
||||
import ready from '../mastodon/ready';
|
||||
|
||||
const { delegate } = require('rails-ujs');
|
||||
const { delegate } = require('@rails/ujs');
|
||||
const { length } = require('stringz');
|
||||
|
||||
delegate(document, '.webapp-btn', 'click', ({ target, button }) => {
|
||||
|
@ -14,20 +14,6 @@ delegate(document, '.webapp-btn', 'click', ({ target, button }) => {
|
|||
return false;
|
||||
});
|
||||
|
||||
delegate(document, '.status__content__spoiler-link', 'click', function() {
|
||||
const contentEl = this.parentNode.parentNode.querySelector('.e-content');
|
||||
|
||||
if (contentEl.style.display === 'block') {
|
||||
contentEl.style.display = 'none';
|
||||
this.parentNode.style.marginBottom = 0;
|
||||
} else {
|
||||
contentEl.style.display = 'block';
|
||||
this.parentNode.style.marginBottom = null;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
delegate(document, '.modal-button', 'click', e => {
|
||||
e.preventDefault();
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// This file will be loaded on settings pages, regardless of theme.
|
||||
|
||||
import escapeTextContentForBrowser from 'escape-html';
|
||||
const { delegate } = require('rails-ujs');
|
||||
const { delegate } = require('@rails/ujs');
|
||||
import emojify from '../mastodon/features/emoji/emoji';
|
||||
|
||||
delegate(document, '#account_display_name', 'input', ({ target }) => {
|
||||
|
|
|
@ -370,6 +370,7 @@ export function fetchFollowersFail(id, error) {
|
|||
type: FOLLOWERS_FETCH_FAIL,
|
||||
id,
|
||||
error,
|
||||
skipNotFound: true,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -456,6 +457,7 @@ export function fetchFollowingFail(id, error) {
|
|||
type: FOLLOWING_FETCH_FAIL,
|
||||
id,
|
||||
error,
|
||||
skipNotFound: true,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -545,6 +547,7 @@ export function fetchRelationshipsFail(error) {
|
|||
type: RELATIONSHIPS_FETCH_FAIL,
|
||||
error,
|
||||
skipLoading: true,
|
||||
skipNotFound: true,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -34,11 +34,11 @@ export function showAlert(title = messages.unexpectedTitle, message = messages.u
|
|||
};
|
||||
};
|
||||
|
||||
export function showAlertForError(error) {
|
||||
export function showAlertForError(error, skipNotFound = false) {
|
||||
if (error.response) {
|
||||
const { data, status, statusText, headers } = error.response;
|
||||
|
||||
if (status === 404 || status === 410) {
|
||||
if (skipNotFound && (status === 404 || status === 410)) {
|
||||
// Skip these errors as they are reflected in the UI
|
||||
return { type: ALERT_NOOP };
|
||||
}
|
||||
|
|
|
@ -259,12 +259,31 @@ export function uploadCompose(files) {
|
|||
// Account for disparity in size of original image and resized data
|
||||
total += file.size - f.size;
|
||||
|
||||
return api(getState).post('/api/v1/media', data, {
|
||||
return api(getState).post('/api/v2/media', data, {
|
||||
onUploadProgress: function({ loaded }){
|
||||
progress[i] = loaded;
|
||||
dispatch(uploadComposeProgress(progress.reduce((a, v) => a + v, 0), total));
|
||||
},
|
||||
}).then(({ data }) => dispatch(uploadComposeSuccess(data, f)));
|
||||
}).then(({ status, data }) => {
|
||||
// If server-side processing of the media attachment has not completed yet,
|
||||
// poll the server until it is, before showing the media attachment as uploaded
|
||||
|
||||
if (status === 200) {
|
||||
dispatch(uploadComposeSuccess(data, f));
|
||||
} else if (status === 202) {
|
||||
const poll = () => {
|
||||
api(getState).get(`/api/v1/media/${data.id}`).then(response => {
|
||||
if (response.status === 200) {
|
||||
dispatch(uploadComposeSuccess(response.data, f));
|
||||
} else if (response.status === 206) {
|
||||
setTimeout(() => poll(), 1000);
|
||||
}
|
||||
}).catch(error => dispatch(uploadComposeFail(error)));
|
||||
};
|
||||
|
||||
poll();
|
||||
}
|
||||
});
|
||||
}).catch(error => dispatch(uploadComposeFail(error)));
|
||||
};
|
||||
};
|
||||
|
|
|
@ -27,4 +27,5 @@ export const fetchAccountIdentityProofsFail = (accountId, err) => ({
|
|||
type: IDENTITY_PROOFS_ACCOUNT_FETCH_FAIL,
|
||||
accountId,
|
||||
err,
|
||||
skipNotFound: true,
|
||||
});
|
||||
|
|
|
@ -165,6 +165,7 @@ export function expandTimelineFail(timeline, error, isLoadingMore) {
|
|||
timeline,
|
||||
error,
|
||||
skipLoading: !isLoadingMore,
|
||||
skipNotFound: timeline.startsWith('account:'),
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import { defineMessages, injectIntl } from 'react-intl';
|
|||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
||||
const messages = defineMessages({
|
||||
unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unhide {domain}' },
|
||||
unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unblock domain {domain}' },
|
||||
});
|
||||
|
||||
export default @injectIntl
|
||||
|
|
|
@ -45,7 +45,7 @@ export default class IntersectionObserverArticle extends React.Component {
|
|||
intersectionObserverWrapper.observe(
|
||||
id,
|
||||
this.node,
|
||||
this.handleIntersection
|
||||
this.handleIntersection,
|
||||
);
|
||||
|
||||
this.componentMounted = true;
|
||||
|
|
|
@ -23,7 +23,7 @@ const messages = defineMessages({
|
|||
id: 'status.sensitive_toggle',
|
||||
},
|
||||
toggle_visible: {
|
||||
defaultMessage: 'Toggle visibility',
|
||||
defaultMessage: 'Hide media',
|
||||
id: 'media_gallery.toggle_visible',
|
||||
},
|
||||
warning: {
|
||||
|
|
|
@ -127,15 +127,7 @@ class Poll extends ImmutablePureComponent {
|
|||
|
||||
return (
|
||||
<li key={option.get('title')}>
|
||||
{showResults && (
|
||||
<Motion defaultStyle={{ width: 0 }} style={{ width: spring(percent, { stiffness: 180, damping: 12 }) }}>
|
||||
{({ width }) =>
|
||||
<span className={classNames('poll__chart', { leading })} style={{ width: `${width}%` }} />
|
||||
}
|
||||
</Motion>
|
||||
)}
|
||||
|
||||
<label className={classNames('poll__text', { selectable: !showResults })}>
|
||||
<label className={classNames('poll__option', { selectable: !showResults })}>
|
||||
<input
|
||||
name='vote-options'
|
||||
type={poll.get('multiple') ? 'checkbox' : 'radio'}
|
||||
|
@ -157,12 +149,26 @@ class Poll extends ImmutablePureComponent {
|
|||
/>
|
||||
)}
|
||||
{showResults && <span className='poll__number'>
|
||||
{!!voted && <Icon id='check' className='poll__vote__mark' title={intl.formatMessage(messages.voted)} />}
|
||||
{Math.round(percent)}%
|
||||
</span>}
|
||||
|
||||
<span dangerouslySetInnerHTML={{ __html: titleEmojified }} />
|
||||
<span
|
||||
className='poll__option__text'
|
||||
dangerouslySetInnerHTML={{ __html: titleEmojified }}
|
||||
/>
|
||||
|
||||
{!!voted && <span className='poll__voted'>
|
||||
<Icon id='check' className='poll__voted__mark' title={intl.formatMessage(messages.voted)} />
|
||||
</span>}
|
||||
</label>
|
||||
|
||||
{showResults && (
|
||||
<Motion defaultStyle={{ width: 0 }} style={{ width: spring(percent, { stiffness: 180, damping: 12 }) }}>
|
||||
{({ width }) =>
|
||||
<span className={classNames('poll__chart', { leading })} style={{ width: `${width}%` }} />
|
||||
}
|
||||
</Motion>
|
||||
)}
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import Domain from '../components/domain';
|
|||
import { openModal } from '../actions/modal';
|
||||
|
||||
const messages = defineMessages({
|
||||
blockDomainConfirm: { id: 'confirmations.domain_block.confirm', defaultMessage: 'Hide entire domain' },
|
||||
blockDomainConfirm: { id: 'confirmations.domain_block.confirm', defaultMessage: 'Block entire domain' },
|
||||
});
|
||||
|
||||
const makeMapStateToProps = () => {
|
||||
|
|
|
@ -30,8 +30,8 @@ const messages = defineMessages({
|
|||
report: { id: 'account.report', defaultMessage: 'Report @{name}' },
|
||||
share: { id: 'account.share', defaultMessage: 'Share @{name}\'s profile' },
|
||||
media: { id: 'account.media', defaultMessage: 'Media' },
|
||||
blockDomain: { id: 'account.block_domain', defaultMessage: 'Hide everything from {domain}' },
|
||||
unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unhide {domain}' },
|
||||
blockDomain: { id: 'account.block_domain', defaultMessage: 'Block domain {domain}' },
|
||||
unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unblock domain {domain}' },
|
||||
hideReblogs: { id: 'account.hide_reblogs', defaultMessage: 'Hide boosts from @{name}' },
|
||||
showReblogs: { id: 'account.show_reblogs', defaultMessage: 'Show boosts from @{name}' },
|
||||
pins: { id: 'navigation_bar.pins', defaultMessage: 'Pinned toots' },
|
||||
|
@ -40,7 +40,7 @@ const messages = defineMessages({
|
|||
favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' },
|
||||
lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' },
|
||||
blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' },
|
||||
domain_blocks: { id: 'navigation_bar.domain_blocks', defaultMessage: 'Hidden domains' },
|
||||
domain_blocks: { id: 'navigation_bar.domain_blocks', defaultMessage: 'Blocked domains' },
|
||||
mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' },
|
||||
endorse: { id: 'account.endorse', defaultMessage: 'Feature on profile' },
|
||||
unendorse: { id: 'account.unendorse', defaultMessage: 'Don\'t feature on profile' },
|
||||
|
@ -136,7 +136,7 @@ class Header extends ImmutablePureComponent {
|
|||
if (me !== account.get('id') && account.getIn(['relationship', 'muting'])) {
|
||||
info.push(<span className='relationship-tag'><FormattedMessage id='account.muted' defaultMessage='Muted' /></span>);
|
||||
} else if (me !== account.get('id') && account.getIn(['relationship', 'domain_blocking'])) {
|
||||
info.push(<span className='relationship-tag'><FormattedMessage id='account.domain_blocked' defaultMessage='Domain hidden' /></span>);
|
||||
info.push(<span className='relationship-tag'><FormattedMessage id='account.domain_blocked' defaultMessage='Domain blocked' /></span>);
|
||||
}
|
||||
|
||||
if (me !== account.get('id')) {
|
||||
|
|
|
@ -199,8 +199,8 @@ class Audio extends React.PureComponent {
|
|||
<div className='video-player__controls active'>
|
||||
<div className='video-player__buttons-bar'>
|
||||
<div className='video-player__buttons left'>
|
||||
<button type='button' aria-label={intl.formatMessage(paused ? messages.play : messages.pause)} onClick={this.togglePlay}><Icon id={paused ? 'play' : 'pause'} fixedWidth /></button>
|
||||
<button type='button' aria-label={intl.formatMessage(muted ? messages.unmute : messages.mute)} onClick={this.toggleMute}><Icon id={muted ? 'volume-off' : 'volume-up'} fixedWidth /></button>
|
||||
<button type='button' title={intl.formatMessage(paused ? messages.play : messages.pause)} aria-label={intl.formatMessage(paused ? messages.play : messages.pause)} onClick={this.togglePlay}><Icon id={paused ? 'play' : 'pause'} fixedWidth /></button>
|
||||
<button type='button' title={intl.formatMessage(muted ? messages.unmute : messages.mute)} aria-label={intl.formatMessage(muted ? messages.unmute : messages.mute)} onClick={this.toggleMute}><Icon id={muted ? 'volume-off' : 'volume-up'} fixedWidth /></button>
|
||||
|
||||
<div className='video-player__volume' onMouseDown={this.handleVolumeMouseDown} ref={this.setVolumeRef}>
|
||||
|
||||
|
@ -221,7 +221,7 @@ class Audio extends React.PureComponent {
|
|||
</div>
|
||||
|
||||
<div className='video-player__buttons right'>
|
||||
<button type='button' aria-label={intl.formatMessage(messages.download)}>
|
||||
<button type='button' title={intl.formatMessage(messages.download)} aria-label={intl.formatMessage(messages.download)}>
|
||||
<a className='video-player__download__icon' href={this.props.src} download>
|
||||
<Icon id={'download'} fixedWidth />
|
||||
</a>
|
||||
|
|
|
@ -66,7 +66,7 @@ class Blocks extends ImmutablePureComponent {
|
|||
bindToDocument={!multiColumn}
|
||||
>
|
||||
{accountIds.map(id =>
|
||||
<AccountContainer key={id} id={id} />
|
||||
<AccountContainer key={id} id={id} />,
|
||||
)}
|
||||
</ScrollableList>
|
||||
</Column>
|
||||
|
|
|
@ -81,18 +81,6 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
this.props.onChange(e.target.value);
|
||||
}
|
||||
|
||||
handleKeyDown = ({ ctrlKey, keyCode, metaKey, altKey }) => {
|
||||
// We submit the status on control/meta + enter.
|
||||
if (keyCode === 13 && (ctrlKey || metaKey)) {
|
||||
this.handleSubmit();
|
||||
}
|
||||
|
||||
// Submit the status with secondary visibility on alt + enter.
|
||||
if (keyCode === 13 && altKey) {
|
||||
this.handleSecondarySubmit();
|
||||
}
|
||||
}
|
||||
|
||||
handleSubmit = (overriddenVisibility = null) => {
|
||||
const { textarea: { value }, uploadForm } = this;
|
||||
const {
|
||||
|
@ -123,7 +111,7 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
// Submit unless there are media with missing descriptions
|
||||
if (mediaDescriptionConfirmation && onMediaDescriptionConfirm && media && media.some(item => !item.get('description'))) {
|
||||
const firstWithoutDescription = media.find(item => !item.get('description'));
|
||||
onMediaDescriptionConfirm(this.context.router ? this.context.router.history : null, firstWithoutDescription.get('id'));
|
||||
onMediaDescriptionConfirm(this.context.router ? this.context.router.history : null, firstWithoutDescription.get('id'), overriddenVisibility);
|
||||
} else if (onSubmit) {
|
||||
if (onChangeVisibility && overriddenVisibility) {
|
||||
onChangeVisibility(overriddenVisibility);
|
||||
|
@ -171,10 +159,20 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
// When the escape key is released, we focus the UI.
|
||||
handleKeyUp = ({ key }) => {
|
||||
handleKeyUp = ({ key, ctrlKey, keyCode, metaKey, altKey }) => {
|
||||
if (key === 'Escape') {
|
||||
document.querySelector('.ui').parentElement.focus();
|
||||
}
|
||||
|
||||
// We submit the status on control/meta + enter.
|
||||
if (keyCode === 13 && (ctrlKey || metaKey)) {
|
||||
this.handleSubmit();
|
||||
}
|
||||
|
||||
// Submit the status with secondary visibility on alt + enter.
|
||||
if (keyCode === 13 && altKey) {
|
||||
this.handleSecondarySubmit();
|
||||
}
|
||||
}
|
||||
|
||||
// Sets a reference to the textarea.
|
||||
|
@ -307,7 +305,6 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
placeholder={intl.formatMessage(messages.spoiler_placeholder)}
|
||||
value={spoilerText}
|
||||
onChange={this.handleChangeSpoiler}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
onKeyUp={this.handleKeyUp}
|
||||
disabled={!spoiler}
|
||||
ref={this.handleRefSpoilerText}
|
||||
|
@ -328,9 +325,9 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
disabled={isSubmitting}
|
||||
value={this.props.text}
|
||||
onChange={this.handleChange}
|
||||
onKeyUp={this.handleKeyUp}
|
||||
suggestions={this.props.suggestions}
|
||||
onFocus={this.handleFocus}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
onSuggestionsFetchRequested={onFetchSuggestions}
|
||||
onSuggestionsClearRequested={onClearSuggestions}
|
||||
onSuggestionSelected={this.onSuggestionSelected}
|
||||
|
|
|
@ -34,7 +34,7 @@ const messages = defineMessages({
|
|||
id: 'content-type.change',
|
||||
},
|
||||
direct_long: {
|
||||
defaultMessage: 'Post to mentioned users only',
|
||||
defaultMessage: 'Visible for mentioned users only',
|
||||
id: 'privacy.direct.long',
|
||||
},
|
||||
direct_short: {
|
||||
|
@ -66,7 +66,7 @@ const messages = defineMessages({
|
|||
id: 'compose.content-type.plain',
|
||||
},
|
||||
private_long: {
|
||||
defaultMessage: 'Post to followers only',
|
||||
defaultMessage: 'Visible for followers only',
|
||||
id: 'privacy.private.long',
|
||||
},
|
||||
private_short: {
|
||||
|
@ -74,7 +74,7 @@ const messages = defineMessages({
|
|||
id: 'privacy.private.short',
|
||||
},
|
||||
public_long: {
|
||||
defaultMessage: 'Post to public timelines',
|
||||
defaultMessage: 'Visible for all, shown in public timelines',
|
||||
id: 'privacy.public.long',
|
||||
},
|
||||
public_short: {
|
||||
|
@ -94,7 +94,7 @@ const messages = defineMessages({
|
|||
id: 'advanced_options.threaded_mode.short',
|
||||
},
|
||||
unlisted_long: {
|
||||
defaultMessage: 'Do not show in public timelines',
|
||||
defaultMessage: 'Visible for all, but not in public timelines',
|
||||
id: 'privacy.unlisted.long',
|
||||
},
|
||||
unlisted_short: {
|
||||
|
|
|
@ -62,7 +62,7 @@ class Option extends React.PureComponent {
|
|||
|
||||
return (
|
||||
<li>
|
||||
<label className='poll__text editable'>
|
||||
<label className='poll__option editable'>
|
||||
<span className={classNames('poll__input', { checkbox: isPollMultiple })} />
|
||||
|
||||
<AutosuggestInput
|
||||
|
@ -143,6 +143,7 @@ class PollForm extends ImmutablePureComponent {
|
|||
<option value='true'>{intl.formatMessage(messages.multiple_choices)}</option>
|
||||
</select>
|
||||
|
||||
{/* eslint-disable-next-line jsx-a11y/no-onchange */}
|
||||
<select value={expiresIn} onChange={this.handleSelectDuration}>
|
||||
<option value={300}>{intl.formatMessage(messages.minutes, { number: 5 })}</option>
|
||||
<option value={1800}>{intl.formatMessage(messages.minutes, { number: 30 })}</option>
|
||||
|
|
|
@ -114,11 +114,16 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
|||
dispatch(changeComposeVisibility(value));
|
||||
},
|
||||
|
||||
onMediaDescriptionConfirm(routerHistory, mediaId) {
|
||||
onMediaDescriptionConfirm(routerHistory, mediaId, overriddenVisibility = null) {
|
||||
dispatch(openModal('CONFIRM', {
|
||||
message: intl.formatMessage(messages.missingDescriptionMessage),
|
||||
confirm: intl.formatMessage(messages.missingDescriptionConfirm),
|
||||
onConfirm: () => dispatch(submitCompose(routerHistory)),
|
||||
onConfirm: () => {
|
||||
if (overriddenVisibility) {
|
||||
dispatch(changeComposeVisibility(overriddenVisibility));
|
||||
};
|
||||
dispatch(submitCompose(routerHistory));
|
||||
},
|
||||
secondary: intl.formatMessage(messages.missingDescriptionEdit),
|
||||
onSecondary: () => dispatch(openModal('FOCAL_POINT', { id: mediaId })),
|
||||
onDoNotAsk: () => dispatch(changeLocalSetting(['confirm_missing_media_description'], false)),
|
||||
|
|
|
@ -195,7 +195,7 @@ class Conversation extends ImmutablePureComponent {
|
|||
return (
|
||||
<HotKeys handlers={handlers}>
|
||||
<div className={classNames('conversation focusable muted', { 'conversation--unread': unread })} tabIndex='0'>
|
||||
<div className='conversation__avatar'>
|
||||
<div className='conversation__avatar' onClick={this.handleClick} role='presentation'>
|
||||
<AvatarComposite accounts={accounts} size={48} />
|
||||
</div>
|
||||
|
||||
|
|
|
@ -13,8 +13,8 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
|
|||
import ScrollableList from 'flavours/glitch/components/scrollable_list';
|
||||
|
||||
const messages = defineMessages({
|
||||
heading: { id: 'column.domain_blocks', defaultMessage: 'Hidden domains' },
|
||||
unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unhide {domain}' },
|
||||
heading: { id: 'column.domain_blocks', defaultMessage: 'Blocked domains' },
|
||||
unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unblock domain {domain}' },
|
||||
});
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
|
@ -54,7 +54,7 @@ class Blocks extends ImmutablePureComponent {
|
|||
);
|
||||
}
|
||||
|
||||
const emptyMessage = <FormattedMessage id='empty_column.domain_blocks' defaultMessage='There are no hidden domains yet.' />;
|
||||
const emptyMessage = <FormattedMessage id='empty_column.domain_blocks' defaultMessage='There are no blocked domains yet.' />;
|
||||
|
||||
return (
|
||||
<Column bindToDocument={!multiColumn} icon='minus-circle' heading={intl.formatMessage(messages.heading)}>
|
||||
|
@ -67,7 +67,7 @@ class Blocks extends ImmutablePureComponent {
|
|||
bindToDocument={!multiColumn}
|
||||
>
|
||||
{domains.map(domain =>
|
||||
<DomainContainer key={domain} domain={domain} />
|
||||
<DomainContainer key={domain} domain={domain} />,
|
||||
)}
|
||||
</ScrollableList>
|
||||
</Column>
|
||||
|
|
|
@ -88,7 +88,7 @@ class Favourites extends ImmutablePureComponent {
|
|||
bindToDocument={!multiColumn}
|
||||
>
|
||||
{accountIds.map(id =>
|
||||
<AccountContainer key={id} id={id} withNote={false} />
|
||||
<AccountContainer key={id} id={id} withNote={false} />,
|
||||
)}
|
||||
</ScrollableList>
|
||||
</Column>
|
||||
|
|
|
@ -11,6 +11,7 @@ import { fetchFollowRequests, expandFollowRequests } from 'flavours/glitch/actio
|
|||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import ScrollableList from 'flavours/glitch/components/scrollable_list';
|
||||
import { me } from 'flavours/glitch/util/initial_state';
|
||||
|
||||
const messages = defineMessages({
|
||||
heading: { id: 'column.follow_requests', defaultMessage: 'Follow requests' },
|
||||
|
@ -19,6 +20,8 @@ const messages = defineMessages({
|
|||
const mapStateToProps = state => ({
|
||||
accountIds: state.getIn(['user_lists', 'follow_requests', 'items']),
|
||||
hasMore: !!state.getIn(['user_lists', 'follow_requests', 'next']),
|
||||
locked: !!state.getIn(['accounts', me, 'locked']),
|
||||
domain: state.getIn(['meta', 'domain']),
|
||||
});
|
||||
|
||||
export default @connect(mapStateToProps)
|
||||
|
@ -30,6 +33,8 @@ class FollowRequests extends ImmutablePureComponent {
|
|||
dispatch: PropTypes.func.isRequired,
|
||||
hasMore: PropTypes.bool,
|
||||
accountIds: ImmutablePropTypes.list,
|
||||
locked: PropTypes.bool,
|
||||
domain: PropTypes.string,
|
||||
intl: PropTypes.object.isRequired,
|
||||
multiColumn: PropTypes.bool,
|
||||
};
|
||||
|
@ -43,7 +48,7 @@ class FollowRequests extends ImmutablePureComponent {
|
|||
}, 300, { leading: true });
|
||||
|
||||
render () {
|
||||
const { intl, accountIds, hasMore, multiColumn } = this.props;
|
||||
const { intl, accountIds, hasMore, multiColumn, locked, domain } = this.props;
|
||||
|
||||
if (!accountIds) {
|
||||
return (
|
||||
|
@ -54,6 +59,15 @@ class FollowRequests extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
const emptyMessage = <FormattedMessage id='empty_column.follow_requests' defaultMessage="You don't have any follow requests yet. When you receive one, it will show up here." />;
|
||||
const unlockedPrependMessage = locked ? null : (
|
||||
<div className='follow_requests-unlocked_explanation'>
|
||||
<FormattedMessage
|
||||
id='follow_requests.unlocked_explanation'
|
||||
defaultMessage='Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.'
|
||||
values={{ domain: domain }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Column bindToDocument={!multiColumn} name='follow-requests' icon='user-plus' heading={intl.formatMessage(messages.heading)}>
|
||||
|
@ -65,9 +79,10 @@ class FollowRequests extends ImmutablePureComponent {
|
|||
hasMore={hasMore}
|
||||
emptyMessage={emptyMessage}
|
||||
bindToDocument={!multiColumn}
|
||||
prepend={unlockedPrependMessage}
|
||||
>
|
||||
{accountIds.map(id =>
|
||||
<AccountAuthorizeContainer key={id} id={id} />
|
||||
<AccountAuthorizeContainer key={id} id={id} />,
|
||||
)}
|
||||
</ScrollableList>
|
||||
</Column>
|
||||
|
|
|
@ -105,7 +105,7 @@ class Followers extends ImmutablePureComponent {
|
|||
bindToDocument={!multiColumn}
|
||||
>
|
||||
{accountIds.map(id =>
|
||||
<AccountContainer key={id} id={id} withNote={false} />
|
||||
<AccountContainer key={id} id={id} withNote={false} />,
|
||||
)}
|
||||
</ScrollableList>
|
||||
</Column>
|
||||
|
|
|
@ -105,7 +105,7 @@ class Following extends ImmutablePureComponent {
|
|||
bindToDocument={!multiColumn}
|
||||
>
|
||||
{accountIds.map(id =>
|
||||
<AccountContainer key={id} id={id} withNote={false} />
|
||||
<AccountContainer key={id} id={id} withNote={false} />,
|
||||
)}
|
||||
</ScrollableList>
|
||||
</Column>
|
||||
|
|
|
@ -95,6 +95,10 @@ class Content extends ImmutablePureComponent {
|
|||
} else if (link.textContent[0] === '#' || (link.previousSibling && link.previousSibling.textContent && link.previousSibling.textContent[link.previousSibling.textContent.length - 1] === '#')) {
|
||||
link.addEventListener('click', this.onHashtagClick.bind(this, link.text), false);
|
||||
} else {
|
||||
let status = this.props.announcement.get('statuses').find(item => link.href === item.get('url'));
|
||||
if (status) {
|
||||
link.addEventListener('click', this.onStatusClick.bind(this, status), false);
|
||||
}
|
||||
link.setAttribute('title', link.href);
|
||||
link.classList.add('unhandled-link');
|
||||
}
|
||||
|
@ -120,6 +124,13 @@ class Content extends ImmutablePureComponent {
|
|||
}
|
||||
}
|
||||
|
||||
onStatusClick = (status, e) => {
|
||||
if (this.context.router && e.button === 0 && !(e.ctrlKey || e.metaKey)) {
|
||||
e.preventDefault();
|
||||
this.context.router.history.push(`/statuses/${status.get('id')}`);
|
||||
}
|
||||
}
|
||||
|
||||
handleEmojiMouseEnter = ({ target }) => {
|
||||
target.src = target.getAttribute('data-original');
|
||||
}
|
||||
|
@ -367,6 +378,14 @@ class Announcements extends ImmutablePureComponent {
|
|||
index: 0,
|
||||
};
|
||||
|
||||
static getDerivedStateFromProps(props, state) {
|
||||
if (props.announcements.size > 0 && state.index >= props.announcements.size) {
|
||||
return { index: props.announcements.size - 1 };
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
this._markAnnouncementAsRead();
|
||||
}
|
||||
|
|
|
@ -73,7 +73,7 @@ class Lists extends ImmutablePureComponent {
|
|||
bindToDocument={!multiColumn}
|
||||
>
|
||||
{lists.map(list =>
|
||||
<ColumnLink key={list.get('id')} to={`/timelines/list/${list.get('id')}`} icon='list-ul' text={list.get('title')} />
|
||||
<ColumnLink key={list.get('id')} to={`/timelines/list/${list.get('id')}`} icon='list-ul' text={list.get('title')} />,
|
||||
)}
|
||||
</ScrollableList>
|
||||
</Column>
|
||||
|
|
|
@ -66,7 +66,7 @@ class Mutes extends ImmutablePureComponent {
|
|||
bindToDocument={!multiColumn}
|
||||
>
|
||||
{accountIds.map(id =>
|
||||
<AccountContainer key={id} id={id} />
|
||||
<AccountContainer key={id} id={id} />,
|
||||
)}
|
||||
</ScrollableList>
|
||||
</Column>
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue