From 4a98e77d0e7fec15e1830059a9a56bf56aa391db Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 8 Oct 2019 05:59:10 +0200 Subject: [PATCH 001/202] Change `tootctl media refresh` to skip already downloaded attachments (#12118) --- lib/mastodon/media_cli.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/mastodon/media_cli.rb b/lib/mastodon/media_cli.rb index bb97751d5..e48175134 100644 --- a/lib/mastodon/media_cli.rb +++ b/lib/mastodon/media_cli.rb @@ -50,6 +50,7 @@ module Mastodon option :concurrency, type: :numeric, default: 5, aliases: [:c] option :verbose, type: :boolean, default: false, aliases: [:v] option :dry_run, type: :boolean, default: false + option :force, type: :boolean, default: false desc 'refresh', 'Fetch remote media files' long_desc <<-DESC Re-downloads media attachments from other servers. You must specify the @@ -62,6 +63,9 @@ module Mastodon using username@domain handle of the account. Use the --domain option to download attachments from a specific domain. + + By default, attachments that are believed to be already downloaded will + not be re-downloaded. To force re-download of every URL, use --force. DESC def refresh dry_run = options[:dry_run] ? ' (DRY RUN)' : '' @@ -85,7 +89,7 @@ module Mastodon end processed, aggregate = parallelize_with_progress(scope) do |media_attachment| - next if media_attachment.remote_url.blank? + next if media_attachment.remote_url.blank? || (!options[:force] && media_attachment.file_file_name.present?) unless options[:dry_run] media_attachment.reset_file! From 19cdc627658166664fb1571ec45564d237e63757 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 8 Oct 2019 22:08:55 +0200 Subject: [PATCH 002/202] Remove fallback to long description on sidebar and meta description (#12119) Fix #12114 --- app/views/about/show.html.haml | 13 ++++++------- app/views/application/_sidebar.html.haml | 2 +- app/views/shared/_og.html.haml | 2 +- config/locales/en.yml | 3 +-- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/app/views/about/show.html.haml b/app/views/about/show.html.haml index f24f4e195..80f4cd828 100644 --- a/app/views/about/show.html.haml +++ b/app/views/about/show.html.haml @@ -52,13 +52,12 @@ .hero-widget__img = image_tag @instance_presenter.hero&.file&.url || @instance_presenter.thumbnail&.file&.url || asset_pack_path('media/images/preview.jpg'), alt: @instance_presenter.site_title - - if @instance_presenter.site_short_description.present? - .hero-widget__text - %p - = @instance_presenter.site_short_description.html_safe.presence - = link_to about_more_path do - = t('about.learn_more') - = fa_icon 'angle-double-right' + .hero-widget__text + %p + = @instance_presenter.site_short_description.html_safe.presence || t('about.about_mastodon_html') + = link_to about_more_path do + = t('about.learn_more') + = fa_icon 'angle-double-right' .hero-widget__footer .hero-widget__footer__column diff --git a/app/views/application/_sidebar.html.haml b/app/views/application/_sidebar.html.haml index 33e7c96fe..7ec91c06a 100644 --- a/app/views/application/_sidebar.html.haml +++ b/app/views/application/_sidebar.html.haml @@ -3,7 +3,7 @@ = image_tag @instance_presenter.hero&.file&.url || @instance_presenter.thumbnail&.file&.url || asset_pack_path('media/images/preview.jpg'), alt: @instance_presenter.site_title .hero-widget__text - %p= @instance_presenter.site_short_description.html_safe.presence || @instance_presenter.site_description.html_safe.presence || t('about.generic_description', domain: site_hostname) + %p= @instance_presenter.site_short_description.html_safe.presence || t('about.about_mastodon_html') - if Setting.trends && !(user_signed_in? && !current_user.setting_trends) - trends = TrendingTags.get(3) diff --git a/app/views/shared/_og.html.haml b/app/views/shared/_og.html.haml index 576f47a67..c8f12974e 100644 --- a/app/views/shared/_og.html.haml +++ b/app/views/shared/_og.html.haml @@ -1,5 +1,5 @@ - thumbnail = @instance_presenter.thumbnail -- description ||= strip_tags(@instance_presenter.site_short_description.presence || @instance_presenter.site_description.presence || t('about.about_mastodon_html')) +- description ||= strip_tags(@instance_presenter.site_short_description.presence || t('about.about_mastodon_html')) %meta{ name: 'description', content: description }/ diff --git a/config/locales/en.yml b/config/locales/en.yml index 4b9f2aab4..68fc21323 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -2,7 +2,7 @@ en: about: about_hashtag_html: These are public toots tagged with #%{hashtag}. You can interact with them if you have an account anywhere in the fediverse. - about_mastodon_html: Mastodon is a social network based on open web protocols and free, open-source software. It is decentralized like e-mail. + about_mastodon_html: 'The social network of the future: No ads, no corporate surveillance, ethical design, and decentralization! Own your data with Mastodon!' about_this: About active_count_after: active active_footnote: Monthly Active Users (MAU) @@ -18,7 +18,6 @@ en: discover_users: Discover users documentation: Documentation federation_hint_html: With an account on %{instance} you'll be able to follow people on any Mastodon server and beyond. - generic_description: "%{domain} is one server in the network" get_apps: Try a mobile app hosted_on: Mastodon hosted on %{domain} instance_actor_flash: | From c8bcf5cbfdc4b076eae0d9091e688436aa7f2508 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 9 Oct 2019 00:30:15 +0200 Subject: [PATCH 003/202] Add admin setting to auto-approve hashtags (#12122) Change inaccurate labels on other admin settings --- app/models/form/admin_settings.rb | 2 ++ app/models/tag.rb | 2 +- app/views/admin/settings/edit.html.haml | 9 ++++++--- config/locales/en.yml | 11 +++++++---- config/settings.yml | 1 + 5 files changed, 17 insertions(+), 8 deletions(-) diff --git a/app/models/form/admin_settings.rb b/app/models/form/admin_settings.rb index 24196e182..70e9c21f1 100644 --- a/app/models/form/admin_settings.rb +++ b/app/models/form/admin_settings.rb @@ -30,6 +30,7 @@ class Form::AdminSettings mascot spam_check_enabled trends + trendable_by_default show_domain_blocks show_domain_blocks_rationale noindex @@ -46,6 +47,7 @@ class Form::AdminSettings profile_directory spam_check_enabled trends + trendable_by_default noindex ).freeze diff --git a/app/models/tag.rb b/app/models/tag.rb index 82786daa8..59445a83b 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -76,7 +76,7 @@ class Tag < ApplicationRecord alias listable? listable def trendable - boolean_with_default('trendable', false) + boolean_with_default('trendable', Setting.trendable_by_default) end alias trendable? trendable diff --git a/app/views/admin/settings/edit.html.haml b/app/views/admin/settings/edit.html.haml index 752386b3c..6282bb39c 100644 --- a/app/views/admin/settings/edit.html.haml +++ b/app/views/admin/settings/edit.html.haml @@ -20,10 +20,10 @@ = f.input :site_contact_email, wrapper: :with_label, label: t('admin.settings.contact_information.email') .fields-group - = f.input :site_description, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_description.title'), hint: t('admin.settings.site_description.desc_html'), input_html: { rows: 4 } + = f.input :site_short_description, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_short_description.title'), hint: t('admin.settings.site_short_description.desc_html'), input_html: { rows: 2 } .fields-group - = f.input :site_short_description, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_short_description.title'), hint: t('admin.settings.site_short_description.desc_html'), input_html: { rows: 2 } + = f.input :site_description, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_description.title'), hint: t('admin.settings.site_description.desc_html'), input_html: { rows: 2 } .fields-row .fields-row__column.fields-row__column-6.fields-group @@ -71,6 +71,9 @@ .fields-group = f.input :trends, as: :boolean, wrapper: :with_label, label: t('admin.settings.trends.title'), hint: t('admin.settings.trends.desc_html') + .fields-group + = f.input :trendable_by_default, as: :boolean, wrapper: :with_label, label: t('admin.settings.trendable_by_default.title'), hint: t('admin.settings.trendable_by_default.desc_html') + .fields-group = f.input :noindex, as: :boolean, wrapper: :with_label, label: t('admin.settings.default_noindex.title'), hint: t('admin.settings.default_noindex.desc_html') @@ -89,8 +92,8 @@ = f.input :show_domain_blocks_rationale, wrapper: :with_label, collection: %i(disabled users all), label: t('admin.settings.domain_blocks_rationale.title'), label_method: lambda { |value| t("admin.settings.domain_blocks.#{value}") }, include_blank: false, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li' .fields-group - = f.input :closed_registrations_message, as: :text, wrapper: :with_block_label, label: t('admin.settings.registrations.closed_message.title'), hint: t('admin.settings.registrations.closed_message.desc_html'), input_html: { rows: 8 } = f.input :site_extended_description, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_description_extended.title'), hint: t('admin.settings.site_description_extended.desc_html'), input_html: { rows: 8 } unless whitelist_mode? + = f.input :closed_registrations_message, as: :text, wrapper: :with_block_label, label: t('admin.settings.registrations.closed_message.title'), hint: t('admin.settings.registrations.closed_message.desc_html'), input_html: { rows: 8 } = f.input :site_terms, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_terms.title'), hint: t('admin.settings.site_terms.desc_html'), input_html: { rows: 8 } = f.input :custom_css, wrapper: :with_block_label, as: :text, input_html: { rows: 8 }, label: t('admin.settings.custom_css.title'), hint: t('admin.settings.custom_css.desc_html') diff --git a/config/locales/en.yml b/config/locales/en.yml index 68fc21323..0e8ee6a76 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -478,8 +478,8 @@ en: open: Anyone can sign up title: Registrations mode show_known_fediverse_at_about_page: - desc_html: When toggled, it will show toots from all the known fediverse on preview. Otherwise it will only show local toots. - title: Show known fediverse on timeline preview + desc_html: When disabled, restricts the public timeline linked from the landing page to showing only local content + title: Include federated content on unauthenticated public timeline page show_staff_badge: desc_html: Show a staff badge on a user page title: Show staff badge @@ -503,9 +503,12 @@ en: desc_html: Used for previews via OpenGraph and API. 1200x630px recommended title: Server thumbnail timeline_preview: - desc_html: Display public timeline on landing page - title: Timeline preview + desc_html: Display link to public timeline on landing page and allow API access to the public timeline without authentication + title: Allow unauthenticated access to public timeline title: Site settings + trendable_by_default: + desc_html: Affects hashtags that have not been previously disallowed + title: Allow hashtags to trend without prior review trends: desc_html: Publicly display previously reviewed hashtags that are currently trending title: Trending hashtags diff --git a/config/settings.yml b/config/settings.yml index 6dbc46706..bd2f65b5e 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -35,6 +35,7 @@ defaults: &defaults use_blurhash: true use_pending_items: false trends: true + trendable_by_default: false notification_emails: follow: false reblog: false From 538db85d3cc6d8fcb3c0a89f7eef069a686c19f4 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 9 Oct 2019 03:45:05 +0200 Subject: [PATCH 004/202] Remove `lang` attribute from individual statuses (#12124) Fix #10930 --- app/javascript/mastodon/components/status_content.js | 8 ++++---- app/views/statuses/_detailed_status.html.haml | 2 +- app/views/statuses/_simple_status.html.haml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/javascript/mastodon/components/status_content.js b/app/javascript/mastodon/components/status_content.js index c171e7a66..4ce9ec49f 100644 --- a/app/javascript/mastodon/components/status_content.js +++ b/app/javascript/mastodon/components/status_content.js @@ -216,14 +216,14 @@ export default class StatusContent extends React.PureComponent { return (
{mentionsPlaceholder} -
+
{!hidden && !!status.get('poll') && }
@@ -231,7 +231,7 @@ export default class StatusContent extends React.PureComponent { } else if (this.props.onClick) { const output = [
-
+
{!!status.get('poll') && }
, @@ -245,7 +245,7 @@ export default class StatusContent extends React.PureComponent { } else { return (
-
+
{!!status.get('poll') && }
diff --git a/app/views/statuses/_detailed_status.html.haml b/app/views/statuses/_detailed_status.html.haml index 12f03ccdd..5cee84ada 100644 --- a/app/views/statuses/_detailed_status.html.haml +++ b/app/views/statuses/_detailed_status.html.haml @@ -20,7 +20,7 @@ %p{ :style => ('margin-bottom: 0' unless current_account&.user&.setting_expand_spoilers) }< %span.p-summary> #{Formatter.instance.format_spoiler(status, autoplay: autoplay)}  %button.status__content__spoiler-link= t('statuses.show_more') - .e-content{ lang: status.language, style: "display: #{!current_account&.user&.setting_expand_spoilers && status.spoiler_text? ? 'none' : 'block'}; direction: #{rtl_status?(status) ? 'rtl' : 'ltr'}" } + .e-content{ style: "display: #{!current_account&.user&.setting_expand_spoilers && status.spoiler_text? ? 'none' : 'block'}; direction: #{rtl_status?(status) ? 'rtl' : 'ltr'}" } = Formatter.instance.format(status, custom_emojify: true, autoplay: autoplay) - if status.preloadable_poll = react_component :poll, disabled: true, poll: ActiveModelSerializers::SerializableResource.new(status.preloadable_poll, serializer: REST::PollSerializer, scope: current_user, scope_name: :current_user).as_json do diff --git a/app/views/statuses/_simple_status.html.haml b/app/views/statuses/_simple_status.html.haml index ca3c8fdd5..a68fe1022 100644 --- a/app/views/statuses/_simple_status.html.haml +++ b/app/views/statuses/_simple_status.html.haml @@ -24,7 +24,7 @@ %p{ :style => ('margin-bottom: 0' unless current_account&.user&.setting_expand_spoilers) }< %span.p-summary> #{Formatter.instance.format_spoiler(status, autoplay: autoplay)}  %button.status__content__spoiler-link= t('statuses.show_more') - .e-content{ lang: status.language, style: "display: #{!current_account&.user&.setting_expand_spoilers && status.spoiler_text? ? 'none' : 'block'}; direction: #{rtl_status?(status) ? 'rtl' : 'ltr'}" } + .e-content{ style: "display: #{!current_account&.user&.setting_expand_spoilers && status.spoiler_text? ? 'none' : 'block'}; direction: #{rtl_status?(status) ? 'rtl' : 'ltr'}" } = Formatter.instance.format(status, custom_emojify: true, autoplay: autoplay) - if status.preloadable_poll = react_component :poll, disabled: true, poll: ActiveModelSerializers::SerializableResource.new(status.preloadable_poll, serializer: REST::PollSerializer, scope: current_user, scope_name: :current_user).as_json do From 354fdd317e9c495ed721013911bc5274d5e0e1f8 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 9 Oct 2019 07:10:46 +0200 Subject: [PATCH 005/202] Fix attachment not being re-downloaded even if file is not stored (#12125) Change the behaviour of remotable concern. Previously, it would skip downloading an attachment if the stored remote URL is identical to the new one. Now it would not be skipped if the attachment is not actually currently stored by Paperclip. --- .../api/v1/streaming_controller.rb | 14 ++++++++++---- app/models/account.rb | 7 +++---- app/models/concerns/remotable.rb | 2 +- config/initializers/paperclip.rb | 19 +++++++++++++------ spec/models/account_spec.rb | 4 ++-- spec/models/concerns/remotable_spec.rb | 13 ++++++++++++- 6 files changed, 41 insertions(+), 18 deletions(-) diff --git a/app/controllers/api/v1/streaming_controller.rb b/app/controllers/api/v1/streaming_controller.rb index 66b812e76..ebb17608c 100644 --- a/app/controllers/api/v1/streaming_controller.rb +++ b/app/controllers/api/v1/streaming_controller.rb @@ -5,11 +5,17 @@ class Api::V1::StreamingController < Api::BaseController def index if Rails.configuration.x.streaming_api_base_url != request.host - uri = URI.parse(request.url) - uri.host = URI.parse(Rails.configuration.x.streaming_api_base_url).host - redirect_to uri.to_s, status: 301 + redirect_to streaming_api_url, status: 301 else - raise ActiveRecord::RecordNotFound + not_found end end + + private + + def streaming_api_url + Addressable::URI.parse(request.url).tap do |uri| + uri.host = Addressable::URI.parse(Rails.configuration.x.streaming_api_base_url).host + end.to_s + end end diff --git a/app/models/account.rb b/app/models/account.rb index 01d45e36c..2f43f337f 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -310,10 +310,9 @@ class Account < ApplicationRecord def save_with_optional_media! save! rescue ActiveRecord::RecordInvalid - self.avatar = nil - self.header = nil - self[:avatar_remote_url] = '' - self[:header_remote_url] = '' + self.avatar = nil + self.header = nil + save! end diff --git a/app/models/concerns/remotable.rb b/app/models/concerns/remotable.rb index 082302619..b7a476c87 100644 --- a/app/models/concerns/remotable.rb +++ b/app/models/concerns/remotable.rb @@ -18,7 +18,7 @@ module Remotable return end - return if !%w(http https).include?(parsed_url.scheme) || parsed_url.host.blank? || self[attribute_name] == url + return if !%w(http https).include?(parsed_url.scheme) || parsed_url.host.blank? || (self[attribute_name] == url && send("#{attachment_name}_file_name").present?) begin Request.new(:get, url).perform do |response| diff --git a/config/initializers/paperclip.rb b/config/initializers/paperclip.rb index a0253f4bc..d3602e655 100644 --- a/config/initializers/paperclip.rb +++ b/config/initializers/paperclip.rb @@ -1,10 +1,11 @@ # frozen_string_literal: true -Paperclip.options[:read_timeout] = 60 - Paperclip.interpolates :filename do |attachment, style| - return attachment.original_filename if style == :original - [basename(attachment, style), extension(attachment, style)].delete_if(&:blank?).join('.') + if style == :original + attachment.original_filename + else + [basename(attachment, style), extension(attachment, style)].delete_if(&:blank?).join('.') + end end Paperclip::Attachment.default_options.merge!( @@ -24,17 +25,21 @@ if ENV['S3_ENABLED'] == 'true' storage: :s3, s3_protocol: s3_protocol, s3_host_name: s3_hostname, + s3_headers: { 'X-Amz-Multipart-Threshold' => ENV.fetch('S3_MULTIPART_THRESHOLD') { 15.megabytes }.to_i, 'Cache-Control' => 'public, max-age=315576000, immutable', }, + s3_permissions: ENV.fetch('S3_PERMISSION') { 'public-read' }, s3_region: s3_region, + s3_credentials: { bucket: ENV['S3_BUCKET'], access_key_id: ENV['AWS_ACCESS_KEY_ID'], secret_access_key: ENV['AWS_SECRET_ACCESS_KEY'], }, + s3_options: { signature_version: ENV.fetch('S3_SIGNATURE_VERSION') { 'v4' }, http_open_timeout: 5, @@ -49,6 +54,7 @@ if ENV['S3_ENABLED'] == 'true' endpoint: ENV['S3_ENDPOINT'], force_path_style: true ) + Paperclip::Attachment.default_options[:url] = ':s3_path_url' end @@ -74,6 +80,7 @@ elsif ENV['SWIFT_ENABLED'] == 'true' openstack_region: ENV['SWIFT_REGION'], openstack_cache_ttl: ENV.fetch('SWIFT_CACHE_TTL') { 60 }, }, + fog_directory: ENV['SWIFT_CONTAINER'], fog_host: ENV['SWIFT_OBJECT_URL'], fog_public: true @@ -82,7 +89,7 @@ else Paperclip::Attachment.default_options.merge!( storage: :filesystem, use_timestamp: true, - path: (ENV['PAPERCLIP_ROOT_PATH'] || ':rails_root/public/system') + '/:class/:attachment/:id_partition/:style/:filename', - url: (ENV['PAPERCLIP_ROOT_URL'] || '/system') + '/:class/:attachment/:id_partition/:style/:filename', + path: ENV.fetch('PAPERCLIP_ROOT_PATH', ':rails_root/public/system') + '/:class/:attachment/:id_partition/:style/:filename', + url: ENV.fetch('PAPERCLIP_ROOT_URL', '/system') + '/:class/:attachment/:id_partition/:style/:filename', ) end diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb index 3eec464bd..b2f6234cb 100644 --- a/spec/models/account_spec.rb +++ b/spec/models/account_spec.rb @@ -126,8 +126,8 @@ RSpec.describe Account, type: :model do end it 'sets default avatar, header, avatar_remote_url, and header_remote_url' do - expect(account.avatar_remote_url).to eq '' - expect(account.header_remote_url).to eq '' + expect(account.avatar_remote_url).to eq 'https://remote.test/invalid_avatar' + expect(account.header_remote_url).to eq expectation.header_remote_url expect(account.avatar_file_name).to eq nil expect(account.header_file_name).to eq nil end diff --git a/spec/models/concerns/remotable_spec.rb b/spec/models/concerns/remotable_spec.rb index a4289cc45..99a60cbf6 100644 --- a/spec/models/concerns/remotable_spec.rb +++ b/spec/models/concerns/remotable_spec.rb @@ -18,6 +18,8 @@ RSpec.describe Remotable do def hoge=(arg); end + def hoge_file_name; end + def hoge_file_name=(arg); end def has_attribute?(arg); end @@ -109,12 +111,21 @@ RSpec.describe Remotable do end context 'foo[attribute_name] == url' do - it 'makes no request' do + it 'makes no request if file is saved' do allow(foo).to receive(:[]).with(attribute_name).and_return(url) + allow(foo).to receive(:hoge_file_name).and_return('foo.jpg') foo.hoge_remote_url = url expect(request).not_to have_been_requested end + + it 'makes request if file is not saved' do + allow(foo).to receive(:[]).with(attribute_name).and_return(url) + allow(foo).to receive(:hoge_file_name).and_return(nil) + + foo.hoge_remote_url = url + expect(request).to have_been_requested + end end context "scheme is https, parsed_url.host isn't empty, and foo[attribute_name] != url" do From b5f7e12817356b9b1795ab0187fe08d07f13a485 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 9 Oct 2019 07:11:23 +0200 Subject: [PATCH 006/202] Remove auto-silence behaviour from spam check (#12117) Fix #12113 --- app/lib/spam_check.rb | 7 +------ app/models/account.rb | 2 +- app/models/admin/account_action.rb | 12 ++++++++++++ config/locales/en.yml | 2 +- spec/lib/spam_check_spec.rb | 4 ---- 5 files changed, 15 insertions(+), 12 deletions(-) diff --git a/app/lib/spam_check.rb b/app/lib/spam_check.rb index 441697364..235e44230 100644 --- a/app/lib/spam_check.rb +++ b/app/lib/spam_check.rb @@ -44,7 +44,6 @@ class SpamCheck end def flag! - auto_silence_account! auto_report_status! end @@ -134,17 +133,13 @@ class SpamCheck text.gsub(/\s+/, ' ').strip end - def auto_silence_account! - @account.silence! - end - def auto_report_status! status_ids = Status.where(visibility: %i(public unlisted)).where(id: matching_status_ids).pluck(:id) + [@status.id] if @status.distributable? ReportService.new.call(Account.representative, @account, status_ids: status_ids, comment: I18n.t('spam_check.spam_detected_and_silenced')) end def already_flagged? - @account.silenced? + @account.silenced? || @account.targeted_reports.unresolved.where(account_id: -99).exists? end def trusted? diff --git a/app/models/account.rb b/app/models/account.rb index 2f43f337f..05936def3 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -198,7 +198,7 @@ class Account < ApplicationRecord end def unsilence! - update!(silenced_at: nil, trust_level: trust_level == TRUST_LEVELS[:untrusted] ? TRUST_LEVELS[:trusted] : trust_level) + update!(silenced_at: nil) end def suspended? diff --git a/app/models/admin/account_action.rb b/app/models/admin/account_action.rb index b30a82369..e9da003a3 100644 --- a/app/models/admin/account_action.rb +++ b/app/models/admin/account_action.rb @@ -62,6 +62,8 @@ class Admin::AccountAction def process_action! case type + when 'none' + handle_resolve! when 'disable' handle_disable! when 'silence' @@ -103,6 +105,16 @@ class Admin::AccountAction end end + def handle_resolve! + if with_report? && report.account_id == -99 && target_account.trust_level == Account::TRUST_LEVELS[:untrusted] + # This is an automated report and it is being dismissed, so it's + # a false positive, in which case update the account's trust level + # to prevent further spam checks + + target_account.update(trust_level: Account::TRUST_LEVELS[:trusted]) + end + end + def handle_disable! authorize(target_account.user, :disable?) log_action(:disable, target_account.user) diff --git a/config/locales/en.yml b/config/locales/en.yml index 0e8ee6a76..1ffc99eb3 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -497,7 +497,7 @@ en: title: Custom terms of service site_title: Server name spam_check_enabled: - desc_html: Mastodon can auto-silence and auto-report accounts that send repeated unsolicited messages. There may be false positives. + desc_html: Mastodon can auto-report accounts that send repeated unsolicited messages. There may be false positives. title: Anti-spam automation thumbnail: desc_html: Used for previews via OpenGraph and API. 1200x630px recommended diff --git a/spec/lib/spam_check_spec.rb b/spec/lib/spam_check_spec.rb index 4cae46111..d4d66a499 100644 --- a/spec/lib/spam_check_spec.rb +++ b/spec/lib/spam_check_spec.rb @@ -181,10 +181,6 @@ RSpec.describe SpamCheck do described_class.new(status2).flag! end - it 'silences the account' do - expect(sender.silenced?).to be true - end - it 'creates a report about the account' do expect(sender.targeted_reports.unresolved.count).to eq 1 end From e6d111f38b95b08a2880390b1519655b54ac2fd0 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 9 Oct 2019 07:36:57 +0200 Subject: [PATCH 007/202] Bump version to 3.0.1 (#12116) --- CHANGELOG.md | 30 ++++++++++++++++++++++++++++++ lib/mastodon/version.rb | 2 +- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b0d23a22..47a466294 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,36 @@ Changelog All notable changes to this project will be documented in this file. +## [3.0.1] - 2019-10-09 +### Added + +- Add `tootctl media usage` command ([Gargron](https://github.com/tootsuite/mastodon/pull/12115)) +- Add admin setting to auto-approve trending hashtags ([Gargron](https://github.com/tootsuite/mastodon/pull/12122)) + +### Changed + +- Change `tootctl media refresh` to skip already downloaded attachments ([Gargron](https://github.com/tootsuite/mastodon/pull/12118)) + +### Removed + +- Remove auto-silence behaviour from spam check ([Gargron](https://github.com/tootsuite/mastodon/pull/12117)) +- Remove HTML `lang` attribute from individual statuses in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/12124)) +- Remove fallback to long description on sidebar and meta description ([Gargron](https://github.com/tootsuite/mastodon/pull/12119)) + +### Fixed + +- Fix attachment not being re-downloaded even if file is not stored ([Gargron](https://github.com/tootsuite/mastodon/pull/12125)) +- Fix old migration trying to use new column due to default status scope ([Gargron](https://github.com/tootsuite/mastodon/pull/12095)) +- Fix column back button missing for not found accounts ([trwnh](https://github.com/tootsuite/mastodon/pull/12094)) +- Fix issues with tootctl's parallelization and progress reporting ([Gargron](https://github.com/tootsuite/mastodon/pull/12093), [Gargron](https://github.com/tootsuite/mastodon/pull/12097)) +- Fix existing user records with now-renamed `pt` locale ([Gargron](https://github.com/tootsuite/mastodon/pull/12092)) +- Fix hashtag timeline REST API accepting too many hashtags ([Gargron](https://github.com/tootsuite/mastodon/pull/12091)) +- Fix `GET /api/v1/instance` REST APIs being unavailable in secure mode ([Gargron](https://github.com/tootsuite/mastodon/pull/12089)) +- Fix performance of home feed regeneration and merging ([Gargron](https://github.com/tootsuite/mastodon/pull/12084)) +- Fix ffmpeg performance issues due to stdout buffer overflow ([hugogameiro](https://github.com/tootsuite/mastodon/pull/12088)) +- Fix S3 adapter retrying failing uploads with exponential backoff ([Gargron](https://github.com/tootsuite/mastodon/pull/12085)) +- Fix `tootctl accounts cull` advertising unused option flag ([Kjwon15](https://github.com/tootsuite/mastodon/pull/12074)) + ## [3.0.0] - 2019-10-03 ### Added diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index a0f4678ec..f3ead6d8d 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -13,7 +13,7 @@ module Mastodon end def patch - 0 + 1 end def flags From 30a28a2eb7bdd39de877ae8171555307986b3e06 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 10 Oct 2019 02:21:52 +0200 Subject: [PATCH 008/202] Fix not showing if emoji has a local counterpart in admin UI (#12135) Fix #12132 --- app/views/admin/custom_emojis/_custom_emoji.html.haml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/views/admin/custom_emojis/_custom_emoji.html.haml b/app/views/admin/custom_emojis/_custom_emoji.html.haml index 2103b0fa7..526c844e9 100644 --- a/app/views/admin/custom_emojis/_custom_emoji.html.haml +++ b/app/views/admin/custom_emojis/_custom_emoji.html.haml @@ -17,6 +17,10 @@ - else = custom_emoji.domain + - if custom_emoji.local_counterpart.present? + • + = t('admin.accounts.location.local') + %br/ - if custom_emoji.disabled? From 915f3712ae7ae44c0cbe50c9694c25e3ee87a540 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 10 Oct 2019 02:22:04 +0200 Subject: [PATCH 009/202] Fix admin setting to auto-approve hashtags not affecting query (#12130) Follow-up to #12122 --- app/models/tag.rb | 1 + app/models/trending_tags.rb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/models/tag.rb b/app/models/tag.rb index 59445a83b..d3a7e1e6d 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -37,6 +37,7 @@ class Tag < ApplicationRecord scope :pending_review, -> { unreviewed.where.not(requested_review_at: nil) } scope :usable, -> { where(usable: [true, nil]) } scope :listable, -> { where(listable: [true, nil]) } + scope :trendable, -> { Setting.trendable_by_default ? where(trendable: [true, nil]) : where(trendable: true) } scope :discoverable, -> { listable.joins(:account_tag_stat).where(AccountTagStat.arel_table[:accounts_count].gt(0)).order(Arel.sql('account_tag_stats.accounts_count desc')) } scope :most_used, ->(account) { joins(:statuses).where(statuses: { account: account }).group(:id).order(Arel.sql('count(*) desc')) } scope :matches_name, ->(value) { where(arel_table[:name].matches("#{value}%")) } diff --git a/app/models/trending_tags.rb b/app/models/trending_tags.rb index 8cdade42d..c69f6d3c3 100644 --- a/app/models/trending_tags.rb +++ b/app/models/trending_tags.rb @@ -90,7 +90,7 @@ class TrendingTags tag_ids = redis.zrevrange(KEY, 0, LIMIT - 1).map(&:to_i) tags = Tag.where(id: tag_ids) - tags = tags.where(trendable: true) if filtered + tags = tags.trendable if filtered tags = tags.each_with_object({}) { |tag, h| h[tag.id] = tag } tag_ids.map { |tag_id| tags[tag_id] }.compact.take(limit) From 6ebd74f4fa640b9616ebb2730d97722799c6ed56 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 10 Oct 2019 05:21:38 +0200 Subject: [PATCH 010/202] Fix media editing modal changing dimensions when image loads (#12131) --- .../components/extended_video_player.js | 63 ---------------- app/javascript/mastodon/components/gifv.js | 75 +++++++++++++++++++ .../ui/components/focal_point_modal.js | 36 ++++++++- .../features/ui/components/media_modal.js | 6 +- .../styles/mastodon/components.scss | 3 +- 5 files changed, 113 insertions(+), 70 deletions(-) delete mode 100644 app/javascript/mastodon/components/extended_video_player.js create mode 100644 app/javascript/mastodon/components/gifv.js diff --git a/app/javascript/mastodon/components/extended_video_player.js b/app/javascript/mastodon/components/extended_video_player.js deleted file mode 100644 index 009c0d559..000000000 --- a/app/javascript/mastodon/components/extended_video_player.js +++ /dev/null @@ -1,63 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; - -export default class ExtendedVideoPlayer extends React.PureComponent { - - static propTypes = { - src: PropTypes.string.isRequired, - alt: PropTypes.string, - width: PropTypes.number, - height: PropTypes.number, - time: PropTypes.number, - controls: PropTypes.bool.isRequired, - muted: PropTypes.bool.isRequired, - onClick: PropTypes.func, - }; - - handleLoadedData = () => { - if (this.props.time) { - this.video.currentTime = this.props.time; - } - } - - componentDidMount () { - this.video.addEventListener('loadeddata', this.handleLoadedData); - } - - componentWillUnmount () { - this.video.removeEventListener('loadeddata', this.handleLoadedData); - } - - setRef = (c) => { - this.video = c; - } - - handleClick = e => { - e.stopPropagation(); - const handler = this.props.onClick; - if (handler) handler(); - } - - render () { - const { src, muted, controls, alt } = this.props; - - return ( -
-
- ); - } - -} diff --git a/app/javascript/mastodon/components/gifv.js b/app/javascript/mastodon/components/gifv.js new file mode 100644 index 000000000..83cfae49c --- /dev/null +++ b/app/javascript/mastodon/components/gifv.js @@ -0,0 +1,75 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +export default class GIFV extends React.PureComponent { + + static propTypes = { + src: PropTypes.string.isRequired, + alt: PropTypes.string, + width: PropTypes.number, + height: PropTypes.number, + onClick: PropTypes.func, + }; + + state = { + loading: true, + }; + + handleLoadedData = () => { + this.setState({ loading: false }); + } + + componentWillReceiveProps (nextProps) { + if (nextProps.src !== this.props.src) { + this.setState({ loading: true }); + } + } + + handleClick = e => { + const { onClick } = this.props; + + if (onClick) { + e.stopPropagation(); + onClick(); + } + } + + render () { + const { src, width, height, alt } = this.props; + const { loading } = this.state; + + return ( +
+ {loading && ( + + )} + +
+ ); + } + +} diff --git a/app/javascript/mastodon/features/ui/components/focal_point_modal.js b/app/javascript/mastodon/features/ui/components/focal_point_modal.js index 1ab79a21d..3694ab904 100644 --- a/app/javascript/mastodon/features/ui/components/focal_point_modal.js +++ b/app/javascript/mastodon/features/ui/components/focal_point_modal.js @@ -16,6 +16,7 @@ import UploadProgress from 'mastodon/features/compose/components/upload_progress import CharacterCounter from 'mastodon/features/compose/components/character_counter'; import { length } from 'stringz'; import { Tesseract as fetchTesseract } from 'mastodon/features/ui/util/async-components'; +import GIFV from 'mastodon/components/gifv'; const messages = defineMessages({ close: { id: 'lightbox.close', defaultMessage: 'Close' }, @@ -41,6 +42,36 @@ const removeExtraLineBreaks = str => str.replace(/\n\n/g, '******') const assetHost = process.env.CDN_HOST || ''; +class ImageLoader extends React.PureComponent { + + static propTypes = { + src: PropTypes.string.isRequired, + width: PropTypes.number, + height: PropTypes.number, + }; + + state = { + loading: true, + }; + + componentDidMount() { + const image = new Image(); + image.addEventListener('load', () => this.setState({ loading: false })); + image.src = this.props.src; + } + + render () { + const { loading } = this.state; + + if (loading) { + return ; + } else { + return ; + } + } + +} + export default @connect(mapStateToProps, mapDispatchToProps) @injectIntl class FocalPointModal extends ImmutablePureComponent { @@ -60,6 +91,7 @@ class FocalPointModal extends ImmutablePureComponent { description: '', dirty: false, progress: 0, + loading: true, }; componentWillMount () { @@ -242,8 +274,8 @@ class FocalPointModal extends ImmutablePureComponent {
{focals && (
- {media.get('type') === 'image' && } - {media.get('type') === 'gifv' &&