diff --git a/.env.nanobox b/.env.nanobox index 7920c47b9..48204a6bf 100644 --- a/.env.nanobox +++ b/.env.nanobox @@ -35,6 +35,17 @@ PAPERCLIP_SECRET=$PAPERCLIP_SECRET SECRET_KEY_BASE=$SECRET_KEY_BASE OTP_SECRET=$OTP_SECRET +# VAPID keys (used for push notifications) +# You can generate the keys using the following command (first is the private key, second is the public one) +# You should only generate this once per instance. If you later decide to change it, all push subscription will +# be invalidated, requiring the users to access the website again to resubscribe. +# +# Generate with `rake mastodon:webpush:generate_vapid_key` task (`nanobox run bundle exec rake mastodon:webpush:generate_vapid_key`) +# +# For more information visit https://rossta.net/blog/using-the-web-push-api-with-vapid.html +VAPID_PRIVATE_KEY=$VAPID_PRIVATE_KEY +VAPID_PUBLIC_KEY=$VAPID_PUBLIC_KEY + # Registrations # Single user mode will disable registrations and redirect frontpage to the first profile # SINGLE_USER_MODE=true @@ -62,7 +73,7 @@ SMTP_FROM_ADDRESS=notifications@${APP_NAME}.nanoapp.io #SMTP_CA_FILE=/etc/ssl/certs/ca-certificates.crt #SMTP_OPENSSL_VERIFY_MODE=peer #SMTP_ENABLE_STARTTLS_AUTO=true - +#SMTP_TLS=true # Optional user upload path and URL (images, avatars). Default is :rails_root/public/system. If you set this variable, you are responsible for making your HTTP server (eg. nginx) serve these files. # PAPERCLIP_ROOT_PATH=/var/lib/mastodon/public-system @@ -91,6 +102,23 @@ SMTP_FROM_ADDRESS=notifications@${APP_NAME}.nanoapp.io # S3_ENDPOINT= # S3_SIGNATURE_VERSION= +# Swift (optional) +# SWIFT_ENABLED=true +# SWIFT_USERNAME= +# For Keystone V3, the value for SWIFT_TENANT should be the project name +# SWIFT_TENANT= +# SWIFT_PASSWORD= +# Keystone V2 and V3 URLs are supported. Use a V3 URL if possible to avoid +# issues with token rate-limiting during high load. +# SWIFT_AUTH_URL= +# SWIFT_CONTAINER= +# SWIFT_OBJECT_URL= +# SWIFT_REGION= +# Defaults to 'default' +# SWIFT_DOMAIN_NAME= +# Defaults to 60 seconds. Set to 0 to disable +# SWIFT_CACHE_TTL= + # Optional alias for S3 if you want to use Cloudfront or Cloudflare in front # S3_CLOUDFRONT_HOST= diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..7cec57180 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,46 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at eugen@zeonfederated.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/Gemfile b/Gemfile index 7b359af1d..d0b7aaef1 100644 --- a/Gemfile +++ b/Gemfile @@ -14,8 +14,10 @@ gem 'pg', '~> 0.20' gem 'pghero', '~> 1.7' gem 'dotenv-rails', '~> 2.2' -gem 'aws-sdk', '~> 2.9' -gem 'fog-openstack', '~> 0.1' +gem 'fog-aws', '~> 1.4', require: false +gem 'fog-core', '~> 1.45' +gem 'fog-local', '~> 0.4', require: false +gem 'fog-openstack', '~> 0.1', require: false gem 'paperclip', '~> 5.1' gem 'paperclip-av-transcoder', '~> 0.6' @@ -38,14 +40,14 @@ gem 'http', '~> 2.2' gem 'http_accept_language', '~> 2.1' gem 'httplog', '~> 0.99' gem 'idn-ruby', require: 'idn' -gem 'kaminari', '~> 1.0' +gem 'kaminari', '~> 1.1' gem 'link_header', '~> 0.0' gem 'mime-types', '~> 3.1' -gem 'nokogiri', '~> 1.7' +gem 'nokogiri', '~> 1.8' gem 'nsa', '~> 0.2' -gem 'oj', '~> 3.0' +gem 'oj', '~> 3.3' gem 'ostatus2', '~> 2.0' -gem 'ox', '~> 2.5' +gem 'ox', '~> 2.8' gem 'pundit', '~> 1.1' gem 'rabl', '~> 0.13' gem 'rack-attack', '~> 5.0' @@ -75,15 +77,15 @@ gem 'json-ld-preloaded', '~> 2.2.1' gem 'rdf-normalize', '~> 0.3.1' group :development, :test do - gem 'fabrication', '~> 2.16' + gem 'fabrication', '~> 2.18' gem 'fuubar', '~> 2.2' gem 'i18n-tasks', '~> 0.9', require: false gem 'pry-rails', '~> 0.3' - gem 'rspec-rails', '~> 3.6' + gem 'rspec-rails', '~> 3.7' end group :test do - gem 'capybara', '~> 2.14' + gem 'capybara', '~> 2.15' gem 'climate_control', '~> 0.2' gem 'faker', '~> 1.7' gem 'microformats', '~> 4.0' @@ -91,13 +93,13 @@ group :test do gem 'rspec-sidekiq', '~> 3.0' gem 'simplecov', '~> 0.14', require: false gem 'webmock', '~> 3.0' - gem 'parallel_tests', '~> 2.14' + gem 'parallel_tests', '~> 2.17' end group :development do gem 'active_record_query_trace', '~> 1.5' gem 'annotate', '~> 2.7' - gem 'better_errors', '~> 2.1' + gem 'better_errors', '~> 2.4' gem 'binding_of_caller', '~> 0.7' gem 'bullet', '~> 5.5' gem 'letter_opener', '~> 1.4' @@ -105,15 +107,15 @@ group :development do gem 'rubocop', require: false gem 'brakeman', '~> 4.0', require: false gem 'bundler-audit', '~> 0.6', require: false - gem 'scss_lint', '~> 0.53', require: false + gem 'scss_lint', '~> 0.55', require: false - gem 'capistrano', '~> 3.8' - gem 'capistrano-rails', '~> 1.2' + gem 'capistrano', '~> 3.10' + gem 'capistrano-rails', '~> 1.3' gem 'capistrano-rbenv', '~> 2.1' gem 'capistrano-yarn', '~> 2.0' end group :production do - gem 'lograge', '~> 0.5' + gem 'lograge', '~> 0.7' gem 'redis-rails', '~> 5.0' end diff --git a/Gemfile.lock b/Gemfile.lock index 14ed0d309..f9c69d538 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -57,25 +57,17 @@ GEM encryptor (~> 3.0.0) av (0.9.0) cocaine (~> 0.5.3) - aws-sdk (2.10.46) - aws-sdk-resources (= 2.10.46) - aws-sdk-core (2.10.46) - aws-sigv4 (~> 1.0) - jmespath (~> 1.0) - aws-sdk-resources (2.10.46) - aws-sdk-core (= 2.10.46) - aws-sigv4 (1.0.2) bcrypt (3.1.11) - better_errors (2.3.0) + better_errors (2.4.0) coderay (>= 1.0.0) erubi (>= 1.0.0) rack (>= 0.9.0) - binding_of_caller (0.7.2) + binding_of_caller (0.7.3) debug_inspector (>= 0.0.1) - bootsnap (1.1.3) + bootsnap (1.1.5) msgpack (~> 1.0) brakeman (4.0.1) - browser (2.5.1) + browser (2.5.2) builder (3.2.3) bullet (5.6.1) activesupport (>= 3.0.0) @@ -83,23 +75,23 @@ GEM bundler-audit (0.6.0) bundler (~> 1.2) thor (~> 0.18) - capistrano (3.9.1) + capistrano (3.10.0) airbrussh (>= 1.0.0) i18n rake (>= 10.0.0) sshkit (>= 1.9.0) - capistrano-bundler (1.2.0) + capistrano-bundler (1.3.0) capistrano (~> 3.1) sshkit (~> 1.2) capistrano-rails (1.3.0) capistrano (~> 3.1) capistrano-bundler (~> 1.1) - capistrano-rbenv (2.1.1) + capistrano-rbenv (2.1.2) capistrano (~> 3.1) sshkit (~> 1.3) capistrano-yarn (2.0.2) capistrano (~> 3.0) - capybara (2.15.1) + capybara (2.15.4) addressable mini_mime (>= 0.1.3) nokogiri (>= 1.3.3) @@ -110,7 +102,7 @@ GEM activesupport charlock_holmes (0.7.5) chunky_png (1.3.8) - cld3 (3.2.0) + cld3 (3.2.1) ffi (>= 1.1.0, < 1.10.0) climate_control (0.2.0) cocaine (0.5.8) @@ -150,16 +142,21 @@ GEM thread thread_safe encryptor (3.0.0) - erubi (1.6.1) - et-orbi (1.0.5) + erubi (1.7.0) + et-orbi (1.0.8) tzinfo excon (0.59.0) execjs (2.7.0) - fabrication (2.16.3) + fabrication (2.18.0) faker (1.8.4) i18n (~> 0.5) fast_blank (1.0.0) ffi (1.9.18) + fog-aws (1.4.1) + fog-core (~> 1.38) + fog-json (~> 1.0) + fog-xml (~> 0.1) + ipaddress (~> 0.8) fog-core (1.45.0) builder excon (~> 0.58) @@ -167,15 +164,20 @@ GEM fog-json (1.0.2) fog-core (~> 1.0) multi_json (~> 1.10) - fog-openstack (0.1.21) + fog-local (0.4.0) + fog-core (~> 1.27) + fog-openstack (0.1.22) fog-core (>= 1.40) fog-json (>= 1.0) ipaddress (>= 0.8) + fog-xml (0.1.3) + fog-core + nokogiri (>= 1.5.11, < 2.0.0) formatador (0.2.5) fuubar (2.2.0) rspec-core (~> 3.0) ruby-progressbar (~> 1.4) - globalid (0.4.0) + globalid (0.4.1) activesupport (>= 4.2.0) goldfinger (2.0.1) addressable (~> 2.5) @@ -211,7 +213,8 @@ GEM httplog (0.99.7) colorize rack - i18n (0.8.6) + i18n (0.9.0) + concurrent-ruby (~> 1.0) i18n-tasks (0.9.18) activesupport (>= 4.0.2) ast (>= 2.1.0) @@ -225,29 +228,28 @@ GEM idn-ruby (0.1.0) ipaddress (0.8.3) iso-639 (0.2.8) - jmespath (1.3.1) json (2.1.0) - json-ld (2.1.5) + json-ld (2.1.7) multi_json (~> 1.12) - rdf (~> 2.2) + rdf (~> 2.2, >= 2.2.8) json-ld-preloaded (2.2.2) json-ld (~> 2.1, >= 2.1.5) multi_json (~> 1.11) rdf (~> 2.2) jsonapi-renderer (0.1.3) jwt (1.5.6) - kaminari (1.0.1) + kaminari (1.1.1) activesupport (>= 4.1.0) - kaminari-actionview (= 1.0.1) - kaminari-activerecord (= 1.0.1) - kaminari-core (= 1.0.1) - kaminari-actionview (1.0.1) + kaminari-actionview (= 1.1.1) + kaminari-activerecord (= 1.1.1) + kaminari-core (= 1.1.1) + kaminari-actionview (1.1.1) actionview - kaminari-core (= 1.0.1) - kaminari-activerecord (1.0.1) + kaminari-core (= 1.1.1) + kaminari-activerecord (1.1.1) activerecord - kaminari-core (= 1.0.1) - kaminari-core (1.0.1) + kaminari-core (= 1.1.1) + kaminari-core (1.1.1) launchy (2.4.3) addressable (~> 2.3) letter_opener (1.4.1) @@ -257,18 +259,19 @@ GEM letter_opener (~> 1.0) railties (>= 3.2) link_header (0.0.8) - lograge (0.6.0) + lograge (0.7.1) actionpack (>= 4, < 5.2) activesupport (>= 4, < 5.2) railties (>= 4, < 5.2) request_store (~> 1.0) - loofah (2.0.3) + loofah (2.1.1) + crass (~> 1.0.2) nokogiri (>= 1.5.9) mail (2.6.6) mime-types (>= 1.16, < 4) mario-redis-lock (1.2.0) redis (~> 3, >= 3.0.5) - method_source (0.8.2) + method_source (0.9.0) microformats (4.0.7) json nokogiri @@ -277,7 +280,7 @@ GEM mime-types-data (3.2016.0521) mimemagic (0.3.2) mini_mime (0.1.4) - mini_portile2 (2.2.0) + mini_portile2 (2.3.0) minitest (5.10.3) msgpack (1.1.0) multi_json (1.12.2) @@ -285,8 +288,8 @@ GEM net-ssh (>= 2.6.5) net-ssh (4.2.0) nio4r (2.1.0) - nokogiri (1.8.0) - mini_portile2 (~> 2.2.0) + nokogiri (1.8.1) + mini_portile2 (~> 2.3.0) nokogumbo (1.4.13) nokogiri nsa (0.2.4) @@ -294,15 +297,15 @@ GEM concurrent-ruby (~> 1.0.0) sidekiq (>= 3.5.0) statsd-ruby (~> 1.2.0) - oj (3.3.5) - openssl (2.0.5) + oj (3.3.9) + openssl (2.0.6) orm_adapter (0.5.0) ostatus2 (2.0.1) addressable (~> 2.4) http (~> 2.0) nokogiri (~> 1.6) openssl (~> 2.0) - ox (2.6.0) + ox (2.8.1) paperclip (5.1.0) activemodel (>= 4.2.0) activesupport (>= 4.2.0) @@ -313,19 +316,18 @@ GEM av (~> 0.9.0) paperclip (>= 2.5.2) parallel (1.12.0) - parallel_tests (2.15.0) + parallel_tests (2.17.0) parallel parser (2.4.0.0) ast (~> 2.2) pg (0.21.0) pghero (1.7.0) activerecord - pkg-config (1.2.7) + pkg-config (1.2.8) powerpack (0.1.1) - pry (0.10.4) + pry (0.11.2) coderay (~> 1.1.0) - method_source (~> 0.8.1) - slop (~> 3.4) + method_source (~> 0.9.0) pry-rails (0.3.6) pry (>= 0.10.4) public_suffix (3.0.0) @@ -379,31 +381,31 @@ GEM thor (>= 0.18.1, < 2.0) rainbow (2.2.2) rake - rake (12.1.0) - rdf (2.2.9) + rake (12.2.1) + rdf (2.2.11) hamster (~> 3.0) link_header (~> 0.0, >= 0.0.8) rdf-normalize (0.3.2) rdf (~> 2.0) - redis (3.3.3) - redis-actionpack (5.0.1) + redis (3.3.5) + redis-actionpack (5.0.2) actionpack (>= 4.0, < 6) redis-rack (>= 1, < 3) - redis-store (>= 1.1.0, < 1.4.0) - redis-activesupport (5.0.3) + redis-store (>= 1.1.0, < 2) + redis-activesupport (5.0.4) activesupport (>= 3, < 6) - redis-store (~> 1.3.0) + redis-store (>= 1.3, < 2) redis-namespace (1.5.3) redis (~> 3.0, >= 3.0.4) - redis-rack (2.0.2) + redis-rack (2.0.3) rack (>= 1.5, < 3) - redis-store (>= 1.2, < 1.4) + redis-store (>= 1.2, < 2) redis-rails (5.0.2) redis-actionpack (>= 5.0, < 6) redis-activesupport (>= 5.0, < 6) redis-store (>= 1.2, < 2) - redis-store (1.3.0) - redis (>= 2.2) + redis-store (1.4.1) + redis (>= 2.2, < 5) request_store (1.3.2) responders (2.4.0) actionpack (>= 4.2.0, < 5.3) @@ -411,27 +413,27 @@ GEM rotp (2.1.2) rqrcode (0.10.1) chunky_png (~> 1.0) - rspec-core (3.6.0) - rspec-support (~> 3.6.0) - rspec-expectations (3.6.0) + rspec-core (3.7.0) + rspec-support (~> 3.7.0) + rspec-expectations (3.7.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.6.0) - rspec-mocks (3.6.0) + rspec-support (~> 3.7.0) + rspec-mocks (3.7.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.6.0) - rspec-rails (3.6.1) + rspec-support (~> 3.7.0) + rspec-rails (3.7.1) actionpack (>= 3.0) activesupport (>= 3.0) railties (>= 3.0) - rspec-core (~> 3.6.0) - rspec-expectations (~> 3.6.0) - rspec-mocks (~> 3.6.0) - rspec-support (~> 3.6.0) + rspec-core (~> 3.7.0) + rspec-expectations (~> 3.7.0) + rspec-mocks (~> 3.7.0) + rspec-support (~> 3.7.0) rspec-sidekiq (3.0.3) rspec-core (~> 3.0, >= 3.0.0) sidekiq (>= 2.4.0) - rspec-support (3.6.0) - rubocop (0.50.0) + rspec-support (3.7.0) + rubocop (0.51.0) parallel (~> 1.10) parser (>= 2.3.3.1, < 3.0) powerpack (~> 0.1) @@ -439,7 +441,7 @@ GEM ruby-progressbar (~> 1.7) unicode-display_width (~> 1.0, >= 1.0.1) ruby-oembed (0.12.0) - ruby-progressbar (1.8.3) + ruby-progressbar (1.9.0) rufus-scheduler (3.4.2) et-orbi (~> 1.0) safe_yaml (1.0.4) @@ -448,19 +450,19 @@ GEM nokogiri (>= 1.4.4) nokogumbo (~> 1.4.1) sass (3.4.25) - scss_lint (0.54.0) + scss_lint (0.55.0) rake (>= 0.9, < 13) sass (~> 3.4.20) - sidekiq (5.0.4) + sidekiq (5.0.5) concurrent-ruby (~> 1.0) connection_pool (~> 2.2, >= 2.2.0) rack-protection (>= 1.5.0) - redis (~> 3.3, >= 3.3.3) + redis (>= 3.3.4, < 5) sidekiq-bulk (0.1.1) activesupport sidekiq - sidekiq-scheduler (2.1.9) - redis (~> 3) + sidekiq-scheduler (2.1.10) + redis (>= 3, < 5) rufus-scheduler (~> 3.2) sidekiq (>= 3) tilt (>= 1.4.0) @@ -477,7 +479,6 @@ GEM json (>= 1.8, < 3) simplecov-html (~> 0.10.0) simplecov-html (0.10.2) - slop (3.6.0) sprockets (3.7.1) concurrent-ruby (~> 1.0) rack (> 1, < 3) @@ -500,9 +501,9 @@ GEM tilt (2.0.8) twitter-text (1.14.7) unf (~> 0.1.0) - tzinfo (1.2.3) + tzinfo (1.2.4) thread_safe (~> 0.1) - tzinfo-data (1.2017.2) + tzinfo-data (1.2017.3) tzinfo (>= 1.0.0) uglifier (3.2.0) execjs (>= 0.3.0, < 3) @@ -517,7 +518,7 @@ GEM addressable (>= 2.3.6) crack (>= 0.3.2) hashdiff - webpacker (3.0.1) + webpacker (3.0.2) activesupport (>= 4.2) rack-proxy (>= 0.6.1) railties (>= 4.2) @@ -538,19 +539,18 @@ DEPENDENCIES active_record_query_trace (~> 1.5) addressable (~> 2.5) annotate (~> 2.7) - aws-sdk (~> 2.9) - better_errors (~> 2.1) + better_errors (~> 2.4) binding_of_caller (~> 0.7) bootsnap brakeman (~> 4.0) browser bullet (~> 5.5) bundler-audit (~> 0.6) - capistrano (~> 3.8) - capistrano-rails (~> 1.2) + capistrano (~> 3.10) + capistrano-rails (~> 1.3) capistrano-rbenv (~> 2.1) capistrano-yarn (~> 2.0) - capybara (~> 2.14) + capybara (~> 2.15) charlock_holmes (~> 0.7.5) cld3 (~> 3.2.0) climate_control (~> 0.2) @@ -558,9 +558,12 @@ DEPENDENCIES devise-two-factor (~> 3.0) doorkeeper (~> 4.2) dotenv-rails (~> 2.2) - fabrication (~> 2.16) + fabrication (~> 2.18) faker (~> 1.7) fast_blank (~> 1.0) + fog-aws (~> 1.4) + fog-core (~> 1.45) + fog-local (~> 0.4) fog-openstack (~> 0.1) fuubar (~> 2.2) goldfinger (~> 2.0) @@ -574,22 +577,22 @@ DEPENDENCIES idn-ruby iso-639 json-ld-preloaded (~> 2.2.1) - kaminari (~> 1.0) + kaminari (~> 1.1) letter_opener (~> 1.4) letter_opener_web (~> 1.3) link_header (~> 0.0) - lograge (~> 0.5) + lograge (~> 0.7) mario-redis-lock (~> 1.2) microformats (~> 4.0) mime-types (~> 3.1) - nokogiri (~> 1.7) + nokogiri (~> 1.8) nsa (~> 0.2) - oj (~> 3.0) + oj (~> 3.3) ostatus2 (~> 2.0) - ox (~> 2.5) + ox (~> 2.8) paperclip (~> 5.1) paperclip-av-transcoder (~> 0.6) - parallel_tests (~> 2.14) + parallel_tests (~> 2.17) pg (~> 0.20) pghero (~> 1.7) pkg-config (~> 1.2) @@ -609,12 +612,12 @@ DEPENDENCIES redis-namespace (~> 1.5) redis-rails (~> 5.0) rqrcode (~> 0.10) - rspec-rails (~> 3.6) + rspec-rails (~> 3.7) rspec-sidekiq (~> 3.0) rubocop ruby-oembed (~> 0.12) sanitize (~> 4.4) - scss_lint (~> 0.53) + scss_lint (~> 0.55) sidekiq (~> 5.0) sidekiq-bulk (~> 0.1.1) sidekiq-scheduler (~> 2.1) diff --git a/app/controllers/admin/account_moderation_notes_controller.rb b/app/controllers/admin/account_moderation_notes_controller.rb index 414a875d0..7f69a3363 100644 --- a/app/controllers/admin/account_moderation_notes_controller.rb +++ b/app/controllers/admin/account_moderation_notes_controller.rb @@ -1,31 +1,41 @@ # frozen_string_literal: true -class Admin::AccountModerationNotesController < Admin::BaseController - def create - @account_moderation_note = current_account.account_moderation_notes.new(resource_params) - if @account_moderation_note.save - @target_account = @account_moderation_note.target_account - redirect_to admin_account_path(@target_account.id), notice: I18n.t('admin.account_moderation_notes.created_msg') - else - @account = @account_moderation_note.target_account - @moderation_notes = @account.targeted_moderation_notes.latest - render template: 'admin/accounts/show' +module Admin + class AccountModerationNotesController < BaseController + before_action :set_account_moderation_note, only: [:destroy] + + def create + authorize AccountModerationNote, :create? + + @account_moderation_note = current_account.account_moderation_notes.new(resource_params) + + if @account_moderation_note.save + redirect_to admin_account_path(@account_moderation_note.target_account_id), notice: I18n.t('admin.account_moderation_notes.created_msg') + else + @account = @account_moderation_note.target_account + @moderation_notes = @account.targeted_moderation_notes.latest + + render template: 'admin/accounts/show' + end + end + + def destroy + authorize @account_moderation_note, :destroy? + @account_moderation_note.destroy + redirect_to admin_account_path(@account_moderation_note.target_account_id), notice: I18n.t('admin.account_moderation_notes.destroyed_msg') + end + + private + + def resource_params + params.require(:account_moderation_note).permit( + :content, + :target_account_id + ) + end + + def set_account_moderation_note + @account_moderation_note = AccountModerationNote.find(params[:id]) end end - - def destroy - @account_moderation_note = AccountModerationNote.find(params[:id]) - @target_account = @account_moderation_note.target_account - @account_moderation_note.destroy - redirect_to admin_account_path(@target_account.id), notice: I18n.t('admin.account_moderation_notes.destroyed_msg') - end - - private - - def resource_params - params.require(:account_moderation_note).permit( - :content, - :target_account_id - ) - end end diff --git a/app/controllers/admin/accounts_controller.rb b/app/controllers/admin/accounts_controller.rb index ffa4dc850..0829bc769 100644 --- a/app/controllers/admin/accounts_controller.rb +++ b/app/controllers/admin/accounts_controller.rb @@ -2,29 +2,54 @@ module Admin class AccountsController < BaseController - before_action :set_account, only: [:show, :subscribe, :unsubscribe, :redownload] + before_action :set_account, only: [:show, :subscribe, :unsubscribe, :redownload, :enable, :disable, :memorialize] before_action :require_remote_account!, only: [:subscribe, :unsubscribe, :redownload] + before_action :require_local_account!, only: [:enable, :disable, :memorialize] def index + authorize :account, :index? @accounts = filtered_accounts.page(params[:page]) end def show + authorize @account, :show? @account_moderation_note = current_account.account_moderation_notes.new(target_account: @account) @moderation_notes = @account.targeted_moderation_notes.latest end def subscribe + authorize @account, :subscribe? Pubsubhubbub::SubscribeWorker.perform_async(@account.id) redirect_to admin_account_path(@account.id) end def unsubscribe + authorize @account, :unsubscribe? Pubsubhubbub::UnsubscribeWorker.perform_async(@account.id) redirect_to admin_account_path(@account.id) end + def memorialize + authorize @account, :memorialize? + @account.memorialize! + redirect_to admin_account_path(@account.id) + end + + def enable + authorize @account.user, :enable? + @account.user.enable! + redirect_to admin_account_path(@account.id) + end + + def disable + authorize @account.user, :disable? + @account.user.disable! + redirect_to admin_account_path(@account.id) + end + def redownload + authorize @account, :redownload? + @account.reset_avatar! @account.reset_header! @account.save! @@ -42,6 +67,10 @@ module Admin redirect_to admin_account_path(@account.id) if @account.local? end + def require_local_account! + redirect_to admin_account_path(@account.id) unless @account.local? && @account.user.present? + end + def filtered_accounts AccountFilter.new(filter_params).results end diff --git a/app/controllers/admin/base_controller.rb b/app/controllers/admin/base_controller.rb index 11fe326bc..db4839a8f 100644 --- a/app/controllers/admin/base_controller.rb +++ b/app/controllers/admin/base_controller.rb @@ -2,7 +2,9 @@ module Admin class BaseController < ApplicationController - before_action :require_admin! + include Authorization + + before_action :require_staff! layout 'admin' end diff --git a/app/controllers/admin/confirmations_controller.rb b/app/controllers/admin/confirmations_controller.rb index 2542e21ee..c10b0ebee 100644 --- a/app/controllers/admin/confirmations_controller.rb +++ b/app/controllers/admin/confirmations_controller.rb @@ -2,15 +2,18 @@ module Admin class ConfirmationsController < BaseController + before_action :set_user + def create - account_user.confirm + authorize @user, :confirm? + @user.confirm! redirect_to admin_accounts_path end private - def account_user - Account.find(params[:account_id]).user || raise(ActiveRecord::RecordNotFound) + def set_user + @user = Account.find(params[:account_id]).user || raise(ActiveRecord::RecordNotFound) end end end diff --git a/app/controllers/admin/custom_emojis_controller.rb b/app/controllers/admin/custom_emojis_controller.rb index cbd7abe95..693d28b1f 100644 --- a/app/controllers/admin/custom_emojis_controller.rb +++ b/app/controllers/admin/custom_emojis_controller.rb @@ -5,14 +5,18 @@ module Admin before_action :set_custom_emoji, except: [:index, :new, :create] def index - @custom_emojis = filtered_custom_emojis.page(params[:page]) + authorize :custom_emoji, :index? + @custom_emojis = filtered_custom_emojis.eager_load(:local_counterpart).page(params[:page]) end def new + authorize :custom_emoji, :create? @custom_emoji = CustomEmoji.new end def create + authorize :custom_emoji, :create? + @custom_emoji = CustomEmoji.new(resource_params) if @custom_emoji.save @@ -23,6 +27,8 @@ module Admin end def update + authorize @custom_emoji, :update? + if @custom_emoji.update(resource_params) redirect_to admin_custom_emojis_path, notice: I18n.t('admin.custom_emojis.updated_msg') else @@ -31,14 +37,17 @@ module Admin end def destroy + authorize @custom_emoji, :destroy? @custom_emoji.destroy redirect_to admin_custom_emojis_path, notice: I18n.t('admin.custom_emojis.destroyed_msg') end def copy - emoji = CustomEmoji.new(domain: nil, shortcode: @custom_emoji.shortcode, image: @custom_emoji.image) + authorize @custom_emoji, :copy? - if emoji.save + emoji = CustomEmoji.find_or_create_by(domain: nil, shortcode: @custom_emoji.shortcode) + + if emoji.update(image: @custom_emoji.image) flash[:notice] = I18n.t('admin.custom_emojis.copied_msg') else flash[:alert] = I18n.t('admin.custom_emojis.copy_failed_msg') @@ -48,11 +57,13 @@ module Admin end def enable + authorize @custom_emoji, :enable? @custom_emoji.update!(disabled: false) redirect_to admin_custom_emojis_path, notice: I18n.t('admin.custom_emojis.enabled_msg') end def disable + authorize @custom_emoji, :disable? @custom_emoji.update!(disabled: true) redirect_to admin_custom_emojis_path, notice: I18n.t('admin.custom_emojis.disabled_msg') end diff --git a/app/controllers/admin/domain_blocks_controller.rb b/app/controllers/admin/domain_blocks_controller.rb index 1ab620e03..e383dc831 100644 --- a/app/controllers/admin/domain_blocks_controller.rb +++ b/app/controllers/admin/domain_blocks_controller.rb @@ -5,14 +5,18 @@ module Admin before_action :set_domain_block, only: [:show, :destroy] def index + authorize :domain_block, :index? @domain_blocks = DomainBlock.page(params[:page]) end def new + authorize :domain_block, :create? @domain_block = DomainBlock.new end def create + authorize :domain_block, :create? + @domain_block = DomainBlock.new(resource_params) if @domain_block.save @@ -23,9 +27,12 @@ module Admin end end - def show; end + def show + authorize @domain_block, :show? + end def destroy + authorize @domain_block, :destroy? UnblockDomainService.new.call(@domain_block, retroactive_unblock?) redirect_to admin_domain_blocks_path, notice: I18n.t('admin.domain_blocks.destroyed_msg') end diff --git a/app/controllers/admin/email_domain_blocks_controller.rb b/app/controllers/admin/email_domain_blocks_controller.rb index 09275d5dc..01058bf46 100644 --- a/app/controllers/admin/email_domain_blocks_controller.rb +++ b/app/controllers/admin/email_domain_blocks_controller.rb @@ -5,14 +5,18 @@ module Admin before_action :set_email_domain_block, only: [:show, :destroy] def index + authorize :email_domain_block, :index? @email_domain_blocks = EmailDomainBlock.page(params[:page]) end def new + authorize :email_domain_block, :create? @email_domain_block = EmailDomainBlock.new end def create + authorize :email_domain_block, :create? + @email_domain_block = EmailDomainBlock.new(resource_params) if @email_domain_block.save @@ -23,6 +27,7 @@ module Admin end def destroy + authorize @email_domain_block, :destroy? @email_domain_block.destroy redirect_to admin_email_domain_blocks_path, notice: I18n.t('admin.email_domain_blocks.destroyed_msg') end diff --git a/app/controllers/admin/instances_controller.rb b/app/controllers/admin/instances_controller.rb index 22f02e5d0..8ed0ea421 100644 --- a/app/controllers/admin/instances_controller.rb +++ b/app/controllers/admin/instances_controller.rb @@ -3,10 +3,12 @@ module Admin class InstancesController < BaseController def index + authorize :instance, :index? @instances = ordered_instances end def resubscribe + authorize :instance, :resubscribe? params.require(:by_domain) Pubsubhubbub::SubscribeWorker.push_bulk(subscribeable_accounts.pluck(:id)) redirect_to admin_instances_path diff --git a/app/controllers/admin/reported_statuses_controller.rb b/app/controllers/admin/reported_statuses_controller.rb index 5a31adecf..4f66ce708 100644 --- a/app/controllers/admin/reported_statuses_controller.rb +++ b/app/controllers/admin/reported_statuses_controller.rb @@ -2,19 +2,20 @@ module Admin class ReportedStatusesController < BaseController - include Authorization - before_action :set_report before_action :set_status, only: [:update, :destroy] def create - @form = Form::StatusBatch.new(form_status_batch_params) - flash[:alert] = t('admin.statuses.failed_to_execute') unless @form.save + authorize :status, :update? + + @form = Form::StatusBatch.new(form_status_batch_params) + flash[:alert] = I18n.t('admin.statuses.failed_to_execute') unless @form.save redirect_to admin_report_path(@report) end def update + authorize @status, :update? @status.update(status_params) redirect_to admin_report_path(@report) end diff --git a/app/controllers/admin/reports_controller.rb b/app/controllers/admin/reports_controller.rb index 226467739..745757ee8 100644 --- a/app/controllers/admin/reports_controller.rb +++ b/app/controllers/admin/reports_controller.rb @@ -5,14 +5,17 @@ module Admin before_action :set_report, except: [:index] def index + authorize :report, :index? @reports = filtered_reports.page(params[:page]) end def show + authorize @report, :show? @form = Form::StatusBatch.new end def update + authorize @report, :update? process_report redirect_to admin_report_path(@report) end diff --git a/app/controllers/admin/resets_controller.rb b/app/controllers/admin/resets_controller.rb index 6db648403..00b590bf6 100644 --- a/app/controllers/admin/resets_controller.rb +++ b/app/controllers/admin/resets_controller.rb @@ -2,17 +2,18 @@ module Admin class ResetsController < BaseController - before_action :set_account + before_action :set_user def create - @account.user.send_reset_password_instructions + authorize @user, :reset_password? + @user.send_reset_password_instructions redirect_to admin_accounts_path end private - def set_account - @account = Account.find(params[:account_id]) + def set_user + @user = Account.find(params[:account_id]).user || raise(ActiveRecord::RecordNotFound) end end end diff --git a/app/controllers/admin/roles_controller.rb b/app/controllers/admin/roles_controller.rb new file mode 100644 index 000000000..8f8685827 --- /dev/null +++ b/app/controllers/admin/roles_controller.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Admin + class RolesController < BaseController + before_action :set_user + + def promote + authorize @user, :promote? + @user.promote! + redirect_to admin_account_path(@user.account_id) + end + + def demote + authorize @user, :demote? + @user.demote! + redirect_to admin_account_path(@user.account_id) + end + + private + + def set_user + @user = Account.find(params[:account_id]).user || raise(ActiveRecord::RecordNotFound) + end + end +end diff --git a/app/controllers/admin/settings_controller.rb b/app/controllers/admin/settings_controller.rb index a2f86b8a9..e81290228 100644 --- a/app/controllers/admin/settings_controller.rb +++ b/app/controllers/admin/settings_controller.rb @@ -28,10 +28,13 @@ module Admin ).freeze def edit + authorize :settings, :show? @admin_settings = Form::AdminSettings.new end def update + authorize :settings, :update? + settings_params.each do |key, value| if UPLOAD_SETTINGS.include?(key) upload = SiteUpload.where(var: key).first_or_initialize(var: key) diff --git a/app/controllers/admin/silences_controller.rb b/app/controllers/admin/silences_controller.rb index 81a3008b9..01fb292de 100644 --- a/app/controllers/admin/silences_controller.rb +++ b/app/controllers/admin/silences_controller.rb @@ -5,11 +5,13 @@ module Admin before_action :set_account def create + authorize @account, :silence? @account.update(silenced: true) redirect_to admin_accounts_path end def destroy + authorize @account, :unsilence? @account.update(silenced: false) redirect_to admin_accounts_path end diff --git a/app/controllers/admin/statuses_controller.rb b/app/controllers/admin/statuses_controller.rb index b05000b16..b54a9b824 100644 --- a/app/controllers/admin/statuses_controller.rb +++ b/app/controllers/admin/statuses_controller.rb @@ -2,8 +2,6 @@ module Admin class StatusesController < BaseController - include Authorization - helper_method :current_params before_action :set_account @@ -12,24 +10,30 @@ module Admin PER_PAGE = 20 def index + authorize :status, :index? + @statuses = @account.statuses + if params[:media] account_media_status_ids = @account.media_attachments.attached.reorder(nil).select(:status_id).distinct @statuses.merge!(Status.where(id: account_media_status_ids)) end - @statuses = @statuses.preload(:media_attachments, :mentions).page(params[:page]).per(PER_PAGE) - @form = Form::StatusBatch.new + @statuses = @statuses.preload(:media_attachments, :mentions).page(params[:page]).per(PER_PAGE) + @form = Form::StatusBatch.new end def create - @form = Form::StatusBatch.new(form_status_batch_params) - flash[:alert] = t('admin.statuses.failed_to_execute') unless @form.save + authorize :status, :update? + + @form = Form::StatusBatch.new(form_status_batch_params) + flash[:alert] = I18n.t('admin.statuses.failed_to_execute') unless @form.save redirect_to admin_account_statuses_path(@account.id, current_params) end def update + authorize @status, :update? @status.update(status_params) redirect_to admin_account_statuses_path(@account.id, current_params) end @@ -60,6 +64,7 @@ module Admin def current_params page = (params[:page] || 1).to_i + { media: params[:media], page: page > 1 && page, diff --git a/app/controllers/admin/subscriptions_controller.rb b/app/controllers/admin/subscriptions_controller.rb index 624a475a3..40500ef43 100644 --- a/app/controllers/admin/subscriptions_controller.rb +++ b/app/controllers/admin/subscriptions_controller.rb @@ -3,6 +3,7 @@ module Admin class SubscriptionsController < BaseController def index + authorize :subscription, :index? @subscriptions = ordered_subscriptions.page(requested_page) end diff --git a/app/controllers/admin/suspensions_controller.rb b/app/controllers/admin/suspensions_controller.rb index 5d9048d94..778feea5e 100644 --- a/app/controllers/admin/suspensions_controller.rb +++ b/app/controllers/admin/suspensions_controller.rb @@ -5,12 +5,14 @@ module Admin before_action :set_account def create + authorize @account, :suspend? Admin::SuspensionWorker.perform_async(@account.id) redirect_to admin_accounts_path end def destroy - @account.update(suspended: false) + authorize @account, :unsuspend? + @account.unsuspend! redirect_to admin_accounts_path end diff --git a/app/controllers/admin/two_factor_authentications_controller.rb b/app/controllers/admin/two_factor_authentications_controller.rb index 69c08f605..5a45d25cd 100644 --- a/app/controllers/admin/two_factor_authentications_controller.rb +++ b/app/controllers/admin/two_factor_authentications_controller.rb @@ -5,6 +5,7 @@ module Admin before_action :set_user def destroy + authorize @user, :disable_2fa? @user.disable_two_factor! redirect_to admin_accounts_path end diff --git a/app/controllers/api/v1/reports_controller.rb b/app/controllers/api/v1/reports_controller.rb index 9592cd4bd..22828217d 100644 --- a/app/controllers/api/v1/reports_controller.rb +++ b/app/controllers/api/v1/reports_controller.rb @@ -19,7 +19,7 @@ class Api::V1::ReportsController < Api::BaseController comment: report_params[:comment] ) - User.admins.includes(:account).each { |u| AdminMailer.new_report(u.account, @report).deliver_later } + User.staff.includes(:account).each { |u| AdminMailer.new_report(u.account, @report).deliver_later } render json: @report, serializer: REST::ReportSerializer end diff --git a/app/controllers/api/v1/search_controller.rb b/app/controllers/api/v1/search_controller.rb index e183a71d7..d1b4e0402 100644 --- a/app/controllers/api/v1/search_controller.rb +++ b/app/controllers/api/v1/search_controller.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class Api::V1::SearchController < Api::BaseController + include Authorization + RESULTS_LIMIT = 10 before_action -> { doorkeeper_authorize! :read } @@ -9,12 +11,24 @@ class Api::V1::SearchController < Api::BaseController respond_to :json def index - @search = Search.new(search_results) + @search = Search.new(search) render json: @search, serializer: REST::SearchSerializer end private + def search + search_results.tap do |search| + search[:statuses].keep_if do |status| + begin + authorize status, :show? + rescue Mastodon::NotPermittedError + false + end + end + end + end + def search_results SearchService.new.call( params[:q], diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index d5eca6ffb..f41a7f9be 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -18,6 +18,7 @@ class ApplicationController < ActionController::Base rescue_from ActionController::RoutingError, with: :not_found rescue_from ActiveRecord::RecordNotFound, with: :not_found rescue_from ActionController::InvalidAuthenticityToken, with: :unprocessable_entity + rescue_from Mastodon::NotPermittedError, with: :forbidden before_action :store_current_location, except: :raise_not_found, unless: :devise_controller? before_action :check_suspension, if: :user_signed_in? @@ -40,6 +41,10 @@ class ApplicationController < ActionController::Base redirect_to root_path unless current_user&.admin? end + def require_staff! + redirect_to root_path unless current_user&.staff? + end + def check_suspension forbidden if current_user.account.suspended? end diff --git a/app/controllers/concerns/authorization.rb b/app/controllers/concerns/authorization.rb index 7828fe48d..95a37e379 100644 --- a/app/controllers/concerns/authorization.rb +++ b/app/controllers/concerns/authorization.rb @@ -2,6 +2,7 @@ module Authorization extend ActiveSupport::Concern + include Pundit def pundit_user diff --git a/app/controllers/settings/notifications_controller.rb b/app/controllers/settings/notifications_controller.rb index 09839f16e..ce2530c54 100644 --- a/app/controllers/settings/notifications_controller.rb +++ b/app/controllers/settings/notifications_controller.rb @@ -26,7 +26,7 @@ class Settings::NotificationsController < ApplicationController def user_settings_params params.require(:user).permit( notification_emails: %i(follow follow_request reblog favourite mention digest), - interactions: %i(must_be_follower must_be_following) + interactions: %i(must_be_follower must_be_following must_be_following_dm) ) end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 6d625e7db..7dfab1df1 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -35,6 +35,11 @@ module ApplicationHelper Rails.env.production? ? site_title : "#{site_title} (Dev)" end + def can?(action, record) + return false if record.nil? + policy(record).public_send("#{action}?") + end + def fa_icon(icon, attributes = {}) class_names = attributes[:class]&.split(' ') || [] class_names << 'fa' @@ -43,6 +48,10 @@ module ApplicationHelper content_tag(:i, nil, attributes.merge(class: class_names.join(' '))) end + def custom_emoji_tag(custom_emoji) + image_tag(custom_emoji.image.url, class: 'emojione', alt: ":#{custom_emoji.shortcode}:") + end + def opengraph(property, content) tag(:meta, content: content, property: property) end diff --git a/app/javascript/glitch/components/account/header.js b/app/javascript/glitch/components/account/header.js index c94fb0851..7bc1a2189 100644 --- a/app/javascript/glitch/components/account/header.js +++ b/app/javascript/glitch/components/account/header.js @@ -51,6 +51,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component'; import emojify from '../../../mastodon/features/emoji/emoji'; import IconButton from '../../../mastodon/components/icon_button'; import Avatar from '../../../mastodon/components/avatar'; +import { me } from '../../../mastodon/initial_state'; // Our imports // import { processBio } from '../../util/bio_metadata'; @@ -88,7 +89,6 @@ export default class AccountHeader extends ImmutablePureComponent { static propTypes = { account : ImmutablePropTypes.map, - me : PropTypes.string.isRequired, onFollow : PropTypes.func.isRequired, intl : PropTypes.object.isRequired, }; @@ -102,7 +102,7 @@ The `render()` function is used to render our component. */ render () { - const { account, me, intl } = this.props; + const { account, intl } = this.props; /* diff --git a/app/javascript/glitch/components/status/action_bar.js b/app/javascript/glitch/components/status/action_bar.js index f4450d31b..34588b008 100644 --- a/app/javascript/glitch/components/status/action_bar.js +++ b/app/javascript/glitch/components/status/action_bar.js @@ -9,6 +9,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component'; import RelativeTimestamp from '../../../mastodon/components/relative_timestamp'; import IconButton from '../../../mastodon/components/icon_button'; import DropdownMenuContainer from '../../../mastodon/containers/dropdown_menu_container'; +import { me } from '../../../mastodon/initial_state'; const messages = defineMessages({ delete: { id: 'status.delete', defaultMessage: 'Delete' }, @@ -50,7 +51,6 @@ export default class StatusActionBar extends ImmutablePureComponent { onEmbed: PropTypes.func, onMuteConversation: PropTypes.func, onPin: PropTypes.func, - me: PropTypes.string, withDismiss: PropTypes.bool, intl: PropTypes.object.isRequired, }; @@ -59,7 +59,6 @@ export default class StatusActionBar extends ImmutablePureComponent { // evaluate to false. See react-immutable-pure-component for usage. updateOnProps = [ 'status', - 'me', 'withDismiss', ] @@ -119,7 +118,7 @@ export default class StatusActionBar extends ImmutablePureComponent { } render () { - const { status, me, intl, withDismiss } = this.props; + const { status, intl, withDismiss } = this.props; const mutingConversation = status.get('muted'); const anonymousAccess = !me; diff --git a/app/javascript/glitch/components/status/container.js b/app/javascript/glitch/components/status/container.js index 24261e763..0054abd14 100644 --- a/app/javascript/glitch/components/status/container.js +++ b/app/javascript/glitch/components/status/container.js @@ -140,12 +140,10 @@ Here are the props we pass to ``. return { status : status, account : account || ownProps.account, - me : state.getIn(['meta', 'me']), settings : state.get('local_settings'), prepend : prepend || ownProps.prepend, reblogModal : state.getIn(['meta', 'boost_modal']), deleteModal : state.getIn(['meta', 'delete_modal']), - autoPlayGif : state.getIn(['meta', 'auto_play_gif']), }; }; diff --git a/app/javascript/glitch/components/status/index.js b/app/javascript/glitch/components/status/index.js index 6bd95b051..33a9730e5 100644 --- a/app/javascript/glitch/components/status/index.js +++ b/app/javascript/glitch/components/status/index.js @@ -39,6 +39,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component'; // Mastodon imports // import scheduleIdleTask from '../../../mastodon/features/ui/util/schedule_idle_task'; +import { autoPlayGif } from '../../../mastodon/initial_state'; // Our imports // import StatusPrepend from './prepend'; @@ -89,9 +90,6 @@ few parts: These are our local settings, fetched from our store. We need this to determine how best to collapse our statuses, among other things. - - __`me` (`PropTypes.number`) :__ - This is the id of the currently-signed-in user. - - __`onFavourite`, `onReblog`, `onModalReblog`, `onDelete`, `onMention`, `onMute`, `onMuteConversation`, onBlock`, `onReport`, `onOpenMedia`, `onOpenVideo` (`PropTypes.func`) :__ @@ -103,9 +101,6 @@ few parts: reblogging and deleting statuses. They are used by the `onReblog` and `onDelete` functions, but we don't deal with them here. - - __`autoPlayGif` (`PropTypes.bool`) :__ - This tells the frontend whether or not to autoplay gifs! - - __`muted` (`PropTypes.bool`) :__ This has nothing to do with a user or conversation mute! "Muted" is what Mastodon internally calls the subdued look of statuses in the @@ -160,7 +155,6 @@ export default class Status extends ImmutablePureComponent { account : ImmutablePropTypes.map, settings : ImmutablePropTypes.map, notification : ImmutablePropTypes.map, - me : PropTypes.string, onFavourite : PropTypes.func, onReblog : PropTypes.func, onModalReblog : PropTypes.func, @@ -177,7 +171,6 @@ export default class Status extends ImmutablePureComponent { onOpenVideo : PropTypes.func, reblogModal : PropTypes.bool, deleteModal : PropTypes.bool, - autoPlayGif : PropTypes.bool, muted : PropTypes.bool, collapse : PropTypes.bool, prepend : PropTypes.string, @@ -211,9 +204,7 @@ to remember to specify it here. 'account', 'settings', 'prepend', - 'me', 'boostModal', - 'autoPlayGif', 'muted', 'collapse', 'notification', @@ -560,7 +551,6 @@ this operation are further explained in the code below. intersectionObserverWrapper, onOpenVideo, onOpenMedia, - autoPlayGif, notification, ...other } = this.props; diff --git a/app/javascript/mastodon/actions/pin_statuses.js b/app/javascript/mastodon/actions/pin_statuses.js index 01bf8930b..3f40f6c2d 100644 --- a/app/javascript/mastodon/actions/pin_statuses.js +++ b/app/javascript/mastodon/actions/pin_statuses.js @@ -4,12 +4,13 @@ export const PINNED_STATUSES_FETCH_REQUEST = 'PINNED_STATUSES_FETCH_REQUEST'; export const PINNED_STATUSES_FETCH_SUCCESS = 'PINNED_STATUSES_FETCH_SUCCESS'; export const PINNED_STATUSES_FETCH_FAIL = 'PINNED_STATUSES_FETCH_FAIL'; +import { me } from '../initial_state'; + export function fetchPinnedStatuses() { return (dispatch, getState) => { dispatch(fetchPinnedStatusesRequest()); - const accountId = getState().getIn(['meta', 'me']); - api(getState).get(`/api/v1/accounts/${accountId}/statuses`, { params: { pinned: true } }).then(response => { + api(getState).get(`/api/v1/accounts/${me}/statuses`, { params: { pinned: true } }).then(response => { dispatch(fetchPinnedStatusesSuccess(response.data, null)); }).catch(error => { dispatch(fetchPinnedStatusesFail(error)); diff --git a/app/javascript/mastodon/actions/streaming.js b/app/javascript/mastodon/actions/streaming.js index a2e25c930..e60ddacd9 100644 --- a/app/javascript/mastodon/actions/streaming.js +++ b/app/javascript/mastodon/actions/streaming.js @@ -1,4 +1,4 @@ -import createStream from '../stream'; +import { connectStream } from '../stream'; import { updateTimeline, deleteFromTimelines, @@ -12,42 +12,19 @@ import { getLocale } from '../locales'; const { messages } = getLocale(); export function connectTimelineStream (timelineId, path, pollingRefresh = null) { - return (dispatch, getState) => { - const streamingAPIBaseURL = getState().getIn(['meta', 'streaming_api_base_url']); - const accessToken = getState().getIn(['meta', 'access_token']); + + return connectStream (path, pollingRefresh, (dispatch, getState) => { const locale = getState().getIn(['meta', 'locale']); - let polling = null; - - const setupPolling = () => { - polling = setInterval(() => { - pollingRefresh(dispatch); - }, 20000); - }; - - const clearPolling = () => { - if (polling) { - clearInterval(polling); - polling = null; - } - }; - - const subscription = createStream(streamingAPIBaseURL, accessToken, path, { - - connected () { - if (pollingRefresh) { - clearPolling(); - } + return { + onConnect() { dispatch(connectTimeline(timelineId)); }, - disconnected () { - if (pollingRefresh) { - setupPolling(); - } + onDisconnect() { dispatch(disconnectTimeline(timelineId)); }, - received (data) { + onReceive (data) { switch(data.event) { case 'update': dispatch(updateTimeline(timelineId, JSON.parse(data.payload))); @@ -60,26 +37,8 @@ export function connectTimelineStream (timelineId, path, pollingRefresh = null) break; } }, - - reconnected () { - if (pollingRefresh) { - clearPolling(); - pollingRefresh(dispatch); - } - dispatch(connectTimeline(timelineId)); - }, - - }); - - const disconnect = () => { - if (subscription) { - subscription.close(); - } - clearPolling(); }; - - return disconnect; - }; + }); } function refreshHomeTimelineAndNotification (dispatch) { diff --git a/app/javascript/mastodon/components/account.js b/app/javascript/mastodon/components/account.js index 376e544fb..2c3a00064 100644 --- a/app/javascript/mastodon/components/account.js +++ b/app/javascript/mastodon/components/account.js @@ -7,6 +7,7 @@ import Permalink from './permalink'; import IconButton from './icon_button'; import { defineMessages, injectIntl } from 'react-intl'; import ImmutablePureComponent from 'react-immutable-pure-component'; +import { me } from '../initial_state'; const messages = defineMessages({ follow: { id: 'account.follow', defaultMessage: 'Follow' }, @@ -23,7 +24,6 @@ export default class Account extends ImmutablePureComponent { static propTypes = { account: ImmutablePropTypes.map.isRequired, - me: PropTypes.string.isRequired, onFollow: PropTypes.func.isRequired, onBlock: PropTypes.func.isRequired, onMute: PropTypes.func.isRequired, @@ -52,7 +52,7 @@ export default class Account extends ImmutablePureComponent { } render () { - const { account, me, intl, hidden } = this.props; + const { account, intl, hidden } = this.props; if (!account) { return
; @@ -82,7 +82,7 @@ export default class Account extends ImmutablePureComponent { } else if (muting) { let hidingNotificationsButton; if (muting.get('notifications')) { - hidingNotificationsButton = ; + hidingNotificationsButton = ; } else { hidingNotificationsButton = ; } diff --git a/app/javascript/mastodon/components/icon_button.js b/app/javascript/mastodon/components/icon_button.js index 76b0da12f..d0c1b049f 100644 --- a/app/javascript/mastodon/components/icon_button.js +++ b/app/javascript/mastodon/components/icon_button.js @@ -65,6 +65,7 @@ export default class IconButton extends React.PureComponent { expanded, icon, inverted, + flip, overlay, pressed, tabIndex, @@ -78,8 +79,8 @@ export default class IconButton extends React.PureComponent { overlayed: overlay, }); - const flipDeg = this.props.flip ? -180 : -360; - const rotateDeg = this.props.active ? flipDeg : 0; + const flipDeg = flip ? -180 : -360; + const rotateDeg = active ? flipDeg : 0; const motionDefaultStyle = { rotate: rotateDeg, @@ -93,6 +94,25 @@ export default class IconButton extends React.PureComponent { rotate: animate ? spring(rotateDeg, springOpts) : 0, }; + if (!animate) { + // Perf optimization: avoid unnecessary components unless + // we actually need to animate. + return ( +