Merge pull request #2122 from ClearlyClaire/glitch-soc/merge-upstream
Merge upstream changes
This commit is contained in:
commit
6a0ed45aa3
225 changed files with 3216 additions and 725 deletions
|
@ -1,16 +1,14 @@
|
||||||
# [Choice] Ruby version (use -bullseye variants on local arm64/Apple Silicon): 3, 3.1, 3.0, 2, 2.7, 2.6, 3-bullseye, 3.1-bullseye, 3.0-bullseye, 2-bullseye, 2.7-bullseye, 2.6-bullseye, 3-buster, 3.1-buster, 3.0-buster, 2-buster, 2.7-buster, 2.6-buster
|
# For details, see https://github.com/devcontainers/images/tree/main/src/ruby
|
||||||
ARG VARIANT=3.1-bullseye
|
FROM mcr.microsoft.com/devcontainers/ruby:0-3.2-bullseye
|
||||||
FROM mcr.microsoft.com/vscode/devcontainers/ruby:${VARIANT}
|
|
||||||
|
|
||||||
# Install Rails
|
# Install Rails
|
||||||
# RUN gem install rails webdrivers
|
# RUN gem install rails webdrivers
|
||||||
|
|
||||||
# Default value to allow debug server to serve content over GitHub Codespace's port forwarding service
|
# Default value to allow debug server to serve content over GitHub Codespace's port forwarding service
|
||||||
# The value is a comma-separated list of allowed domains
|
# The value is a comma-separated list of allowed domains
|
||||||
ENV RAILS_DEVELOPMENT_HOSTS=".githubpreview.dev"
|
ENV RAILS_DEVELOPMENT_HOSTS=".githubpreview.dev,.preview.app.github.dev,.app.github.dev"
|
||||||
|
|
||||||
# [Choice] Node.js version: lts/*, 18, 16, 14
|
ARG NODE_VERSION="16"
|
||||||
ARG NODE_VERSION="lts/*"
|
|
||||||
RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"
|
RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"
|
||||||
|
|
||||||
# [Optional] Uncomment this section to install additional OS packages.
|
# [Optional] Uncomment this section to install additional OS packages.
|
||||||
|
@ -22,3 +20,5 @@ RUN gem install foreman
|
||||||
|
|
||||||
# [Optional] Uncomment this line to install global node packages.
|
# [Optional] Uncomment this line to install global node packages.
|
||||||
RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g yarn" 2>&1
|
RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g yarn" 2>&1
|
||||||
|
|
||||||
|
COPY welcome-message.txt /usr/local/etc/vscode-dev-containers/first-run-notice.txt
|
||||||
|
|
|
@ -1,30 +1,13 @@
|
||||||
|
// For more details, see https://aka.ms/devcontainer.json.
|
||||||
{
|
{
|
||||||
"name": "Mastodon",
|
"name": "Mastodon",
|
||||||
"dockerComposeFile": "docker-compose.yml",
|
"dockerComposeFile": "docker-compose.yml",
|
||||||
"service": "app",
|
"service": "app",
|
||||||
"workspaceFolder": "/mastodon",
|
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
|
||||||
|
|
||||||
// Configure tool-specific properties.
|
|
||||||
"customizations": {
|
|
||||||
// Configure properties specific to VS Code.
|
|
||||||
"vscode": {
|
|
||||||
// Set *default* container specific settings.json values on container create.
|
|
||||||
"settings": {},
|
|
||||||
|
|
||||||
// Add the IDs of extensions you want installed when the container is created.
|
|
||||||
"extensions": [
|
|
||||||
"EditorConfig.EditorConfig",
|
|
||||||
"dbaeumer.vscode-eslint",
|
|
||||||
"rebornix.Ruby",
|
|
||||||
"webben.browserslist"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
|
// Features to add to the dev container. More info: https://containers.dev/features.
|
||||||
"features": {
|
"features": {
|
||||||
"ghcr.io/devcontainers/features/sshd:1": {
|
"ghcr.io/devcontainers/features/sshd:1": {}
|
||||||
"version": "latest"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||||
|
@ -33,7 +16,16 @@
|
||||||
|
|
||||||
// Use 'postCreateCommand' to run commands after the container is created.
|
// Use 'postCreateCommand' to run commands after the container is created.
|
||||||
"postCreateCommand": ".devcontainer/post-create.sh",
|
"postCreateCommand": ".devcontainer/post-create.sh",
|
||||||
|
"waitFor": "postCreateCommand",
|
||||||
|
|
||||||
// Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
|
// Configure tool-specific properties.
|
||||||
"remoteUser": "vscode"
|
"customizations": {
|
||||||
|
// Configure properties specific to VS Code.
|
||||||
|
"vscode": {
|
||||||
|
// Set *default* container specific settings.json values on container create.
|
||||||
|
"settings": {},
|
||||||
|
// Add the IDs of extensions you want installed when the container is created.
|
||||||
|
"extensions": ["EditorConfig.EditorConfig", "webben.browserslist"]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,15 +5,8 @@ services:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
args:
|
|
||||||
# Update 'VARIANT' to pick a version of Ruby: 3, 3.1, 3.0, 2, 2.7, 2.6
|
|
||||||
# Append -bullseye or -buster to pin to an OS version.
|
|
||||||
# Use -bullseye variants on local arm64/Apple Silicon.
|
|
||||||
VARIANT: '3.0-bullseye'
|
|
||||||
# Optional Node.js version to install
|
|
||||||
NODE_VERSION: '16'
|
|
||||||
volumes:
|
volumes:
|
||||||
- ..:/mastodon:cached
|
- ../..:/workspaces:cached
|
||||||
environment:
|
environment:
|
||||||
RAILS_ENV: development
|
RAILS_ENV: development
|
||||||
NODE_ENV: development
|
NODE_ENV: development
|
||||||
|
@ -33,7 +26,6 @@ services:
|
||||||
networks:
|
networks:
|
||||||
- external_network
|
- external_network
|
||||||
- internal_network
|
- internal_network
|
||||||
user: vscode
|
|
||||||
|
|
||||||
db:
|
db:
|
||||||
image: postgres:14-alpine
|
image: postgres:14-alpine
|
||||||
|
|
8
.devcontainer/welcome-message.txt
Normal file
8
.devcontainer/welcome-message.txt
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
👋 Welcome to "Mastodon" in GitHub Codespaces!
|
||||||
|
|
||||||
|
🛠️ Your environment is fully setup with all the required software.
|
||||||
|
|
||||||
|
🔍 To explore VS Code to its fullest, search using the Command Palette (Cmd/Ctrl + Shift + P or F1).
|
||||||
|
|
||||||
|
📝 Edit away, run your app as usual, and we'll automatically make it available for you to access.
|
||||||
|
|
|
@ -19,7 +19,6 @@ AllCops:
|
||||||
NewCops: enable
|
NewCops: enable
|
||||||
Exclude:
|
Exclude:
|
||||||
- db/schema.rb
|
- db/schema.rb
|
||||||
- 'app/views/**/*'
|
|
||||||
- 'config/**/*'
|
- 'config/**/*'
|
||||||
- 'bin/*'
|
- 'bin/*'
|
||||||
- 'Rakefile'
|
- 'Rakefile'
|
||||||
|
@ -97,6 +96,10 @@ Rails/Exit:
|
||||||
- 'lib/mastodon/cli_helper.rb'
|
- 'lib/mastodon/cli_helper.rb'
|
||||||
- 'lib/cli.rb'
|
- 'lib/cli.rb'
|
||||||
|
|
||||||
|
RSpec/FilePath:
|
||||||
|
CustomTransform:
|
||||||
|
DeepL: deepl
|
||||||
|
|
||||||
RSpec/NotToNot:
|
RSpec/NotToNot:
|
||||||
EnforcedStyle: to_not
|
EnforcedStyle: to_not
|
||||||
|
|
||||||
|
@ -123,3 +126,6 @@ Style/TrailingCommaInArrayLiteral:
|
||||||
|
|
||||||
Style/TrailingCommaInHashLiteral:
|
Style/TrailingCommaInHashLiteral:
|
||||||
EnforcedStyleForMultiline: 'comma'
|
EnforcedStyleForMultiline: 'comma'
|
||||||
|
|
||||||
|
Style/SymbolArray:
|
||||||
|
Enabled: false
|
||||||
|
|
|
@ -2235,134 +2235,3 @@ Style/SlicingWithRange:
|
||||||
- 'lib/active_record/batches.rb'
|
- 'lib/active_record/batches.rb'
|
||||||
- 'lib/mastodon/premailer_webpack_strategy.rb'
|
- 'lib/mastodon/premailer_webpack_strategy.rb'
|
||||||
- 'lib/tasks/repo.rake'
|
- 'lib/tasks/repo.rake'
|
||||||
|
|
||||||
# Offense count: 272
|
|
||||||
# This cop supports safe autocorrection (--autocorrect).
|
|
||||||
# Configuration parameters: EnforcedStyle, MinSize.
|
|
||||||
# SupportedStyles: percent, brackets
|
|
||||||
Style/SymbolArray:
|
|
||||||
Exclude:
|
|
||||||
- 'app/controllers/accounts_controller.rb'
|
|
||||||
- 'app/controllers/activitypub/replies_controller.rb'
|
|
||||||
- 'app/controllers/admin/accounts_controller.rb'
|
|
||||||
- 'app/controllers/admin/announcements_controller.rb'
|
|
||||||
- 'app/controllers/admin/domain_blocks_controller.rb'
|
|
||||||
- 'app/controllers/admin/email_domain_blocks_controller.rb'
|
|
||||||
- 'app/controllers/admin/relationships_controller.rb'
|
|
||||||
- 'app/controllers/admin/relays_controller.rb'
|
|
||||||
- 'app/controllers/admin/roles_controller.rb'
|
|
||||||
- 'app/controllers/admin/rules_controller.rb'
|
|
||||||
- 'app/controllers/admin/statuses_controller.rb'
|
|
||||||
- 'app/controllers/admin/trends/statuses_controller.rb'
|
|
||||||
- 'app/controllers/admin/warning_presets_controller.rb'
|
|
||||||
- 'app/controllers/admin/webhooks_controller.rb'
|
|
||||||
- 'app/controllers/api/v1/accounts/credentials_controller.rb'
|
|
||||||
- 'app/controllers/api/v1/accounts_controller.rb'
|
|
||||||
- 'app/controllers/api/v1/admin/accounts_controller.rb'
|
|
||||||
- 'app/controllers/api/v1/admin/canonical_email_blocks_controller.rb'
|
|
||||||
- 'app/controllers/api/v1/admin/domain_allows_controller.rb'
|
|
||||||
- 'app/controllers/api/v1/admin/domain_blocks_controller.rb'
|
|
||||||
- 'app/controllers/api/v1/admin/email_domain_blocks_controller.rb'
|
|
||||||
- 'app/controllers/api/v1/admin/ip_blocks_controller.rb'
|
|
||||||
- 'app/controllers/api/v1/admin/reports_controller.rb'
|
|
||||||
- 'app/controllers/api/v1/crypto/deliveries_controller.rb'
|
|
||||||
- 'app/controllers/api/v1/crypto/keys/claims_controller.rb'
|
|
||||||
- 'app/controllers/api/v1/crypto/keys/uploads_controller.rb'
|
|
||||||
- 'app/controllers/api/v1/featured_tags_controller.rb'
|
|
||||||
- 'app/controllers/api/v1/filters_controller.rb'
|
|
||||||
- 'app/controllers/api/v1/lists_controller.rb'
|
|
||||||
- 'app/controllers/api/v1/notifications_controller.rb'
|
|
||||||
- 'app/controllers/api/v1/push/subscriptions_controller.rb'
|
|
||||||
- 'app/controllers/api/v1/scheduled_statuses_controller.rb'
|
|
||||||
- 'app/controllers/api/v1/statuses/reblogged_by_accounts_controller.rb'
|
|
||||||
- 'app/controllers/api/v1/statuses_controller.rb'
|
|
||||||
- 'app/controllers/api/v2/filters/keywords_controller.rb'
|
|
||||||
- 'app/controllers/api/v2/filters/statuses_controller.rb'
|
|
||||||
- 'app/controllers/api/v2/filters_controller.rb'
|
|
||||||
- 'app/controllers/api/web/push_subscriptions_controller.rb'
|
|
||||||
- 'app/controllers/application_controller.rb'
|
|
||||||
- 'app/controllers/auth/registrations_controller.rb'
|
|
||||||
- 'app/controllers/filters_controller.rb'
|
|
||||||
- 'app/controllers/settings/applications_controller.rb'
|
|
||||||
- 'app/controllers/settings/featured_tags_controller.rb'
|
|
||||||
- 'app/controllers/settings/profiles_controller.rb'
|
|
||||||
- 'app/controllers/settings/two_factor_authentication/webauthn_credentials_controller.rb'
|
|
||||||
- 'app/controllers/statuses_controller.rb'
|
|
||||||
- 'app/lib/feed_manager.rb'
|
|
||||||
- 'app/models/account.rb'
|
|
||||||
- 'app/models/account_filter.rb'
|
|
||||||
- 'app/models/admin/status_filter.rb'
|
|
||||||
- 'app/models/announcement.rb'
|
|
||||||
- 'app/models/concerns/ldap_authenticable.rb'
|
|
||||||
- 'app/models/concerns/status_threading_concern.rb'
|
|
||||||
- 'app/models/custom_filter.rb'
|
|
||||||
- 'app/models/domain_block.rb'
|
|
||||||
- 'app/models/import.rb'
|
|
||||||
- 'app/models/list.rb'
|
|
||||||
- 'app/models/media_attachment.rb'
|
|
||||||
- 'app/models/preview_card.rb'
|
|
||||||
- 'app/models/relay.rb'
|
|
||||||
- 'app/models/report.rb'
|
|
||||||
- 'app/models/site_upload.rb'
|
|
||||||
- 'app/models/status.rb'
|
|
||||||
- 'app/serializers/initial_state_serializer.rb'
|
|
||||||
- 'app/serializers/rest/notification_serializer.rb'
|
|
||||||
- 'db/migrate/20160220174730_create_accounts.rb'
|
|
||||||
- 'db/migrate/20160221003621_create_follows.rb'
|
|
||||||
- 'db/migrate/20160223171800_create_favourites.rb'
|
|
||||||
- 'db/migrate/20160224223247_create_mentions.rb'
|
|
||||||
- 'db/migrate/20160314164231_add_owner_to_application.rb'
|
|
||||||
- 'db/migrate/20160316103650_add_missing_indices.rb'
|
|
||||||
- 'db/migrate/20160926213048_remove_owner_from_application.rb'
|
|
||||||
- 'db/migrate/20161003145426_create_blocks.rb'
|
|
||||||
- 'db/migrate/20161006213403_rails_settings_migration.rb'
|
|
||||||
- 'db/migrate/20161105130633_create_statuses_tags_join_table.rb'
|
|
||||||
- 'db/migrate/20161119211120_create_notifications.rb'
|
|
||||||
- 'db/migrate/20161128103007_create_subscriptions.rb'
|
|
||||||
- 'db/migrate/20161222204147_create_follow_requests.rb'
|
|
||||||
- 'db/migrate/20170112154826_migrate_settings.rb'
|
|
||||||
- 'db/migrate/20170301222600_create_mutes.rb'
|
|
||||||
- 'db/migrate/20170406215816_add_notifications_and_favourites_indices.rb'
|
|
||||||
- 'db/migrate/20170424003227_create_account_domain_blocks.rb'
|
|
||||||
- 'db/migrate/20170427011934_re_add_owner_to_application.rb'
|
|
||||||
- 'db/migrate/20170507141759_optimize_index_subscriptions.rb'
|
|
||||||
- 'db/migrate/20170508230434_create_conversation_mutes.rb'
|
|
||||||
- 'db/migrate/20170720000000_add_index_favourites_on_account_id_and_id.rb'
|
|
||||||
- 'db/migrate/20170823162448_create_status_pins.rb'
|
|
||||||
- 'db/migrate/20170901142658_create_join_table_preview_cards_statuses.rb'
|
|
||||||
- 'db/migrate/20170905044538_add_index_id_account_id_activity_type_on_notifications.rb'
|
|
||||||
- 'db/migrate/20170917153509_create_custom_emojis.rb'
|
|
||||||
- 'db/migrate/20170918125918_ids_to_bigints.rb'
|
|
||||||
- 'db/migrate/20171116161857_create_list_accounts.rb'
|
|
||||||
- 'db/migrate/20171122120436_add_index_account_and_reblog_of_id_to_statuses.rb'
|
|
||||||
- 'db/migrate/20171125185353_add_index_reblog_of_id_and_account_to_statuses.rb'
|
|
||||||
- 'db/migrate/20171125190735_remove_old_reblog_index_on_statuses.rb'
|
|
||||||
- 'db/migrate/20171129172043_add_index_on_stream_entries.rb'
|
|
||||||
- 'db/migrate/20171226094803_more_faster_index_on_notifications.rb'
|
|
||||||
- 'db/migrate/20180106000232_add_index_on_statuses_for_api_v1_accounts_account_id_statuses.rb'
|
|
||||||
- 'db/migrate/20180514140000_revert_index_change_on_statuses_for_api_v1_accounts_account_id_statuses.rb'
|
|
||||||
- 'db/migrate/20180808175627_create_account_pins.rb'
|
|
||||||
- 'db/migrate/20180831171112_create_bookmarks.rb'
|
|
||||||
- 'db/migrate/20180929222014_create_account_conversations.rb'
|
|
||||||
- 'db/migrate/20181007025445_create_pghero_space_stats.rb'
|
|
||||||
- 'db/migrate/20181203003808_create_accounts_tags_join_table.rb'
|
|
||||||
- 'db/migrate/20190316190352_create_account_identity_proofs.rb'
|
|
||||||
- 'db/migrate/20190511134027_add_silenced_at_suspended_at_to_accounts.rb'
|
|
||||||
- 'db/migrate/20190820003045_update_statuses_index.rb'
|
|
||||||
- 'db/migrate/20190823221802_add_local_index_to_statuses.rb'
|
|
||||||
- 'db/migrate/20190904222339_create_markers.rb'
|
|
||||||
- 'db/migrate/20200113125135_create_announcement_mutes.rb'
|
|
||||||
- 'db/migrate/20200114113335_create_announcement_reactions.rb'
|
|
||||||
- 'db/migrate/20200119112504_add_public_index_to_statuses.rb'
|
|
||||||
- 'db/migrate/20200628133322_create_account_notes.rb'
|
|
||||||
- 'db/migrate/20200917222316_add_index_notifications_on_type.rb'
|
|
||||||
- 'db/migrate/20210425135952_add_index_on_media_attachments_account_id_status_id.rb'
|
|
||||||
- 'db/migrate/20220714171049_create_tag_follows.rb'
|
|
||||||
- 'db/migrate/20221021055441_add_index_featured_tags_on_account_id_and_tag_id.rb'
|
|
||||||
- 'db/post_migrate/20190511152737_remove_suspended_silenced_account_fields.rb'
|
|
||||||
- 'db/post_migrate/20200917222734_remove_index_notifications_on_account_activity.rb'
|
|
||||||
- 'spec/controllers/api/v1/streaming_controller_spec.rb'
|
|
||||||
- 'spec/controllers/api/v2/admin/accounts_controller_spec.rb'
|
|
||||||
- 'spec/controllers/concerns/signature_verification_spec.rb'
|
|
||||||
- 'spec/fabricators/notification_fabricator.rb'
|
|
||||||
- 'spec/models/public_feed_spec.rb'
|
|
||||||
|
|
3
Gemfile
3
Gemfile
|
@ -104,8 +104,6 @@ group :development, :test do
|
||||||
gem 'fabrication', '~> 2.30'
|
gem 'fabrication', '~> 2.30'
|
||||||
gem 'fuubar', '~> 2.5'
|
gem 'fuubar', '~> 2.5'
|
||||||
gem 'i18n-tasks', '~> 1.0', require: false
|
gem 'i18n-tasks', '~> 1.0', require: false
|
||||||
gem 'pry-byebug', '~> 3.10'
|
|
||||||
gem 'pry-rails', '~> 0.3'
|
|
||||||
gem 'rspec-rails', '~> 6.0'
|
gem 'rspec-rails', '~> 6.0'
|
||||||
gem 'rubocop-performance', require: false
|
gem 'rubocop-performance', require: false
|
||||||
gem 'rubocop-rails', require: false
|
gem 'rubocop-rails', require: false
|
||||||
|
@ -119,7 +117,6 @@ end
|
||||||
|
|
||||||
group :test do
|
group :test do
|
||||||
gem 'capybara', '~> 3.38'
|
gem 'capybara', '~> 3.38'
|
||||||
gem 'climate_control', '~> 0.2'
|
|
||||||
gem 'faker', '~> 3.1'
|
gem 'faker', '~> 3.1'
|
||||||
gem 'json-schema', '~> 3.0'
|
gem 'json-schema', '~> 3.0'
|
||||||
gem 'rack-test', '~> 2.0'
|
gem 'rack-test', '~> 2.0'
|
||||||
|
|
16
Gemfile.lock
16
Gemfile.lock
|
@ -144,7 +144,7 @@ GEM
|
||||||
bootsnap (1.16.0)
|
bootsnap (1.16.0)
|
||||||
msgpack (~> 1.2)
|
msgpack (~> 1.2)
|
||||||
brakeman (5.4.0)
|
brakeman (5.4.0)
|
||||||
browser (4.2.0)
|
browser (5.3.1)
|
||||||
brpoplpush-redis_script (0.1.3)
|
brpoplpush-redis_script (0.1.3)
|
||||||
concurrent-ruby (~> 1.0, >= 1.0.5)
|
concurrent-ruby (~> 1.0, >= 1.0.5)
|
||||||
redis (>= 1.0, < 6)
|
redis (>= 1.0, < 6)
|
||||||
|
@ -155,7 +155,6 @@ GEM
|
||||||
bundler-audit (0.9.1)
|
bundler-audit (0.9.1)
|
||||||
bundler (>= 1.2.0, < 3)
|
bundler (>= 1.2.0, < 3)
|
||||||
thor (~> 1.0)
|
thor (~> 1.0)
|
||||||
byebug (11.1.3)
|
|
||||||
capistrano (3.17.2)
|
capistrano (3.17.2)
|
||||||
airbrussh (>= 1.0.0)
|
airbrussh (>= 1.0.0)
|
||||||
i18n
|
i18n
|
||||||
|
@ -499,14 +498,6 @@ GEM
|
||||||
net-smtp
|
net-smtp
|
||||||
premailer (~> 1.7, >= 1.7.9)
|
premailer (~> 1.7, >= 1.7.9)
|
||||||
private_address_check (0.5.0)
|
private_address_check (0.5.0)
|
||||||
pry (0.14.1)
|
|
||||||
coderay (~> 1.1)
|
|
||||||
method_source (~> 1.0)
|
|
||||||
pry-byebug (3.10.1)
|
|
||||||
byebug (~> 11.0)
|
|
||||||
pry (>= 0.13, < 0.15)
|
|
||||||
pry-rails (0.3.9)
|
|
||||||
pry (>= 0.10.4)
|
|
||||||
public_suffix (5.0.1)
|
public_suffix (5.0.1)
|
||||||
puma (6.1.0)
|
puma (6.1.0)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
|
@ -569,7 +560,7 @@ GEM
|
||||||
rdf-normalize (0.5.1)
|
rdf-normalize (0.5.1)
|
||||||
rdf (~> 3.2)
|
rdf (~> 3.2)
|
||||||
redcarpet (3.6.0)
|
redcarpet (3.6.0)
|
||||||
redis (4.5.1)
|
redis (4.8.1)
|
||||||
redis-namespace (1.10.0)
|
redis-namespace (1.10.0)
|
||||||
redis (>= 4)
|
redis (>= 4)
|
||||||
redlock (1.3.2)
|
redlock (1.3.2)
|
||||||
|
@ -794,7 +785,6 @@ DEPENDENCIES
|
||||||
capybara (~> 3.38)
|
capybara (~> 3.38)
|
||||||
charlock_holmes (~> 0.7.7)
|
charlock_holmes (~> 0.7.7)
|
||||||
chewy (~> 7.2)
|
chewy (~> 7.2)
|
||||||
climate_control (~> 0.2)
|
|
||||||
cocoon (~> 1.2)
|
cocoon (~> 1.2)
|
||||||
color_diff (~> 0.1)
|
color_diff (~> 0.1)
|
||||||
concurrent-ruby
|
concurrent-ruby
|
||||||
|
@ -853,8 +843,6 @@ DEPENDENCIES
|
||||||
posix-spawn
|
posix-spawn
|
||||||
premailer-rails
|
premailer-rails
|
||||||
private_address_check (~> 0.5)
|
private_address_check (~> 0.5)
|
||||||
pry-byebug (~> 3.10)
|
|
||||||
pry-rails (~> 0.3)
|
|
||||||
public_suffix (~> 5.0)
|
public_suffix (~> 5.0)
|
||||||
puma (~> 6.1)
|
puma (~> 6.1)
|
||||||
pundit (~> 2.3)
|
pundit (~> 2.3)
|
||||||
|
|
|
@ -20,6 +20,8 @@ class RelationshipsController < ApplicationController
|
||||||
@form.save
|
@form.save
|
||||||
rescue ActionController::ParameterMissing
|
rescue ActionController::ParameterMissing
|
||||||
# Do nothing
|
# Do nothing
|
||||||
|
rescue Mastodon::NotPermittedError, ActiveRecord::RecordNotFound
|
||||||
|
flash[:alert] = I18n.t('relationships.follow_failure') if action_from_button == 'follow'
|
||||||
ensure
|
ensure
|
||||||
redirect_to relationships_path(filter_params)
|
redirect_to relationships_path(filter_params)
|
||||||
end
|
end
|
||||||
|
@ -61,8 +63,8 @@ class RelationshipsController < ApplicationController
|
||||||
'unfollow'
|
'unfollow'
|
||||||
elsif params[:remove_from_followers]
|
elsif params[:remove_from_followers]
|
||||||
'remove_from_followers'
|
'remove_from_followers'
|
||||||
elsif params[:block_domains]
|
elsif params[:block_domains] || params[:remove_domains_from_followers]
|
||||||
'block_domains'
|
'remove_domains_from_followers'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { FormattedMessage, injectIntl } from 'react-intl';
|
||||||
import Permalink from './permalink';
|
import Permalink from './permalink';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import Icon from 'flavours/glitch/components/icon';
|
import Icon from 'flavours/glitch/components/icon';
|
||||||
import { autoPlayGif, languages as preloadedLanguages, translationEnabled } from 'flavours/glitch/initial_state';
|
import { autoPlayGif, languages as preloadedLanguages } from 'flavours/glitch/initial_state';
|
||||||
import { decode as decodeIDNA } from 'flavours/glitch/utils/idna';
|
import { decode as decodeIDNA } from 'flavours/glitch/utils/idna';
|
||||||
|
|
||||||
const textMatchesTarget = (text, origin, host) => {
|
const textMatchesTarget = (text, origin, host) => {
|
||||||
|
@ -315,7 +315,7 @@ class StatusContent extends React.PureComponent {
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const hidden = this.props.onExpandedToggle ? !this.props.expanded : this.state.hidden;
|
const hidden = this.props.onExpandedToggle ? !this.props.expanded : this.state.hidden;
|
||||||
const renderTranslate = translationEnabled && this.context.identity.signedIn && this.props.onTranslate && ['public', 'unlisted'].includes(status.get('visibility')) && status.get('contentHtml').length > 0 && status.get('language') !== null && intl.locale !== status.get('language');
|
const renderTranslate = this.props.onTranslate && status.get('translatable');
|
||||||
|
|
||||||
const content = { __html: status.get('translation') ? status.getIn(['translation', 'content']) : status.get('contentHtml') };
|
const content = { __html: status.get('translation') ? status.getIn(['translation', 'content']) : status.get('contentHtml') };
|
||||||
const spoilerContent = { __html: status.get('spoilerHtml') };
|
const spoilerContent = { __html: status.get('spoilerHtml') };
|
||||||
|
|
|
@ -23,8 +23,8 @@ const mapDispatchToProps = (dispatch) => ({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default @connect(null, mapDispatchToProps)
|
export default @withRouter
|
||||||
@withRouter
|
@connect(null, mapDispatchToProps)
|
||||||
class Header extends React.PureComponent {
|
class Header extends React.PureComponent {
|
||||||
|
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
|
|
|
@ -78,8 +78,8 @@ class NavigationPanel extends React.Component {
|
||||||
{signedIn && (
|
{signedIn && (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<ColumnLink transparent to='/conversations' icon='at' text={intl.formatMessage(messages.direct)} />
|
<ColumnLink transparent to='/conversations' icon='at' text={intl.formatMessage(messages.direct)} />
|
||||||
<ColumnLink transparent to='/favourites' icon='star' text={intl.formatMessage(messages.favourites)} />
|
|
||||||
<ColumnLink transparent to='/bookmarks' icon='bookmark' text={intl.formatMessage(messages.bookmarks)} />
|
<ColumnLink transparent to='/bookmarks' icon='bookmark' text={intl.formatMessage(messages.bookmarks)} />
|
||||||
|
<ColumnLink transparent to='/favourites' icon='star' text={intl.formatMessage(messages.favourites)} />
|
||||||
<ColumnLink transparent to='/lists' icon='list-ul' text={intl.formatMessage(messages.lists)} />
|
<ColumnLink transparent to='/lists' icon='list-ul' text={intl.formatMessage(messages.lists)} />
|
||||||
|
|
||||||
<ListPanel />
|
<ListPanel />
|
||||||
|
|
|
@ -140,7 +140,6 @@ export const unfollowModal = getMeta('unfollow_modal');
|
||||||
export const useBlurhash = getMeta('use_blurhash');
|
export const useBlurhash = getMeta('use_blurhash');
|
||||||
export const usePendingItems = getMeta('use_pending_items');
|
export const usePendingItems = getMeta('use_pending_items');
|
||||||
export const version = getMeta('version');
|
export const version = getMeta('version');
|
||||||
export const translationEnabled = getMeta('translation_enabled');
|
|
||||||
export const languages = initialState?.languages;
|
export const languages = initialState?.languages;
|
||||||
export const statusPageUrl = getMeta('status_page_url');
|
export const statusPageUrl = getMeta('status_page_url');
|
||||||
|
|
||||||
|
|
|
@ -164,8 +164,7 @@
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
|
|
||||||
p,
|
p,
|
||||||
pre,
|
pre {
|
||||||
blockquote {
|
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
|
|
||||||
|
@ -174,79 +173,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
h1,
|
|
||||||
h2,
|
|
||||||
h3,
|
|
||||||
h4,
|
|
||||||
h5 {
|
|
||||||
margin-top: 20px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1,
|
|
||||||
h2 {
|
|
||||||
font-weight: 700;
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3,
|
|
||||||
h4,
|
|
||||||
h5 {
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
blockquote {
|
|
||||||
padding-left: 10px;
|
|
||||||
border-left: 3px solid $inverted-text-color;
|
|
||||||
color: $inverted-text-color;
|
|
||||||
white-space: normal;
|
|
||||||
|
|
||||||
p:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
b,
|
|
||||||
strong {
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
em,
|
|
||||||
i {
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub {
|
|
||||||
font-size: smaller;
|
|
||||||
vertical-align: sub;
|
|
||||||
}
|
|
||||||
|
|
||||||
sup {
|
|
||||||
font-size: smaller;
|
|
||||||
vertical-align: super;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul,
|
|
||||||
ol {
|
|
||||||
margin-left: 1em;
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
list-style-type: disc;
|
|
||||||
}
|
|
||||||
|
|
||||||
ol {
|
|
||||||
list-style-type: decimal;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: $lighter-text-color;
|
color: $lighter-text-color;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
|
|
@ -68,8 +68,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
p,
|
p,
|
||||||
pre,
|
pre {
|
||||||
blockquote {
|
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
unicode-bidi: plaintext;
|
unicode-bidi: plaintext;
|
||||||
|
@ -79,89 +78,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.status__content__text,
|
|
||||||
.e-content {
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
& > ul,
|
|
||||||
& > ol {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1,
|
|
||||||
h2,
|
|
||||||
h3,
|
|
||||||
h4,
|
|
||||||
h5 {
|
|
||||||
margin-top: 20px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1,
|
|
||||||
h2 {
|
|
||||||
font-weight: 700;
|
|
||||||
font-size: 1.2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
font-size: 1.1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3,
|
|
||||||
h4,
|
|
||||||
h5 {
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
blockquote {
|
|
||||||
padding-left: 10px;
|
|
||||||
border-left: 3px solid $darker-text-color;
|
|
||||||
color: $darker-text-color;
|
|
||||||
white-space: normal;
|
|
||||||
|
|
||||||
p:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
b,
|
|
||||||
strong {
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
em,
|
|
||||||
i {
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub {
|
|
||||||
font-size: smaller;
|
|
||||||
vertical-align: sub;
|
|
||||||
}
|
|
||||||
|
|
||||||
sup {
|
|
||||||
font-size: smaller;
|
|
||||||
vertical-align: super;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul,
|
|
||||||
ol {
|
|
||||||
margin-left: 2em;
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
list-style-type: disc;
|
|
||||||
}
|
|
||||||
|
|
||||||
ol {
|
|
||||||
list-style-type: decimal;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: $secondary-text-color;
|
color: $secondary-text-color;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
|
|
@ -21,3 +21,4 @@
|
||||||
@import 'accessibility';
|
@import 'accessibility';
|
||||||
@import 'rtl';
|
@import 'rtl';
|
||||||
@import 'dashboard';
|
@import 'dashboard';
|
||||||
|
@import 'rich_text';
|
||||||
|
|
99
app/javascript/flavours/glitch/styles/rich_text.scss
Normal file
99
app/javascript/flavours/glitch/styles/rich_text.scss
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
.status__content__text,
|
||||||
|
.e-content,
|
||||||
|
.reply-indicator__content {
|
||||||
|
pre,
|
||||||
|
blockquote {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
unicode-bidi: plaintext;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
padding-left: 10px;
|
||||||
|
border-left: 3px solid $darker-text-color;
|
||||||
|
color: $darker-text-color;
|
||||||
|
white-space: normal;
|
||||||
|
|
||||||
|
p:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& > ul,
|
||||||
|
& > ol {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5 {
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2 {
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 1.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5 {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
b,
|
||||||
|
strong {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
em,
|
||||||
|
i {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub {
|
||||||
|
font-size: smaller;
|
||||||
|
vertical-align: sub;
|
||||||
|
}
|
||||||
|
|
||||||
|
sup {
|
||||||
|
font-size: smaller;
|
||||||
|
vertical-align: super;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul,
|
||||||
|
ol {
|
||||||
|
margin-left: 2em;
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style-type: disc;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol {
|
||||||
|
list-style-type: decimal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.reply-indicator__content {
|
||||||
|
blockquote {
|
||||||
|
border-left-color: $inverted-text-color;
|
||||||
|
color: $inverted-text-color;
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,10 +15,10 @@ export default class ColumnBackButton extends React.PureComponent {
|
||||||
};
|
};
|
||||||
|
|
||||||
handleClick = () => {
|
handleClick = () => {
|
||||||
if (window.history && window.history.length === 1) {
|
if (window.history && window.history.state) {
|
||||||
this.context.router.history.push('/');
|
|
||||||
} else {
|
|
||||||
this.context.router.history.goBack();
|
this.context.router.history.goBack();
|
||||||
|
} else {
|
||||||
|
this.context.router.history.push('/');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -43,14 +43,6 @@ class ColumnHeader extends React.PureComponent {
|
||||||
animating: false,
|
animating: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
historyBack = () => {
|
|
||||||
if (window.history && window.history.length === 1) {
|
|
||||||
this.context.router.history.push('/');
|
|
||||||
} else {
|
|
||||||
this.context.router.history.goBack();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
handleToggleClick = (e) => {
|
handleToggleClick = (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
this.setState({ collapsed: !this.state.collapsed, animating: true });
|
this.setState({ collapsed: !this.state.collapsed, animating: true });
|
||||||
|
@ -69,7 +61,11 @@ class ColumnHeader extends React.PureComponent {
|
||||||
};
|
};
|
||||||
|
|
||||||
handleBackClick = () => {
|
handleBackClick = () => {
|
||||||
this.historyBack();
|
if (window.history && window.history.state) {
|
||||||
|
this.context.router.history.goBack();
|
||||||
|
} else {
|
||||||
|
this.context.router.history.push('/');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
handleTransitionEnd = () => {
|
handleTransitionEnd = () => {
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { Link } from 'react-router-dom';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import PollContainer from 'mastodon/containers/poll_container';
|
import PollContainer from 'mastodon/containers/poll_container';
|
||||||
import Icon from 'mastodon/components/icon';
|
import Icon from 'mastodon/components/icon';
|
||||||
import { autoPlayGif, languages as preloadedLanguages, translationEnabled } from 'mastodon/initial_state';
|
import { autoPlayGif, languages as preloadedLanguages } from 'mastodon/initial_state';
|
||||||
|
|
||||||
const MAX_HEIGHT = 706; // 22px * 32 (+ 2px padding at the top)
|
const MAX_HEIGHT = 706; // 22px * 32 (+ 2px padding at the top)
|
||||||
|
|
||||||
|
@ -220,7 +220,7 @@ class StatusContent extends React.PureComponent {
|
||||||
|
|
||||||
const hidden = this.props.onExpandedToggle ? !this.props.expanded : this.state.hidden;
|
const hidden = this.props.onExpandedToggle ? !this.props.expanded : this.state.hidden;
|
||||||
const renderReadMore = this.props.onClick && status.get('collapsed');
|
const renderReadMore = this.props.onClick && status.get('collapsed');
|
||||||
const renderTranslate = translationEnabled && this.context.identity.signedIn && this.props.onTranslate && ['public', 'unlisted'].includes(status.get('visibility')) && status.get('contentHtml').length > 0 && status.get('language') !== null && intl.locale !== status.get('language');
|
const renderTranslate = this.props.onTranslate && status.get('translatable');
|
||||||
|
|
||||||
const content = { __html: status.get('translation') ? status.getIn(['translation', 'content']) : status.get('contentHtml') };
|
const content = { __html: status.get('translation') ? status.getIn(['translation', 'content']) : status.get('contentHtml') };
|
||||||
const spoilerContent = { __html: status.get('spoilerHtml') };
|
const spoilerContent = { __html: status.get('spoilerHtml') };
|
||||||
|
|
|
@ -22,8 +22,8 @@ const mapDispatchToProps = (dispatch) => ({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default @connect(null, mapDispatchToProps)
|
export default @withRouter
|
||||||
@withRouter
|
@connect(null, mapDispatchToProps)
|
||||||
class Header extends React.PureComponent {
|
class Header extends React.PureComponent {
|
||||||
|
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
|
|
|
@ -82,8 +82,8 @@ class NavigationPanel extends React.Component {
|
||||||
{signedIn && (
|
{signedIn && (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<ColumnLink transparent to='/conversations' icon='at' text={intl.formatMessage(messages.direct)} />
|
<ColumnLink transparent to='/conversations' icon='at' text={intl.formatMessage(messages.direct)} />
|
||||||
<ColumnLink transparent to='/favourites' icon='star' text={intl.formatMessage(messages.favourites)} />
|
|
||||||
<ColumnLink transparent to='/bookmarks' icon='bookmark' text={intl.formatMessage(messages.bookmarks)} />
|
<ColumnLink transparent to='/bookmarks' icon='bookmark' text={intl.formatMessage(messages.bookmarks)} />
|
||||||
|
<ColumnLink transparent to='/favourites' icon='star' text={intl.formatMessage(messages.favourites)} />
|
||||||
<ColumnLink transparent to='/lists' icon='list-ul' text={intl.formatMessage(messages.lists)} />
|
<ColumnLink transparent to='/lists' icon='list-ul' text={intl.formatMessage(messages.lists)} />
|
||||||
|
|
||||||
<ListPanel />
|
<ListPanel />
|
||||||
|
|
|
@ -474,10 +474,10 @@ class UI extends React.PureComponent {
|
||||||
};
|
};
|
||||||
|
|
||||||
handleHotkeyBack = () => {
|
handleHotkeyBack = () => {
|
||||||
if (window.history && window.history.length === 1) {
|
if (window.history && window.history.state) {
|
||||||
this.context.router.history.push('/');
|
|
||||||
} else {
|
|
||||||
this.context.router.history.goBack();
|
this.context.router.history.goBack();
|
||||||
|
} else {
|
||||||
|
this.context.router.history.push('/');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -80,7 +80,6 @@
|
||||||
* @property {boolean} use_blurhash
|
* @property {boolean} use_blurhash
|
||||||
* @property {boolean=} use_pending_items
|
* @property {boolean=} use_pending_items
|
||||||
* @property {string} version
|
* @property {string} version
|
||||||
* @property {boolean} translation_enabled
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -132,7 +131,6 @@ export const unfollowModal = getMeta('unfollow_modal');
|
||||||
export const useBlurhash = getMeta('use_blurhash');
|
export const useBlurhash = getMeta('use_blurhash');
|
||||||
export const usePendingItems = getMeta('use_pending_items');
|
export const usePendingItems = getMeta('use_pending_items');
|
||||||
export const version = getMeta('version');
|
export const version = getMeta('version');
|
||||||
export const translationEnabled = getMeta('translation_enabled');
|
|
||||||
export const languages = initialState?.languages;
|
export const languages = initialState?.languages;
|
||||||
export const statusPageUrl = getMeta('status_page_url');
|
export const statusPageUrl = getMeta('status_page_url');
|
||||||
|
|
||||||
|
|
|
@ -23,3 +23,4 @@
|
||||||
@import 'mastodon/dashboard';
|
@import 'mastodon/dashboard';
|
||||||
@import 'mastodon/rtl';
|
@import 'mastodon/rtl';
|
||||||
@import 'mastodon/accessibility';
|
@import 'mastodon/accessibility';
|
||||||
|
@import 'mastodon/rich_text';
|
||||||
|
|
64
app/javascript/styles/mastodon/rich_text.scss
Normal file
64
app/javascript/styles/mastodon/rich_text.scss
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
.status__content__text,
|
||||||
|
.e-content,
|
||||||
|
.reply-indicator__content {
|
||||||
|
pre,
|
||||||
|
blockquote {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
unicode-bidi: plaintext;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
padding-left: 10px;
|
||||||
|
border-left: 3px solid $darker-text-color;
|
||||||
|
color: $darker-text-color;
|
||||||
|
white-space: normal;
|
||||||
|
|
||||||
|
p:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& > ul,
|
||||||
|
& > ol {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
b,
|
||||||
|
strong {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
em,
|
||||||
|
i {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul,
|
||||||
|
ol {
|
||||||
|
margin-left: 2em;
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style-type: disc;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol {
|
||||||
|
list-style-type: decimal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.reply-indicator__content {
|
||||||
|
blockquote {
|
||||||
|
border-left-color: $inverted-text-color;
|
||||||
|
color: $inverted-text-color;
|
||||||
|
}
|
||||||
|
}
|
|
@ -322,27 +322,27 @@ class FeedManager
|
||||||
def clean_feeds!(type, ids)
|
def clean_feeds!(type, ids)
|
||||||
reblogged_id_sets = {}
|
reblogged_id_sets = {}
|
||||||
|
|
||||||
redis.pipelined do
|
redis.pipelined do |pipeline|
|
||||||
ids.each do |feed_id|
|
ids.each do |feed_id|
|
||||||
redis.del(key(type, feed_id))
|
|
||||||
reblog_key = key(type, feed_id, 'reblogs')
|
reblog_key = key(type, feed_id, 'reblogs')
|
||||||
# We collect a future for this: we don't block while getting
|
# We collect a future for this: we don't block while getting
|
||||||
# it, but we can iterate over it later.
|
# it, but we can iterate over it later.
|
||||||
reblogged_id_sets[feed_id] = redis.zrange(reblog_key, 0, -1)
|
reblogged_id_sets[feed_id] = pipeline.zrange(reblog_key, 0, -1)
|
||||||
redis.del(reblog_key)
|
pipeline.del(key(type, feed_id), reblog_key)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Remove all of the reblog tracking keys we just removed the
|
# Remove all of the reblog tracking keys we just removed the
|
||||||
# references to.
|
# references to.
|
||||||
redis.pipelined do
|
keys_to_delete = reblogged_id_sets.flat_map do |feed_id, future|
|
||||||
reblogged_id_sets.each do |feed_id, future|
|
future.value.map do |reblogged_id|
|
||||||
future.value.each do |reblogged_id|
|
key(type, feed_id, "reblogs:#{reblogged_id}")
|
||||||
reblog_set_key = key(type, feed_id, "reblogs:#{reblogged_id}")
|
|
||||||
redis.del(reblog_set_key)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
redis.del(keys_to_delete) unless keys_to_delete.empty?
|
||||||
|
|
||||||
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -21,6 +21,10 @@ class TranslationService
|
||||||
ENV['DEEPL_API_KEY'].present? || ENV['LIBRE_TRANSLATE_ENDPOINT'].present?
|
ENV['DEEPL_API_KEY'].present? || ENV['LIBRE_TRANSLATE_ENDPOINT'].present?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def supported?(_source_language, _target_language)
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
def translate(_text, _source_language, _target_language)
|
def translate(_text, _source_language, _target_language)
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
end
|
end
|
||||||
|
|
|
@ -11,33 +11,53 @@ class TranslationService::DeepL < TranslationService
|
||||||
end
|
end
|
||||||
|
|
||||||
def translate(text, source_language, target_language)
|
def translate(text, source_language, target_language)
|
||||||
request(text, source_language, target_language).perform do |res|
|
form = { text: text, source_lang: source_language&.upcase, target_lang: target_language, tag_handling: 'html' }
|
||||||
|
request(:post, '/v2/translate', form: form) do |res|
|
||||||
|
transform_response(res.body_with_limit)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def supported?(source_language, target_language)
|
||||||
|
source_language.in?(languages('source')) && target_language.in?(languages('target'))
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def languages(type)
|
||||||
|
Rails.cache.fetch("translation_service/deepl/languages/#{type}", expires_in: 7.days, race_condition_ttl: 1.minute) do
|
||||||
|
request(:get, "/v2/languages?type=#{type}") do |res|
|
||||||
|
# In DeepL, EN and PT are deprecated in favor of EN-GB/EN-US and PT-BR/PT-PT, so
|
||||||
|
# they are supported but not returned by the API.
|
||||||
|
extra = type == 'source' ? [nil] : %w(en pt)
|
||||||
|
languages = Oj.load(res.body_with_limit).map { |language| language['language'].downcase }
|
||||||
|
|
||||||
|
languages + extra
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def request(verb, path, **options)
|
||||||
|
req = Request.new(verb, "#{base_url}#{path}", **options)
|
||||||
|
req.add_headers(Authorization: "DeepL-Auth-Key #{@api_key}")
|
||||||
|
req.perform do |res|
|
||||||
case res.code
|
case res.code
|
||||||
when 429
|
when 429
|
||||||
raise TooManyRequestsError
|
raise TooManyRequestsError
|
||||||
when 456
|
when 456
|
||||||
raise QuotaExceededError
|
raise QuotaExceededError
|
||||||
when 200...300
|
when 200...300
|
||||||
transform_response(res.body_with_limit)
|
yield res
|
||||||
else
|
else
|
||||||
raise UnexpectedResponseError
|
raise UnexpectedResponseError
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
def base_url
|
||||||
|
|
||||||
def request(text, source_language, target_language)
|
|
||||||
req = Request.new(:post, endpoint_url, form: { text: text, source_lang: source_language&.upcase, target_lang: target_language, tag_handling: 'html' })
|
|
||||||
req.add_headers(Authorization: "DeepL-Auth-Key #{@api_key}")
|
|
||||||
req
|
|
||||||
end
|
|
||||||
|
|
||||||
def endpoint_url
|
|
||||||
if @plan == 'free'
|
if @plan == 'free'
|
||||||
'https://api-free.deepl.com/v2/translate'
|
'https://api-free.deepl.com'
|
||||||
else
|
else
|
||||||
'https://api.deepl.com/v2/translate'
|
'https://api.deepl.com'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -9,29 +9,45 @@ class TranslationService::LibreTranslate < TranslationService
|
||||||
end
|
end
|
||||||
|
|
||||||
def translate(text, source_language, target_language)
|
def translate(text, source_language, target_language)
|
||||||
request(text, source_language, target_language).perform do |res|
|
body = Oj.dump(q: text, source: source_language.presence || 'auto', target: target_language, format: 'html', api_key: @api_key)
|
||||||
|
request(:post, '/translate', body: body) do |res|
|
||||||
|
transform_response(res.body_with_limit, source_language)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def supported?(source_language, target_language)
|
||||||
|
languages.key?(source_language) && languages[source_language].include?(target_language)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def languages
|
||||||
|
Rails.cache.fetch('translation_service/libre_translate/languages', expires_in: 7.days, race_condition_ttl: 1.minute) do
|
||||||
|
request(:get, '/languages') do |res|
|
||||||
|
languages = Oj.load(res.body_with_limit).to_h { |language| [language['code'], language['targets']] }
|
||||||
|
languages[nil] = languages.values.flatten.uniq
|
||||||
|
languages
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def request(verb, path, **options)
|
||||||
|
req = Request.new(verb, "#{@base_url}#{path}", allow_local: true, **options)
|
||||||
|
req.add_headers('Content-Type': 'application/json')
|
||||||
|
req.perform do |res|
|
||||||
case res.code
|
case res.code
|
||||||
when 429
|
when 429
|
||||||
raise TooManyRequestsError
|
raise TooManyRequestsError
|
||||||
when 403
|
when 403
|
||||||
raise QuotaExceededError
|
raise QuotaExceededError
|
||||||
when 200...300
|
when 200...300
|
||||||
transform_response(res.body_with_limit, source_language)
|
yield res
|
||||||
else
|
else
|
||||||
raise UnexpectedResponseError
|
raise UnexpectedResponseError
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def request(text, source_language, target_language)
|
|
||||||
body = Oj.dump(q: text, source: source_language.presence || 'auto', target: target_language, format: 'html', api_key: @api_key)
|
|
||||||
req = Request.new(:post, "#{@base_url}/translate", body: body, allow_local: true)
|
|
||||||
req.add_headers('Content-Type': 'application/json')
|
|
||||||
req
|
|
||||||
end
|
|
||||||
|
|
||||||
def transform_response(str, source_language)
|
def transform_response(str, source_language)
|
||||||
json = Oj.load(str, mode: :strict)
|
json = Oj.load(str, mode: :strict)
|
||||||
|
|
||||||
|
|
|
@ -7,9 +7,17 @@ class ApplicationMailer < ActionMailer::Base
|
||||||
helper :instance
|
helper :instance
|
||||||
helper :formatting
|
helper :formatting
|
||||||
|
|
||||||
|
after_action :set_autoreply_headers!
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def locale_for_account(account, &block)
|
def locale_for_account(account, &block)
|
||||||
I18n.with_locale(account.user_locale || I18n.default_locale, &block)
|
I18n.with_locale(account.user_locale || I18n.default_locale, &block)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def set_autoreply_headers!
|
||||||
|
headers['Precedence'] = 'list'
|
||||||
|
headers['X-Auto-Response-Suppress'] = 'All'
|
||||||
|
headers['Auto-Submitted'] = 'auto-generated'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -61,7 +61,7 @@ module Omniauthable
|
||||||
user.account.avatar_remote_url = nil
|
user.account.avatar_remote_url = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
user.skip_confirmation! if email_is_verified
|
user.confirm! if email_is_verified
|
||||||
user.save!
|
user.save!
|
||||||
user
|
user
|
||||||
end
|
end
|
||||||
|
|
|
@ -20,9 +20,9 @@ class FollowRecommendationSuppression < ApplicationRecord
|
||||||
private
|
private
|
||||||
|
|
||||||
def remove_follow_recommendations
|
def remove_follow_recommendations
|
||||||
redis.pipelined do
|
redis.pipelined do |pipeline|
|
||||||
I18n.available_locales.each do |locale|
|
I18n.available_locales.each do |locale|
|
||||||
redis.zrem("follow_recommendations:#{locale}", account_id)
|
pipeline.zrem("follow_recommendations:#{locale}", account_id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -17,8 +17,8 @@ class Form::AccountBatch
|
||||||
unfollow!
|
unfollow!
|
||||||
when 'remove_from_followers'
|
when 'remove_from_followers'
|
||||||
remove_from_followers!
|
remove_from_followers!
|
||||||
when 'block_domains'
|
when 'remove_domains_from_followers'
|
||||||
block_domains!
|
remove_domains_from_followers!
|
||||||
when 'approve'
|
when 'approve'
|
||||||
approve!
|
approve!
|
||||||
when 'reject'
|
when 'reject'
|
||||||
|
@ -35,9 +35,15 @@ class Form::AccountBatch
|
||||||
private
|
private
|
||||||
|
|
||||||
def follow!
|
def follow!
|
||||||
|
error = nil
|
||||||
|
|
||||||
accounts.each do |target_account|
|
accounts.each do |target_account|
|
||||||
FollowService.new.call(current_account, target_account)
|
FollowService.new.call(current_account, target_account)
|
||||||
|
rescue Mastodon::NotPermittedError, ActiveRecord::RecordNotFound => e
|
||||||
|
error ||= e
|
||||||
end
|
end
|
||||||
|
|
||||||
|
raise error if error.present?
|
||||||
end
|
end
|
||||||
|
|
||||||
def unfollow!
|
def unfollow!
|
||||||
|
@ -50,10 +56,8 @@ class Form::AccountBatch
|
||||||
RemoveFromFollowersService.new.call(current_account, account_ids)
|
RemoveFromFollowersService.new.call(current_account, account_ids)
|
||||||
end
|
end
|
||||||
|
|
||||||
def block_domains!
|
def remove_domains_from_followers!
|
||||||
AfterAccountDomainBlockWorker.push_bulk(account_domains) do |domain|
|
RemoveDomainsFromFollowersService.new.call(current_account, account_domains)
|
||||||
[current_account.id, domain]
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def account_domains
|
def account_domains
|
||||||
|
|
|
@ -237,6 +237,16 @@ class Status < ApplicationRecord
|
||||||
public_visibility? || unlisted_visibility?
|
public_visibility? || unlisted_visibility?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def translatable?
|
||||||
|
translate_target_locale = I18n.locale.to_s.split(/[_-]/).first
|
||||||
|
|
||||||
|
distributable? &&
|
||||||
|
content.present? &&
|
||||||
|
language != translate_target_locale &&
|
||||||
|
TranslationService.configured? &&
|
||||||
|
TranslationService.configured.supported?(language, translate_target_locale)
|
||||||
|
end
|
||||||
|
|
||||||
alias sign? distributable?
|
alias sign? distributable?
|
||||||
|
|
||||||
def with_media?
|
def with_media?
|
||||||
|
|
|
@ -44,7 +44,6 @@ class InitialStateSerializer < ActiveModel::Serializer
|
||||||
timeline_preview: Setting.timeline_preview,
|
timeline_preview: Setting.timeline_preview,
|
||||||
activity_api_enabled: Setting.activity_api_enabled,
|
activity_api_enabled: Setting.activity_api_enabled,
|
||||||
single_user_mode: Rails.configuration.x.single_user_mode,
|
single_user_mode: Rails.configuration.x.single_user_mode,
|
||||||
translation_enabled: TranslationService.configured?,
|
|
||||||
trends_as_landing_page: Setting.trends_as_landing_page,
|
trends_as_landing_page: Setting.trends_as_landing_page,
|
||||||
status_page_url: Setting.status_page_url,
|
status_page_url: Setting.status_page_url,
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ class REST::StatusSerializer < ActiveModel::Serializer
|
||||||
include FormattingHelper
|
include FormattingHelper
|
||||||
|
|
||||||
attributes :id, :created_at, :in_reply_to_id, :in_reply_to_account_id,
|
attributes :id, :created_at, :in_reply_to_id, :in_reply_to_account_id,
|
||||||
:sensitive, :spoiler_text, :visibility, :language,
|
:sensitive, :spoiler_text, :visibility, :language, :translatable,
|
||||||
:uri, :url, :replies_count, :reblogs_count,
|
:uri, :url, :replies_count, :reblogs_count,
|
||||||
:favourites_count, :edited_at
|
:favourites_count, :edited_at
|
||||||
|
|
||||||
|
@ -52,6 +52,10 @@ class REST::StatusSerializer < ActiveModel::Serializer
|
||||||
object.account.user_shows_application? || (current_user? && current_user.account_id == object.account_id)
|
object.account.user_shows_application? || (current_user? && current_user.account_id == object.account_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def translatable
|
||||||
|
current_user? && object.translatable?
|
||||||
|
end
|
||||||
|
|
||||||
def visibility
|
def visibility
|
||||||
# This visibility is masked behind "private"
|
# This visibility is masked behind "private"
|
||||||
# to avoid API changes because there are no
|
# to avoid API changes because there are no
|
||||||
|
|
|
@ -48,9 +48,9 @@ class BatchedRemoveStatusService < BaseService
|
||||||
|
|
||||||
# Cannot be batched
|
# Cannot be batched
|
||||||
@status_id_cutoff = Mastodon::Snowflake.id_at(2.weeks.ago)
|
@status_id_cutoff = Mastodon::Snowflake.id_at(2.weeks.ago)
|
||||||
redis.pipelined do
|
redis.pipelined do |pipeline|
|
||||||
statuses.each do |status|
|
statuses.each do |status|
|
||||||
unpush_from_public_timelines(status)
|
unpush_from_public_timelines(status, pipeline)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -73,22 +73,22 @@ class BatchedRemoveStatusService < BaseService
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def unpush_from_public_timelines(status)
|
def unpush_from_public_timelines(status, pipeline)
|
||||||
return unless status.public_visibility? && status.id > @status_id_cutoff
|
return unless status.public_visibility? && status.id > @status_id_cutoff
|
||||||
|
|
||||||
payload = Oj.dump(event: :delete, payload: status.id.to_s)
|
payload = Oj.dump(event: :delete, payload: status.id.to_s)
|
||||||
|
|
||||||
redis.publish('timeline:public', payload)
|
pipeline.publish('timeline:public', payload)
|
||||||
redis.publish(status.local? ? 'timeline:public:local' : 'timeline:public:remote', payload)
|
pipeline.publish(status.local? ? 'timeline:public:local' : 'timeline:public:remote', payload)
|
||||||
|
|
||||||
if status.media_attachments.any?
|
if status.media_attachments.any?
|
||||||
redis.publish('timeline:public:media', payload)
|
pipeline.publish('timeline:public:media', payload)
|
||||||
redis.publish(status.local? ? 'timeline:public:local:media' : 'timeline:public:remote:media', payload)
|
pipeline.publish(status.local? ? 'timeline:public:local:media' : 'timeline:public:remote:media', payload)
|
||||||
end
|
end
|
||||||
|
|
||||||
status.tags.map { |tag| tag.name.mb_chars.downcase }.each do |hashtag|
|
status.tags.map { |tag| tag.name.mb_chars.downcase }.each do |hashtag|
|
||||||
redis.publish("timeline:hashtag:#{hashtag}", payload)
|
pipeline.publish("timeline:hashtag:#{hashtag}", payload)
|
||||||
redis.publish("timeline:hashtag:#{hashtag}:local", payload) if status.local?
|
pipeline.publish("timeline:hashtag:#{hashtag}:local", payload) if status.local?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
40
app/services/follow_migration_service.rb
Normal file
40
app/services/follow_migration_service.rb
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class FollowMigrationService < FollowService
|
||||||
|
# Follow an account with the same settings as another account, and unfollow the old account once the request is sent
|
||||||
|
# @param [Account] source_account From which to follow
|
||||||
|
# @param [Account] target_account Account to follow
|
||||||
|
# @param [Account] old_target_account Account to unfollow once the follow request has been sent to the new one
|
||||||
|
# @option [Boolean] bypass_locked Whether to immediately follow the new account even if it is locked
|
||||||
|
def call(source_account, target_account, old_target_account, bypass_locked: false)
|
||||||
|
@old_target_account = old_target_account
|
||||||
|
|
||||||
|
follow = source_account.active_relationships.find_by(target_account: old_target_account)
|
||||||
|
reblogs = follow&.show_reblogs?
|
||||||
|
notify = follow&.notify?
|
||||||
|
languages = follow&.languages
|
||||||
|
|
||||||
|
super(source_account, target_account, reblogs: reblogs, notify: notify, languages: languages, bypass_locked: bypass_locked, bypass_limit: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def request_follow!
|
||||||
|
follow_request = @source_account.request_follow!(@target_account, **follow_options.merge(rate_limit: @options[:with_rate_limit], bypass_limit: @options[:bypass_limit]))
|
||||||
|
|
||||||
|
if @target_account.local?
|
||||||
|
LocalNotificationWorker.perform_async(@target_account.id, follow_request.id, follow_request.class.name, 'follow_request')
|
||||||
|
UnfollowService.new.call(@source_account, @old_target_account, skip_unmerge: true)
|
||||||
|
elsif @target_account.activitypub?
|
||||||
|
ActivityPub::MigratedFollowDeliveryWorker.perform_async(build_json(follow_request), @source_account.id, @target_account.inbox_url, @old_target_account.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
follow_request
|
||||||
|
end
|
||||||
|
|
||||||
|
def direct_follow!
|
||||||
|
follow = super
|
||||||
|
UnfollowService.new.call(@source_account, @old_target_account, skip_unmerge: true)
|
||||||
|
follow
|
||||||
|
end
|
||||||
|
end
|
23
app/services/remove_domains_from_followers_service.rb
Normal file
23
app/services/remove_domains_from_followers_service.rb
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class RemoveDomainsFromFollowersService < BaseService
|
||||||
|
include Payloadable
|
||||||
|
|
||||||
|
def call(source_account, target_domains)
|
||||||
|
source_account.passive_relationships.where(account_id: Account.where(domain: target_domains)).find_each do |follow|
|
||||||
|
follow.destroy
|
||||||
|
|
||||||
|
create_notification(follow) if source_account.local? && !follow.account.local? && follow.account.activitypub?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def create_notification(follow)
|
||||||
|
ActivityPub::DeliveryWorker.perform_async(build_json(follow), follow.target_account_id, follow.account.inbox_url)
|
||||||
|
end
|
||||||
|
|
||||||
|
def build_json(follow)
|
||||||
|
Oj.dump(serialize_payload(follow, ActivityPub::RejectFollowSerializer))
|
||||||
|
end
|
||||||
|
end
|
|
@ -6,7 +6,7 @@ class TranslateStatusService < BaseService
|
||||||
include FormattingHelper
|
include FormattingHelper
|
||||||
|
|
||||||
def call(status, target_language)
|
def call(status, target_language)
|
||||||
raise Mastodon::NotPermittedError unless status.public_visibility? || status.unlisted_visibility?
|
raise Mastodon::NotPermittedError unless status.translatable?
|
||||||
|
|
||||||
@status = status
|
@status = status
|
||||||
@content = status_content_format(@status)
|
@content = status_content_format(@status)
|
||||||
|
|
|
@ -6,7 +6,7 @@ class Ed25519KeyValidator < ActiveModel::EachValidator
|
||||||
|
|
||||||
key = Base64.decode64(value)
|
key = Base64.decode64(value)
|
||||||
|
|
||||||
record.errors[attribute] << I18n.t('crypto.errors.invalid_key') unless verified?(key)
|
record.errors.add(attribute, I18n.t('crypto.errors.invalid_key')) unless verified?(key)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -8,7 +8,7 @@ class Ed25519SignatureValidator < ActiveModel::EachValidator
|
||||||
signature = Base64.decode64(value)
|
signature = Base64.decode64(value)
|
||||||
message = option_to_value(record, :message)
|
message = option_to_value(record, :message)
|
||||||
|
|
||||||
record.errors[attribute] << I18n.t('crypto.errors.invalid_signature') unless verified?(verify_key, signature, message)
|
record.errors.add(attribute, I18n.t('crypto.errors.invalid_signature')) unless verified?(verify_key, signature, message)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -5,7 +5,7 @@ RSS::Builder.build do |doc|
|
||||||
doc.image(full_asset_url(@account.avatar.url(:original)), display_name(@account), params[:tag].present? ? short_account_tag_url(@account, params[:tag]) : short_account_url(@account))
|
doc.image(full_asset_url(@account.avatar.url(:original)), display_name(@account), params[:tag].present? ? short_account_tag_url(@account, params[:tag]) : short_account_url(@account))
|
||||||
doc.last_build_date(@statuses.first.created_at) if @statuses.any?
|
doc.last_build_date(@statuses.first.created_at) if @statuses.any?
|
||||||
doc.icon(full_asset_url(@account.avatar.url(:original)))
|
doc.icon(full_asset_url(@account.avatar.url(:original)))
|
||||||
doc.generator("Mastodon v#{Mastodon::Version.to_s}")
|
doc.generator("Mastodon v#{Mastodon::Version}")
|
||||||
|
|
||||||
@statuses.each do |status|
|
@statuses.each do |status|
|
||||||
doc.item do |item|
|
doc.item do |item|
|
||||||
|
@ -18,12 +18,12 @@ RSS::Builder.build do |doc|
|
||||||
item.enclosure(full_asset_url(media.file.url(:original, false)), media.file.content_type, media.file.size)
|
item.enclosure(full_asset_url(media.file.url(:original, false)), media.file.content_type, media.file.size)
|
||||||
end
|
end
|
||||||
|
|
||||||
status.ordered_media_attachments.each do |media|
|
status.ordered_media_attachments.each do |media_attachment|
|
||||||
item.media_content(full_asset_url(media.file.url(:original, false)), media.file.content_type, media.file.size) do |media_content|
|
item.media_content(full_asset_url(media_attachment.file.url(:original, false)), media_attachment.file.content_type, media_attachment.file.size) do |media_content|
|
||||||
media_content.medium(media.gifv? ? 'image' : media.type.to_s)
|
media_content.medium(media_attachment.gifv? ? 'image' : media_attachment.type.to_s)
|
||||||
media_content.rating(status.sensitive? ? 'adult' : 'nonadult')
|
media_content.rating(status.sensitive? ? 'adult' : 'nonadult')
|
||||||
media_content.description(media.description) if media.description.present?
|
media_content.description(media_attachment.description) if media_attachment.description.present?
|
||||||
media_content.thumbnail(media.thumbnail.url(:original, false)) if media.thumbnail?
|
media_content.thumbnail(media_attachment.thumbnail.url(:original, false)) if media_attachment.thumbnail?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
%td
|
%td
|
||||||
- if @status.trend.allowed?
|
- if @status.trend.allowed?
|
||||||
%abbr{ title: t('admin.trends.tags.current_score', score: @status.trend.score) }= t('admin.trends.tags.trending_rank', rank: @status.trend.rank)
|
%abbr{ title: t('admin.trends.tags.current_score', score: @status.trend.score) }= t('admin.trends.tags.trending_rank', rank: @status.trend.rank)
|
||||||
- elsif @status.trend.requires_review?
|
- elsif @status.requires_review?
|
||||||
= t('admin.trends.pending_review')
|
= t('admin.trends.pending_review')
|
||||||
- else
|
- else
|
||||||
= t('admin.trends.not_allowed_to_trend')
|
= t('admin.trends.not_allowed_to_trend')
|
||||||
|
|
|
@ -45,7 +45,7 @@
|
||||||
|
|
||||||
= f.button safe_join([fa_icon('trash'), t('relationships.remove_selected_followers')]), name: :remove_from_followers, class: 'table-action-link', type: :submit, data: { confirm: t('relationships.confirm_remove_selected_followers') } unless following_relationship?
|
= f.button safe_join([fa_icon('trash'), t('relationships.remove_selected_followers')]), name: :remove_from_followers, class: 'table-action-link', type: :submit, data: { confirm: t('relationships.confirm_remove_selected_followers') } unless following_relationship?
|
||||||
|
|
||||||
= f.button safe_join([fa_icon('trash'), t('relationships.remove_selected_domains')]), name: :block_domains, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') } if followed_by_relationship?
|
= f.button safe_join([fa_icon('trash'), t('relationships.remove_selected_domains')]), name: :remove_domains_from_followers, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') } if followed_by_relationship?
|
||||||
.batch-table__body
|
.batch-table__body
|
||||||
- if @accounts.empty?
|
- if @accounts.empty?
|
||||||
= nothing_here 'nothing-here--under-tabs'
|
= nothing_here 'nothing-here--under-tabs'
|
||||||
|
|
|
@ -3,7 +3,7 @@ RSS::Builder.build do |doc|
|
||||||
doc.description(I18n.t('rss.descriptions.tag', hashtag: @tag.display_name))
|
doc.description(I18n.t('rss.descriptions.tag', hashtag: @tag.display_name))
|
||||||
doc.link(tag_url(@tag))
|
doc.link(tag_url(@tag))
|
||||||
doc.last_build_date(@statuses.first.created_at) if @statuses.any?
|
doc.last_build_date(@statuses.first.created_at) if @statuses.any?
|
||||||
doc.generator("Mastodon v#{Mastodon::Version.to_s}")
|
doc.generator("Mastodon v#{Mastodon::Version}")
|
||||||
|
|
||||||
@statuses.each do |status|
|
@statuses.each do |status|
|
||||||
doc.item do |item|
|
doc.item do |item|
|
||||||
|
@ -16,12 +16,12 @@ RSS::Builder.build do |doc|
|
||||||
item.enclosure(full_asset_url(media.file.url(:original, false)), media.file.content_type, media.file.size)
|
item.enclosure(full_asset_url(media.file.url(:original, false)), media.file.content_type, media.file.size)
|
||||||
end
|
end
|
||||||
|
|
||||||
status.ordered_media_attachments.each do |media|
|
status.ordered_media_attachments.each do |media_attachment|
|
||||||
item.media_content(full_asset_url(media.file.url(:original, false)), media.file.content_type, media.file.size) do |media_content|
|
item.media_content(full_asset_url(media_attachment.file.url(:original, false)), media_attachment.file.content_type, media_attachment.file.size) do |media_content|
|
||||||
media_content.medium(media.gifv? ? 'image' : media.type.to_s)
|
media_content.medium(media_attachment.gifv? ? 'image' : media_attachment.type.to_s)
|
||||||
media_content.rating(status.sensitive? ? 'adult' : 'nonadult')
|
media_content.rating(status.sensitive? ? 'adult' : 'nonadult')
|
||||||
media_content.description(media.description) if media.description.present?
|
media_content.description(media_attachment.description) if media_attachment.description.present?
|
||||||
media_content.thumbnail(media.thumbnail.url(:original, false)) if media.thumbnail?
|
media_content.thumbnail(media_attachment.thumbnail.url(:original, false)) if media_attachment.thumbnail?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
%tbody
|
%tbody
|
||||||
%tr
|
%tr
|
||||||
%td
|
%td
|
||||||
= image_tag full_pack_url('media/images/mailer/icon_warning.png'), alt: ''
|
= image_tag full_pack_url('media/images/mailer/icon_flag.png'), alt: ''
|
||||||
|
|
||||||
%h1= t 'user_mailer.appeal_rejected.title'
|
%h1= t 'user_mailer.appeal_rejected.title'
|
||||||
|
|
||||||
|
|
|
@ -9,4 +9,4 @@ doc << Ox::Element.new('XRD').tap do |xrd|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
('<?xml version="1.0" encoding="UTF-8"?>' + Ox.dump(doc, effort: :tolerant)).force_encoding('UTF-8')
|
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>#{Ox.dump(doc, effort: :tolerant)}".force_encoding('UTF-8')
|
||||||
|
|
|
@ -10,6 +10,16 @@ class ActivityPub::DeliveryWorker
|
||||||
|
|
||||||
sidekiq_options queue: 'push', retry: 16, dead: false
|
sidekiq_options queue: 'push', retry: 16, dead: false
|
||||||
|
|
||||||
|
# Unfortunately, we cannot control Sidekiq's jitter, so add our own
|
||||||
|
sidekiq_retry_in do |count|
|
||||||
|
# This is Sidekiq's default delay
|
||||||
|
delay = (count**4) + 15
|
||||||
|
# Our custom jitter, that will be added to Sidekiq's built-in one.
|
||||||
|
# Sidekiq's built-in jitter is `rand(10) * (count + 1)`
|
||||||
|
jitter = rand(0.5 * (count**4))
|
||||||
|
delay + jitter
|
||||||
|
end
|
||||||
|
|
||||||
HEADERS = { 'Content-Type' => 'application/activity+json' }.freeze
|
HEADERS = { 'Content-Type' => 'application/activity+json' }.freeze
|
||||||
|
|
||||||
def perform(json, source_account_id, inbox_url, options = {})
|
def perform(json, source_account_id, inbox_url, options = {})
|
||||||
|
|
17
app/workers/activitypub/migrated_follow_delivery_worker.rb
Normal file
17
app/workers/activitypub/migrated_follow_delivery_worker.rb
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class ActivityPub::MigratedFollowDeliveryWorker < ActivityPub::DeliveryWorker
|
||||||
|
def perform(json, source_account_id, inbox_url, old_target_account_id, options = {})
|
||||||
|
super(json, source_account_id, inbox_url, options)
|
||||||
|
unfollow_old_account!(old_target_account_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def unfollow_old_account!(old_target_account_id)
|
||||||
|
old_target_account = Account.find(old_target_account_id)
|
||||||
|
UnfollowService.new.call(@source_account, old_target_account, skip_unmerge: true)
|
||||||
|
rescue
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end
|
|
@ -20,7 +20,7 @@ class Scheduler::FollowRecommendationsScheduler
|
||||||
|
|
||||||
Trends.available_locales.each do |locale|
|
Trends.available_locales.each do |locale|
|
||||||
recommendations = if AccountSummary.safe.filtered.localized(locale).exists? # We can skip the work if no accounts with that language exist
|
recommendations = if AccountSummary.safe.filtered.localized(locale).exists? # We can skip the work if no accounts with that language exist
|
||||||
FollowRecommendation.localized(locale).order(rank: :desc).limit(SET_SIZE).map { |recommendation| [recommendation.account_id, recommendation.rank] }
|
FollowRecommendation.localized(locale).order(rank: :desc).limit(SET_SIZE).map { |recommendation| [recommendation.rank, recommendation.account_id] }
|
||||||
else
|
else
|
||||||
[]
|
[]
|
||||||
end
|
end
|
||||||
|
@ -33,14 +33,14 @@ class Scheduler::FollowRecommendationsScheduler
|
||||||
|
|
||||||
# Language-specific results should be above language-agnostic ones,
|
# Language-specific results should be above language-agnostic ones,
|
||||||
# otherwise language-agnostic ones will always overshadow them
|
# otherwise language-agnostic ones will always overshadow them
|
||||||
recommendations.map! { |(account_id, rank)| [account_id, rank + max_fallback_rank] }
|
recommendations.map! { |(rank, account_id)| [rank + max_fallback_rank, account_id] }
|
||||||
|
|
||||||
added = 0
|
added = 0
|
||||||
|
|
||||||
fallback_recommendations.each do |recommendation|
|
fallback_recommendations.each do |recommendation|
|
||||||
next if recommendations.any? { |(account_id, _)| account_id == recommendation.account_id }
|
next if recommendations.any? { |(_, account_id)| account_id == recommendation.account_id }
|
||||||
|
|
||||||
recommendations << [recommendation.account_id, recommendation.rank]
|
recommendations << [recommendation.rank, recommendation.account_id]
|
||||||
added += 1
|
added += 1
|
||||||
|
|
||||||
break if added >= missing
|
break if added >= missing
|
||||||
|
@ -49,10 +49,7 @@ class Scheduler::FollowRecommendationsScheduler
|
||||||
|
|
||||||
redis.multi do |multi|
|
redis.multi do |multi|
|
||||||
multi.del(key(locale))
|
multi.del(key(locale))
|
||||||
|
multi.zadd(key(locale), recommendations)
|
||||||
recommendations.each do |(account_id, rank)|
|
|
||||||
multi.zadd(key(locale), rank, account_id)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,13 +10,7 @@ class UnfollowFollowWorker
|
||||||
old_target_account = Account.find(old_target_account_id)
|
old_target_account = Account.find(old_target_account_id)
|
||||||
new_target_account = Account.find(new_target_account_id)
|
new_target_account = Account.find(new_target_account_id)
|
||||||
|
|
||||||
follow = follower_account.active_relationships.find_by(target_account: old_target_account)
|
FollowMigrationService.new.call(follower_account, new_target_account, old_target_account, bypass_locked: bypass_locked)
|
||||||
reblogs = follow&.show_reblogs?
|
|
||||||
notify = follow&.notify?
|
|
||||||
languages = follow&.languages
|
|
||||||
|
|
||||||
FollowService.new.call(follower_account, new_target_account, reblogs: reblogs, notify: notify, languages: languages, bypass_locked: bypass_locked, bypass_limit: true)
|
|
||||||
UnfollowService.new.call(follower_account, old_target_account, skip_unmerge: true)
|
|
||||||
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
|
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,6 +5,7 @@ default: &default
|
||||||
connect_timeout: 15
|
connect_timeout: 15
|
||||||
encoding: unicode
|
encoding: unicode
|
||||||
sslmode: <%= ENV['DB_SSLMODE'] || "prefer" %>
|
sslmode: <%= ENV['DB_SSLMODE'] || "prefer" %>
|
||||||
|
application_name: ''
|
||||||
|
|
||||||
development:
|
development:
|
||||||
<<: *default
|
<<: *default
|
||||||
|
|
|
@ -87,6 +87,8 @@ Rails.application.configure do
|
||||||
config.x.otp_secret = ENV.fetch('OTP_SECRET', '1fc2b87989afa6351912abeebe31ffc5c476ead9bf8b3d74cbc4a302c7b69a45b40b1bbef3506ddad73e942e15ed5ca4b402bf9a66423626051104f4b5f05109')
|
config.x.otp_secret = ENV.fetch('OTP_SECRET', '1fc2b87989afa6351912abeebe31ffc5c476ead9bf8b3d74cbc4a302c7b69a45b40b1bbef3506ddad73e942e15ed5ca4b402bf9a66423626051104f4b5f05109')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Redis.raise_deprecations = true
|
||||||
|
|
||||||
ActiveRecordQueryTrace.enabled = ENV['QUERY_TRACE_ENABLED'] == 'true'
|
ActiveRecordQueryTrace.enabled = ENV['QUERY_TRACE_ENABLED'] == 'true'
|
||||||
|
|
||||||
module PrivateAddressCheck
|
module PrivateAddressCheck
|
||||||
|
|
|
@ -128,6 +128,7 @@ Rails.application.configure do
|
||||||
enable_starttls_auto: enable_starttls_auto,
|
enable_starttls_auto: enable_starttls_auto,
|
||||||
tls: ENV['SMTP_TLS'].presence && ENV['SMTP_TLS'] == 'true',
|
tls: ENV['SMTP_TLS'].presence && ENV['SMTP_TLS'] == 'true',
|
||||||
ssl: ENV['SMTP_SSL'].presence && ENV['SMTP_SSL'] == 'true',
|
ssl: ENV['SMTP_SSL'].presence && ENV['SMTP_SSL'] == 'true',
|
||||||
|
read_timeout: 20,
|
||||||
}
|
}
|
||||||
|
|
||||||
config.action_mailer.delivery_method = ENV.fetch('SMTP_DELIVERY_METHOD', 'smtp').to_sym
|
config.action_mailer.delivery_method = ENV.fetch('SMTP_DELIVERY_METHOD', 'smtp').to_sym
|
||||||
|
|
|
@ -73,3 +73,5 @@ end
|
||||||
|
|
||||||
# Catch serialization warnings early
|
# Catch serialization warnings early
|
||||||
Sidekiq.strict_args!
|
Sidekiq.strict_args!
|
||||||
|
|
||||||
|
Redis.raise_deprecations = true
|
||||||
|
|
|
@ -90,6 +90,12 @@ if ENV['S3_ENABLED'] == 'true'
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if ENV.has_key?('S3_STORAGE_CLASS')
|
||||||
|
Paperclip::Attachment.default_options[:s3_headers].merge!(
|
||||||
|
'X-Amz-Storage-Class' => ENV['S3_STORAGE_CLASS']
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
# Some S3-compatible providers might not actually be compatible with some APIs
|
# Some S3-compatible providers might not actually be compatible with some APIs
|
||||||
# used by kt-paperclip, see https://github.com/mastodon/mastodon/issues/16822
|
# used by kt-paperclip, see https://github.com/mastodon/mastodon/issues/16822
|
||||||
if ENV['S3_FORCE_SINGLE_REQUEST'] == 'true'
|
if ENV['S3_FORCE_SINGLE_REQUEST'] == 'true'
|
||||||
|
|
1
config/initializers/redis.rb
Normal file
1
config/initializers/redis.rb
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Redis.sadd_returns_boolean = false
|
|
@ -2,8 +2,8 @@
|
||||||
es:
|
es:
|
||||||
admin:
|
admin:
|
||||||
custom_emojis:
|
custom_emojis:
|
||||||
batch_copy_error: 'Se produjo un error cuando se copian algunos emojis seleccionados %{message}'
|
batch_copy_error: Se produjo un error cuando se copian algunos emojis seleccionados %{message}
|
||||||
batch_error: 'Ocurrió un error %{message}'
|
batch_error: Ocurrió un error %{message}
|
||||||
settings:
|
settings:
|
||||||
captcha_enabled:
|
captcha_enabled:
|
||||||
title: Requerir que usuarixs nuevxs resuelvan un CAPTCHA para confirmar su cuenta
|
title: Requerir que usuarixs nuevxs resuelvan un CAPTCHA para confirmar su cuenta
|
||||||
|
|
|
@ -1408,6 +1408,7 @@ en:
|
||||||
confirm_remove_selected_followers: Are you sure you want to remove selected followers?
|
confirm_remove_selected_followers: Are you sure you want to remove selected followers?
|
||||||
confirm_remove_selected_follows: Are you sure you want to remove selected follows?
|
confirm_remove_selected_follows: Are you sure you want to remove selected follows?
|
||||||
dormant: Dormant
|
dormant: Dormant
|
||||||
|
follow_failure: Could not follow some of the selected accounts.
|
||||||
follow_selected_followers: Follow selected followers
|
follow_selected_followers: Follow selected followers
|
||||||
followers: Followers
|
followers: Followers
|
||||||
following: Following
|
following: Following
|
||||||
|
@ -1447,6 +1448,7 @@ en:
|
||||||
electron: Electron
|
electron: Electron
|
||||||
firefox: Firefox
|
firefox: Firefox
|
||||||
generic: Unknown browser
|
generic: Unknown browser
|
||||||
|
huawei_browser: Huawei Browser
|
||||||
ie: Internet Explorer
|
ie: Internet Explorer
|
||||||
micro_messenger: MicroMessenger
|
micro_messenger: MicroMessenger
|
||||||
nokia: Nokia S40 Ovi Browser
|
nokia: Nokia S40 Ovi Browser
|
||||||
|
@ -1456,6 +1458,7 @@ en:
|
||||||
qq: QQ Browser
|
qq: QQ Browser
|
||||||
safari: Safari
|
safari: Safari
|
||||||
uc_browser: UC Browser
|
uc_browser: UC Browser
|
||||||
|
unknown_browser: Unknown Browser
|
||||||
weibo: Weibo
|
weibo: Weibo
|
||||||
current_session: Current session
|
current_session: Current session
|
||||||
description: "%{browser} on %{platform}"
|
description: "%{browser} on %{platform}"
|
||||||
|
@ -1468,9 +1471,10 @@ en:
|
||||||
chrome_os: ChromeOS
|
chrome_os: ChromeOS
|
||||||
firefox_os: Firefox OS
|
firefox_os: Firefox OS
|
||||||
ios: iOS
|
ios: iOS
|
||||||
|
kai_os: KaiOS
|
||||||
linux: Linux
|
linux: Linux
|
||||||
mac: macOS
|
mac: macOS
|
||||||
other: unknown platform
|
unknown_platform: Unknown Platform
|
||||||
windows: Windows
|
windows: Windows
|
||||||
windows_mobile: Windows Mobile
|
windows_mobile: Windows Mobile
|
||||||
windows_phone: Windows Phone
|
windows_phone: Windows Phone
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
class FixReblogsInFeeds < ActiveRecord::Migration[5.1]
|
class FixReblogsInFeeds < ActiveRecord::Migration[5.1]
|
||||||
def up
|
def up
|
||||||
redis = Redis.current
|
redis = RedisConfiguration.pool.checkout
|
||||||
fm = FeedManager.instance
|
fm = FeedManager.instance
|
||||||
|
|
||||||
# Old scheme:
|
# Old scheme:
|
||||||
|
|
|
@ -2,7 +2,8 @@ class MigrateUnavailableInboxes < ActiveRecord::Migration[5.2]
|
||||||
disable_ddl_transaction!
|
disable_ddl_transaction!
|
||||||
|
|
||||||
def up
|
def up
|
||||||
urls = Redis.current.smembers('unavailable_inboxes')
|
redis = RedisConfiguration.pool.checkout
|
||||||
|
urls = redis.smembers('unavailable_inboxes')
|
||||||
|
|
||||||
hosts = urls.map do |url|
|
hosts = urls.map do |url|
|
||||||
Addressable::URI.parse(url).normalized_host
|
Addressable::URI.parse(url).normalized_host
|
||||||
|
@ -14,7 +15,7 @@ class MigrateUnavailableInboxes < ActiveRecord::Migration[5.2]
|
||||||
UnavailableDomain.create(domain: host)
|
UnavailableDomain.create(domain: host)
|
||||||
end
|
end
|
||||||
|
|
||||||
Redis.current.del(*(['unavailable_inboxes'] + Redis.current.keys('exhausted_deliveries:*')))
|
redis.del(*(['unavailable_inboxes'] + redis.keys('exhausted_deliveries:*')))
|
||||||
end
|
end
|
||||||
|
|
||||||
def down; end
|
def down; end
|
||||||
|
|
|
@ -627,7 +627,7 @@ module Mastodon
|
||||||
exit(1)
|
exit(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
unless options[:force] || migration.target_acount_id == account.moved_to_account_id
|
unless options[:force] || migration.target_account_id == account.moved_to_account_id
|
||||||
say('The specified account is not redirecting to its last migration target. Use --force if you want to replay the migration anyway', :red)
|
say('The specified account is not redirecting to its last migration target. Use --force if you want to replay the migration anyway', :red)
|
||||||
exit(1)
|
exit(1)
|
||||||
end
|
end
|
||||||
|
|
|
@ -53,11 +53,7 @@ module Mastodon
|
||||||
desc 'clear', 'Remove all home and list feeds from Redis'
|
desc 'clear', 'Remove all home and list feeds from Redis'
|
||||||
def clear
|
def clear
|
||||||
keys = redis.keys('feed:*')
|
keys = redis.keys('feed:*')
|
||||||
|
redis.del(keys)
|
||||||
redis.pipelined do
|
|
||||||
keys.each { |key| redis.del(key) }
|
|
||||||
end
|
|
||||||
|
|
||||||
say('OK', :green)
|
say('OK', :green)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -75,7 +75,6 @@ class Sanitize
|
||||||
attributes: {
|
attributes: {
|
||||||
'a' => %w(href rel class title),
|
'a' => %w(href rel class title),
|
||||||
'span' => %w(class),
|
'span' => %w(class),
|
||||||
'abbr' => %w(title),
|
|
||||||
'blockquote' => %w(cite),
|
'blockquote' => %w(cite),
|
||||||
'ol' => %w(start reversed),
|
'ol' => %w(start reversed),
|
||||||
'li' => %w(value),
|
'li' => %w(value),
|
||||||
|
|
|
@ -85,6 +85,7 @@
|
||||||
"object.values": "^1.1.6",
|
"object.values": "^1.1.6",
|
||||||
"path-complete-extname": "^1.0.0",
|
"path-complete-extname": "^1.0.0",
|
||||||
"pg": "^8.5.0",
|
"pg": "^8.5.0",
|
||||||
|
"pg-connection-string": "^2.5.0",
|
||||||
"postcss": "^8.4.21",
|
"postcss": "^8.4.21",
|
||||||
"postcss-loader": "^3.0.0",
|
"postcss-loader": "^3.0.0",
|
||||||
"promise.prototype.finally": "^3.1.4",
|
"promise.prototype.finally": "^3.1.4",
|
||||||
|
|
23
spec/controllers/admin/account_actions_controller_spec.rb
Normal file
23
spec/controllers/admin/account_actions_controller_spec.rb
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe Admin::AccountActionsController do
|
||||||
|
render_views
|
||||||
|
|
||||||
|
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
sign_in user, scope: :user
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET #new' do
|
||||||
|
let(:account) { Fabricate(:account) }
|
||||||
|
|
||||||
|
it 'returns http success' do
|
||||||
|
get :new, params: { account_id: account.id }
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
21
spec/controllers/admin/announcements_controller_spec.rb
Normal file
21
spec/controllers/admin/announcements_controller_spec.rb
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe Admin::AnnouncementsController do
|
||||||
|
render_views
|
||||||
|
|
||||||
|
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
sign_in user, scope: :user
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET #index' do
|
||||||
|
it 'returns http success' do
|
||||||
|
get :index
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,21 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe Admin::FollowRecommendationsController do
|
||||||
|
render_views
|
||||||
|
|
||||||
|
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
sign_in user, scope: :user
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET #show' do
|
||||||
|
it 'returns http success' do
|
||||||
|
get :show
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
21
spec/controllers/admin/ip_blocks_controller_spec.rb
Normal file
21
spec/controllers/admin/ip_blocks_controller_spec.rb
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe Admin::IpBlocksController do
|
||||||
|
render_views
|
||||||
|
|
||||||
|
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
sign_in user, scope: :user
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET #index' do
|
||||||
|
it 'returns http success' do
|
||||||
|
get :index
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
23
spec/controllers/admin/relationships_controller_spec.rb
Normal file
23
spec/controllers/admin/relationships_controller_spec.rb
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe Admin::RelationshipsController do
|
||||||
|
render_views
|
||||||
|
|
||||||
|
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
sign_in user, scope: :user
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET #index' do
|
||||||
|
let(:account) { Fabricate(:account) }
|
||||||
|
|
||||||
|
it 'returns http success' do
|
||||||
|
get :index, params: { account_id: account.id }
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
21
spec/controllers/admin/relays_controller_spec.rb
Normal file
21
spec/controllers/admin/relays_controller_spec.rb
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe Admin::RelaysController do
|
||||||
|
render_views
|
||||||
|
|
||||||
|
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
sign_in user, scope: :user
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET #index' do
|
||||||
|
it 'returns http success' do
|
||||||
|
get :index
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -57,6 +57,9 @@ describe Admin::Reports::ActionsController do
|
||||||
let!(:media) { Fabricate(:media_attachment, account: target_account, status: statuses[0]) }
|
let!(:media) { Fabricate(:media_attachment, account: target_account, status: statuses[0]) }
|
||||||
let(:report) { Fabricate(:report, target_account: target_account, status_ids: statuses.map(&:id)) }
|
let(:report) { Fabricate(:report, target_account: target_account, status_ids: statuses.map(&:id)) }
|
||||||
let(:text) { 'hello' }
|
let(:text) { 'hello' }
|
||||||
|
let(:common_params) do
|
||||||
|
{ report_id: report.id, text: text }
|
||||||
|
end
|
||||||
|
|
||||||
shared_examples 'common behavior' do
|
shared_examples 'common behavior' do
|
||||||
it 'closes the report' do
|
it 'closes the report' do
|
||||||
|
@ -72,6 +75,26 @@ describe Admin::Reports::ActionsController do
|
||||||
subject
|
subject
|
||||||
expect(response).to redirect_to(admin_reports_path)
|
expect(response).to redirect_to(admin_reports_path)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when text is unset' do
|
||||||
|
let(:common_params) do
|
||||||
|
{ report_id: report.id }
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'closes the report' do
|
||||||
|
expect { subject }.to change { report.reload.action_taken? }.from(false).to(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates a strike with the expected text' do
|
||||||
|
expect { subject }.to change { report.target_account.strikes.count }.by(1)
|
||||||
|
expect(report.target_account.strikes.last.text).to eq ''
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'redirects' do
|
||||||
|
subject
|
||||||
|
expect(response).to redirect_to(admin_reports_path)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
shared_examples 'all action types' do
|
shared_examples 'all action types' do
|
||||||
|
@ -124,13 +147,13 @@ describe Admin::Reports::ActionsController do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'action as submit button' do
|
context 'action as submit button' do
|
||||||
subject { post :create, params: { report_id: report.id, text: text, action => '' } }
|
subject { post :create, params: common_params.merge({ action => '' }) }
|
||||||
|
|
||||||
it_behaves_like 'all action types'
|
it_behaves_like 'all action types'
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'action as submit button' do
|
context 'action as submit button' do
|
||||||
subject { post :create, params: { report_id: report.id, text: text, moderation_action: action } }
|
subject { post :create, params: common_params.merge({ moderation_action: action }) }
|
||||||
|
|
||||||
it_behaves_like 'all action types'
|
it_behaves_like 'all action types'
|
||||||
end
|
end
|
||||||
|
|
21
spec/controllers/admin/rules_controller_spec.rb
Normal file
21
spec/controllers/admin/rules_controller_spec.rb
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe Admin::RulesController do
|
||||||
|
render_views
|
||||||
|
|
||||||
|
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
sign_in user, scope: :user
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET #index' do
|
||||||
|
it 'returns http success' do
|
||||||
|
get :index
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
21
spec/controllers/admin/settings/about_controller_spec.rb
Normal file
21
spec/controllers/admin/settings/about_controller_spec.rb
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe Admin::Settings::AboutController do
|
||||||
|
render_views
|
||||||
|
|
||||||
|
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
sign_in user, scope: :user
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET #show' do
|
||||||
|
it 'returns http success' do
|
||||||
|
get :show
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,21 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe Admin::Settings::AppearanceController do
|
||||||
|
render_views
|
||||||
|
|
||||||
|
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
sign_in user, scope: :user
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET #show' do
|
||||||
|
it 'returns http success' do
|
||||||
|
get :show
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,21 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe Admin::Settings::ContentRetentionController do
|
||||||
|
render_views
|
||||||
|
|
||||||
|
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
sign_in user, scope: :user
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET #show' do
|
||||||
|
it 'returns http success' do
|
||||||
|
get :show
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
21
spec/controllers/admin/settings/discovery_controller_spec.rb
Normal file
21
spec/controllers/admin/settings/discovery_controller_spec.rb
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe Admin::Settings::DiscoveryController do
|
||||||
|
render_views
|
||||||
|
|
||||||
|
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
sign_in user, scope: :user
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET #show' do
|
||||||
|
it 'returns http success' do
|
||||||
|
get :show
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,21 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe Admin::Settings::RegistrationsController do
|
||||||
|
render_views
|
||||||
|
|
||||||
|
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
sign_in user, scope: :user
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET #show' do
|
||||||
|
it 'returns http success' do
|
||||||
|
get :show
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
23
spec/controllers/admin/site_uploads_controller_spec.rb
Normal file
23
spec/controllers/admin/site_uploads_controller_spec.rb
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe Admin::SiteUploadsController do
|
||||||
|
render_views
|
||||||
|
|
||||||
|
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
sign_in user, scope: :user
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'DELETE #destroy' do
|
||||||
|
let(:site_upload) { Fabricate(:site_upload, var: 'thumbnail') }
|
||||||
|
|
||||||
|
it 'returns http success' do
|
||||||
|
delete :destroy, params: { id: site_upload.id }
|
||||||
|
|
||||||
|
expect(response).to redirect_to(admin_settings_path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,21 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe Admin::Trends::Links::PreviewCardProvidersController do
|
||||||
|
render_views
|
||||||
|
|
||||||
|
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
sign_in user, scope: :user
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET #index' do
|
||||||
|
it 'returns http success' do
|
||||||
|
get :index
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
21
spec/controllers/admin/trends/links_controller_spec.rb
Normal file
21
spec/controllers/admin/trends/links_controller_spec.rb
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe Admin::Trends::LinksController do
|
||||||
|
render_views
|
||||||
|
|
||||||
|
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
sign_in user, scope: :user
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET #index' do
|
||||||
|
it 'returns http success' do
|
||||||
|
get :index
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
21
spec/controllers/admin/trends/statuses_controller_spec.rb
Normal file
21
spec/controllers/admin/trends/statuses_controller_spec.rb
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe Admin::Trends::StatusesController do
|
||||||
|
render_views
|
||||||
|
|
||||||
|
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
sign_in user, scope: :user
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET #index' do
|
||||||
|
it 'returns http success' do
|
||||||
|
get :index
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
21
spec/controllers/admin/trends/tags_controller_spec.rb
Normal file
21
spec/controllers/admin/trends/tags_controller_spec.rb
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe Admin::Trends::TagsController do
|
||||||
|
render_views
|
||||||
|
|
||||||
|
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
sign_in user, scope: :user
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET #index' do
|
||||||
|
it 'returns http success' do
|
||||||
|
get :index
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
21
spec/controllers/admin/warning_presets_controller_spec.rb
Normal file
21
spec/controllers/admin/warning_presets_controller_spec.rb
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe Admin::WarningPresetsController do
|
||||||
|
render_views
|
||||||
|
|
||||||
|
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
sign_in user, scope: :user
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET #index' do
|
||||||
|
it 'returns http success' do
|
||||||
|
get :index
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
23
spec/controllers/admin/webhooks/secrets_controller_spec.rb
Normal file
23
spec/controllers/admin/webhooks/secrets_controller_spec.rb
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe Admin::Webhooks::SecretsController do
|
||||||
|
render_views
|
||||||
|
|
||||||
|
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
sign_in user, scope: :user
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'POST #rotate' do
|
||||||
|
let(:webhook) { Fabricate(:webhook) }
|
||||||
|
|
||||||
|
it 'returns http success' do
|
||||||
|
post :rotate, params: { webhook_id: webhook.id }
|
||||||
|
|
||||||
|
expect(response).to redirect_to(admin_webhook_path(webhook))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
21
spec/controllers/admin/webhooks_controller_spec.rb
Normal file
21
spec/controllers/admin/webhooks_controller_spec.rb
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe Admin::WebhooksController do
|
||||||
|
render_views
|
||||||
|
|
||||||
|
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
sign_in user, scope: :user
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET #index' do
|
||||||
|
it 'returns http success' do
|
||||||
|
get :index
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,23 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe Api::V1::Accounts::FamiliarFollowersController do
|
||||||
|
render_views
|
||||||
|
|
||||||
|
let(:user) { Fabricate(:user) }
|
||||||
|
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:follows') }
|
||||||
|
let(:account) { Fabricate(:account) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(controller).to receive(:doorkeeper_token) { token }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET #index' do
|
||||||
|
it 'returns http success' do
|
||||||
|
get :index, params: { account_id: account.id, limit: 2 }
|
||||||
|
|
||||||
|
expect(response).to have_http_status(200)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,23 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe Api::V1::Accounts::FeaturedTagsController do
|
||||||
|
render_views
|
||||||
|
|
||||||
|
let(:user) { Fabricate(:user) }
|
||||||
|
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:accounts') }
|
||||||
|
let(:account) { Fabricate(:account) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(controller).to receive(:doorkeeper_token) { token }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET #index' do
|
||||||
|
it 'returns http success' do
|
||||||
|
get :index, params: { account_id: account.id, limit: 2 }
|
||||||
|
|
||||||
|
expect(response).to have_http_status(200)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,23 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe Api::V1::Accounts::IdentityProofsController do
|
||||||
|
render_views
|
||||||
|
|
||||||
|
let(:user) { Fabricate(:user) }
|
||||||
|
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:accounts') }
|
||||||
|
let(:account) { Fabricate(:account) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(controller).to receive(:doorkeeper_token) { token }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET #index' do
|
||||||
|
it 'returns http success' do
|
||||||
|
get :index, params: { account_id: account.id, limit: 2 }
|
||||||
|
|
||||||
|
expect(response).to have_http_status(200)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
23
spec/controllers/api/v1/accounts/lookup_controller_spec.rb
Normal file
23
spec/controllers/api/v1/accounts/lookup_controller_spec.rb
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe Api::V1::Accounts::LookupController do
|
||||||
|
render_views
|
||||||
|
|
||||||
|
let(:user) { Fabricate(:user) }
|
||||||
|
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:accounts') }
|
||||||
|
let(:account) { Fabricate(:account) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(controller).to receive(:doorkeeper_token) { token }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET #show' do
|
||||||
|
it 'returns http success' do
|
||||||
|
get :show, params: { account_id: account.id, acct: account.acct }
|
||||||
|
|
||||||
|
expect(response).to have_http_status(200)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,23 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe Api::V1::Admin::CanonicalEmailBlocksController do
|
||||||
|
render_views
|
||||||
|
|
||||||
|
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
|
||||||
|
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'admin:read') }
|
||||||
|
let(:account) { Fabricate(:account) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(controller).to receive(:doorkeeper_token) { token }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET #index' do
|
||||||
|
it 'returns http success' do
|
||||||
|
get :index, params: { account_id: account.id, limit: 2 }
|
||||||
|
|
||||||
|
expect(response).to have_http_status(200)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
23
spec/controllers/api/v1/admin/dimensions_controller_spec.rb
Normal file
23
spec/controllers/api/v1/admin/dimensions_controller_spec.rb
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe Api::V1::Admin::DimensionsController do
|
||||||
|
render_views
|
||||||
|
|
||||||
|
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
|
||||||
|
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'admin:read') }
|
||||||
|
let(:account) { Fabricate(:account) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(controller).to receive(:doorkeeper_token) { token }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'POST #create' do
|
||||||
|
it 'returns http success' do
|
||||||
|
post :create, params: { account_id: account.id, limit: 2 }
|
||||||
|
|
||||||
|
expect(response).to have_http_status(200)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,23 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe Api::V1::Admin::EmailDomainBlocksController do
|
||||||
|
render_views
|
||||||
|
|
||||||
|
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
|
||||||
|
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'admin:read') }
|
||||||
|
let(:account) { Fabricate(:account) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(controller).to receive(:doorkeeper_token) { token }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET #index' do
|
||||||
|
it 'returns http success' do
|
||||||
|
get :index, params: { account_id: account.id, limit: 2 }
|
||||||
|
|
||||||
|
expect(response).to have_http_status(200)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
23
spec/controllers/api/v1/admin/ip_blocks_controller_spec.rb
Normal file
23
spec/controllers/api/v1/admin/ip_blocks_controller_spec.rb
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe Api::V1::Admin::IpBlocksController do
|
||||||
|
render_views
|
||||||
|
|
||||||
|
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
|
||||||
|
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'admin:read') }
|
||||||
|
let(:account) { Fabricate(:account) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(controller).to receive(:doorkeeper_token) { token }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET #index' do
|
||||||
|
it 'returns http success' do
|
||||||
|
get :index, params: { account_id: account.id, limit: 2 }
|
||||||
|
|
||||||
|
expect(response).to have_http_status(200)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
23
spec/controllers/api/v1/admin/measures_controller_spec.rb
Normal file
23
spec/controllers/api/v1/admin/measures_controller_spec.rb
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe Api::V1::Admin::MeasuresController do
|
||||||
|
render_views
|
||||||
|
|
||||||
|
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
|
||||||
|
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'admin:read') }
|
||||||
|
let(:account) { Fabricate(:account) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(controller).to receive(:doorkeeper_token) { token }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'POST #create' do
|
||||||
|
it 'returns http success' do
|
||||||
|
post :create, params: { account_id: account.id, limit: 2 }
|
||||||
|
|
||||||
|
expect(response).to have_http_status(200)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
23
spec/controllers/api/v1/admin/retention_controller_spec.rb
Normal file
23
spec/controllers/api/v1/admin/retention_controller_spec.rb
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe Api::V1::Admin::RetentionController do
|
||||||
|
render_views
|
||||||
|
|
||||||
|
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
|
||||||
|
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'admin:read') }
|
||||||
|
let(:account) { Fabricate(:account) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(controller).to receive(:doorkeeper_token) { token }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'POST #create' do
|
||||||
|
it 'returns http success' do
|
||||||
|
post :create, params: { account_id: account.id, limit: 2 }
|
||||||
|
|
||||||
|
expect(response).to have_http_status(200)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,23 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe Api::V1::Admin::Trends::LinksController do
|
||||||
|
render_views
|
||||||
|
|
||||||
|
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
|
||||||
|
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'admin:read') }
|
||||||
|
let(:account) { Fabricate(:account) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(controller).to receive(:doorkeeper_token) { token }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET #index' do
|
||||||
|
it 'returns http success' do
|
||||||
|
get :index, params: { account_id: account.id, limit: 2 }
|
||||||
|
|
||||||
|
expect(response).to have_http_status(200)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,23 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe Api::V1::Admin::Trends::StatusesController do
|
||||||
|
render_views
|
||||||
|
|
||||||
|
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
|
||||||
|
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'admin:read') }
|
||||||
|
let(:account) { Fabricate(:account) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(controller).to receive(:doorkeeper_token) { token }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET #index' do
|
||||||
|
it 'returns http success' do
|
||||||
|
get :index, params: { account_id: account.id, limit: 2 }
|
||||||
|
|
||||||
|
expect(response).to have_http_status(200)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue