about summary refs log tree commit diff
path: root/app
diff options
context:
space:
mode:
authorThibaut Girka <thib@sitedethib.com>2019-12-19 13:52:54 +0100
committerThibaut Girka <thib@sitedethib.com>2019-12-19 13:52:54 +0100
commitab5ff638f2968e3f434e504c7caab370bf71c9c0 (patch)
tree172f2c2fc8192688b960dd2bd42ea126ad9245fb /app
parent66ab1f83b0d342341181e0c88da83c966391fc0c (diff)
parent902c6bed5aab12c2e97af3452f122247777226af (diff)
Merge branch 'master' into glitch-soc/master
Conflicts:
- `config/locales/en.yml`
  No real conflict, upstream added a translatable string “too close” to
  one specific to glitch-soc
- `lib/mastodon/statuses_cli.rb`
  Fixes made upstream, while changed in glitch-soc to keep bookmarked statuses
- `package.json`
  No real conflict, additional dependency in glitch-soc
Diffstat (limited to 'app')
-rw-r--r--app/javascript/core/admin.js22
-rw-r--r--app/javascript/mastodon/extra_polyfills.js2
-rw-r--r--app/javascript/mastodon/features/compose/components/poll_form.js4
-rw-r--r--app/javascript/styles/mastodon/admin.scss31
-rw-r--r--app/javascript/styles/mastodon/components.scss1
-rw-r--r--app/lib/activitypub/activity.rb2
-rw-r--r--app/lib/activitypub/activity/create.rb4
-rw-r--r--app/models/backup.rb2
-rw-r--r--app/models/form/admin_settings.rb2
-rw-r--r--app/models/form/custom_emoji_batch.rb2
-rw-r--r--app/models/tag.rb2
-rw-r--r--app/services/backup_service.rb4
-rw-r--r--app/services/bootstrap_timeline_service.rb2
-rw-r--r--app/services/fetch_link_card_service.rb2
-rw-r--r--app/services/fetch_oembed_service.rb2
-rw-r--r--app/services/fetch_remote_account_service.rb17
-rw-r--r--app/services/fetch_remote_status_service.rb9
-rw-r--r--app/services/fetch_resource_service.rb2
-rw-r--r--app/services/resolve_url_service.rb10
-rw-r--r--app/views/admin/reports/index.html.haml2
-rw-r--r--app/views/admin/reports/show.html.haml32
-rw-r--r--app/views/admin/settings/edit.html.haml7
-rw-r--r--app/views/layouts/admin.html.haml7
-rw-r--r--app/views/settings/exports/show.html.haml6
24 files changed, 103 insertions, 73 deletions
diff --git a/app/javascript/core/admin.js b/app/javascript/core/admin.js
index ffdabe674..e4d683dd0 100644
--- a/app/javascript/core/admin.js
+++ b/app/javascript/core/admin.js
@@ -47,7 +47,25 @@ const onDomainBlockSeverityChange = (target) => {
 
 delegate(document, '#domain_block_severity', 'change', ({ target }) => onDomainBlockSeverityChange(target));
 
+const onEnableBootstrapTimelineAccountsChange = (target) => {
+  const bootstrapTimelineAccountsField = document.querySelector('#form_admin_settings_bootstrap_timeline_accounts');
+
+  if (bootstrapTimelineAccountsField) {
+    bootstrapTimelineAccountsField.disabled = !target.checked;
+    if (target.checked) {
+      bootstrapTimelineAccountsField.parentElement.classList.remove('disabled');
+    } else {
+      bootstrapTimelineAccountsField.parentElement.classList.add('disabled');
+    }
+  }
+};
+
+delegate(document, '#form_admin_settings_enable_bootstrap_timeline_accounts', 'change', ({ target }) => onEnableBootstrapTimelineAccountsChange(target));
+
 ready(() => {
-  const input = document.getElementById('domain_block_severity');
-  if (input) onDomainBlockSeverityChange(input);
+  const domainBlockSeverityInput = document.getElementById('domain_block_severity');
+  if (domainBlockSeverityInput) onDomainBlockSeverityChange(domainBlockSeverityInput);
+
+  const enableBootstrapTimelineAccounts = document.getElementById('form_admin_settings_enable_bootstrap_timeline_accounts');
+  if (enableBootstrapTimelineAccounts) onEnableBootstrapTimelineAccountsChange(enableBootstrapTimelineAccounts);
 });
diff --git a/app/javascript/mastodon/extra_polyfills.js b/app/javascript/mastodon/extra_polyfills.js
index 3acc55abd..13c4f6da9 100644
--- a/app/javascript/mastodon/extra_polyfills.js
+++ b/app/javascript/mastodon/extra_polyfills.js
@@ -1,5 +1,5 @@
 import 'intersection-observer';
 import 'requestidlecallback';
-import objectFitImages  from 'object-fit-images';
+import objectFitImages from 'object-fit-images';
 
 objectFitImages();
diff --git a/app/javascript/mastodon/features/compose/components/poll_form.js b/app/javascript/mastodon/features/compose/components/poll_form.js
index ba245f4d3..cac3776bb 100644
--- a/app/javascript/mastodon/features/compose/components/poll_form.js
+++ b/app/javascript/mastodon/features/compose/components/poll_form.js
@@ -82,8 +82,8 @@ class Option extends React.PureComponent {
             onKeyPress={this.handleCheckboxKeypress}
             role='button'
             tabIndex='0'
-            title={intl.formatMessage(isPollMultiple ? messages.switchToMultiple : messages.switchToSingle)}
-            aria-label={intl.formatMessage(isPollMultiple ? messages.switchToMultiple : messages.switchToSingle)}
+            title={intl.formatMessage(isPollMultiple ? messages.switchToSingle : messages.switchToMultiple)}
+            aria-label={intl.formatMessage(isPollMultiple ? messages.switchToSingle : messages.switchToMultiple)}
           />
 
           <AutosuggestInput
diff --git a/app/javascript/styles/mastodon/admin.scss b/app/javascript/styles/mastodon/admin.scss
index de95d82bf..cba552433 100644
--- a/app/javascript/styles/mastodon/admin.scss
+++ b/app/javascript/styles/mastodon/admin.scss
@@ -181,18 +181,39 @@ $content-width: 840px;
       padding-top: 30px;
     }
 
-    h2 {
-      color: $secondary-text-color;
-      font-size: 24px;
-      line-height: 28px;
-      font-weight: 400;
+    &-heading {
+      display: flex;
+
       padding-bottom: 40px;
       border-bottom: 1px solid lighten($ui-base-color, 8%);
       margin-bottom: 40px;
 
+      flex-wrap: wrap;
+      align-items: center;
+
+      justify-content: space-between;
+
+      &-actions {
+        display: inline-flex;
+
+        & > * {
+          margin-left: 5px;
+        }
+      }
+
       @media screen and (max-width: $no-columns-breakpoint) {
         border-bottom: 0;
         padding-bottom: 0;
+      }
+    }
+
+    h2 {
+      color: $secondary-text-color;
+      font-size: 24px;
+      line-height: 28px;
+      font-weight: 400;
+
+      @media screen and (max-width: $no-columns-breakpoint) {
         font-weight: 700;
       }
     }
diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss
index 0ec25e3f8..01a633c5f 100644
--- a/app/javascript/styles/mastodon/components.scss
+++ b/app/javascript/styles/mastodon/components.scss
@@ -2512,7 +2512,6 @@ a.account__display-name {
   overflow-x: hidden;
   flex: 1 1 auto;
   -webkit-overflow-scrolling: touch;
-  will-change: transform; // improves perf in mobile Chrome
 
   &.optionally-scrollable {
     overflow-y: auto;
diff --git a/app/lib/activitypub/activity.rb b/app/lib/activitypub/activity.rb
index 0ca6b92a4..49b1dc9cd 100644
--- a/app/lib/activitypub/activity.rb
+++ b/app/lib/activitypub/activity.rb
@@ -5,7 +5,7 @@ class ActivityPub::Activity
   include Redisable
 
   SUPPORTED_TYPES = %w(Note Question).freeze
-  CONVERTED_TYPES = %w(Image Audio Video Article Page).freeze
+  CONVERTED_TYPES = %w(Image Audio Video Article Page Event).freeze
 
   def initialize(json, account, **options)
     @json    = json
diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb
index 8a12a2b08..c55cfe08e 100644
--- a/app/lib/activitypub/activity/create.rb
+++ b/app/lib/activitypub/activity/create.rb
@@ -157,7 +157,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
     return if tag['name'].blank?
 
     Tag.find_or_create_by_names(tag['name']) do |hashtag|
-      @tags << hashtag unless @tags.include?(hashtag)
+      @tags << hashtag unless @tags.include?(hashtag) || !hashtag.valid?
     end
   rescue ActiveRecord::RecordInvalid
     nil
@@ -167,7 +167,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
     return if tag['href'].blank?
 
     account = account_from_uri(tag['href'])
-    account = ::FetchRemoteAccountService.new.call(tag['href']) if account.nil?
+    account = ActivityPub::FetchRemoteAccountService.new.call(tag['href']) if account.nil?
 
     return if account.nil?
 
diff --git a/app/models/backup.rb b/app/models/backup.rb
index c2651313b..8eeb1748a 100644
--- a/app/models/backup.rb
+++ b/app/models/backup.rb
@@ -7,7 +7,7 @@
 #  user_id           :bigint(8)
 #  dump_file_name    :string
 #  dump_content_type :string
-#  dump_file_size    :integer
+#  dump_file_size    :bigint
 #  dump_updated_at   :datetime
 #  processed         :boolean          default(FALSE), not null
 #  created_at        :datetime         not null
diff --git a/app/models/form/admin_settings.rb b/app/models/form/admin_settings.rb
index 3398af169..84a656864 100644
--- a/app/models/form/admin_settings.rb
+++ b/app/models/form/admin_settings.rb
@@ -16,6 +16,7 @@ class Form::AdminSettings
     open_deletion
     timeline_preview
     show_staff_badge
+    enable_bootstrap_timeline_accounts
     bootstrap_timeline_accounts
     flavour
     skin
@@ -46,6 +47,7 @@ class Form::AdminSettings
     open_deletion
     timeline_preview
     show_staff_badge
+    enable_bootstrap_timeline_accounts
     activity_api_enabled
     peers_api_enabled
     show_known_fediverse_at_about_page
diff --git a/app/models/form/custom_emoji_batch.rb b/app/models/form/custom_emoji_batch.rb
index 076e8c9e3..6b7ea5355 100644
--- a/app/models/form/custom_emoji_batch.rb
+++ b/app/models/form/custom_emoji_batch.rb
@@ -40,7 +40,7 @@ class Form::CustomEmojiBatch
       if category_id.present?
         CustomEmojiCategory.find(category_id)
       elsif category_name.present?
-        CustomEmojiCategory.create!(name: category_name)
+        CustomEmojiCategory.find_or_create_by!(name: category_name)
       end
     end
 
diff --git a/app/models/tag.rb b/app/models/tag.rb
index d3a7e1e6d..bce76fc16 100644
--- a/app/models/tag.rb
+++ b/app/models/tag.rb
@@ -117,7 +117,7 @@ class Tag < ApplicationRecord
   class << self
     def find_or_create_by_names(name_or_names)
       Array(name_or_names).map(&method(:normalize)).uniq { |str| str.mb_chars.downcase.to_s }.map do |normalized_name|
-        tag = matching_name(normalized_name).first || create!(name: normalized_name)
+        tag = matching_name(normalized_name).first || create(name: normalized_name)
 
         yield tag if block_given?
 
diff --git a/app/services/backup_service.rb b/app/services/backup_service.rb
index cc9fb1f4e..0b57b6d0c 100644
--- a/app/services/backup_service.rb
+++ b/app/services/backup_service.rb
@@ -3,6 +3,8 @@
 require 'rubygems/package'
 
 class BackupService < BaseService
+  include Payloadable
+
   attr_reader :account, :backup, :collection
 
   def call(backup)
@@ -20,7 +22,7 @@ class BackupService < BaseService
 
     account.statuses.with_includes.reorder(nil).find_in_batches do |statuses|
       statuses.each do |status|
-        item = serialize(status, ActivityPub::ActivitySerializer)
+        item = serialize_payload(status, ActivityPub::ActivitySerializer, signer: @account)
         item.delete(:'@context')
 
         unless item[:type] == 'Announce' || item[:object][:attachment].blank?
diff --git a/app/services/bootstrap_timeline_service.rb b/app/services/bootstrap_timeline_service.rb
index c489601c1..8412aa7e7 100644
--- a/app/services/bootstrap_timeline_service.rb
+++ b/app/services/bootstrap_timeline_service.rb
@@ -5,7 +5,7 @@ class BootstrapTimelineService < BaseService
     @source_account = source_account
 
     autofollow_inviter!
-    autofollow_bootstrap_timeline_accounts!
+    autofollow_bootstrap_timeline_accounts! if Setting.enable_bootstrap_timeline_accounts
   end
 
   private
diff --git a/app/services/fetch_link_card_service.rb b/app/services/fetch_link_card_service.rb
index 5d4a7c303..91141c1f5 100644
--- a/app/services/fetch_link_card_service.rb
+++ b/app/services/fetch_link_card_service.rb
@@ -45,7 +45,7 @@ class FetchLinkCardService < BaseService
   def html
     return @html if defined?(@html)
 
-    Request.new(:get, @url).perform do |res|
+    Request.new(:get, @url).add_headers('Accept' => 'text/html').perform do |res|
       if res.code == 200 && res.mime_type == 'text/html'
         @html = res.body_with_limit
         @html_charset = res.charset
diff --git a/app/services/fetch_oembed_service.rb b/app/services/fetch_oembed_service.rb
index 76d971bc5..67e33875c 100644
--- a/app/services/fetch_oembed_service.rb
+++ b/app/services/fetch_oembed_service.rb
@@ -93,7 +93,7 @@ class FetchOEmbedService
   def html
     return @html if defined?(@html)
 
-    @html = @options[:html] || Request.new(:get, @url).perform do |res|
+    @html = @options[:html] || Request.new(:get, @url).add_headers('Accept' => 'text/html').perform do |res|
       res.code != 200 || res.mime_type != 'text/html' ? nil : res.body_with_limit
     end
   end
diff --git a/app/services/fetch_remote_account_service.rb b/app/services/fetch_remote_account_service.rb
deleted file mode 100644
index 3cd06e30f..000000000
--- a/app/services/fetch_remote_account_service.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-# frozen_string_literal: true
-
-class FetchRemoteAccountService < BaseService
-  def call(url, prefetched_body = nil, protocol = :ostatus)
-    if prefetched_body.nil?
-      resource_url, resource_options, protocol = FetchResourceService.new.call(url)
-    else
-      resource_url     = url
-      resource_options = { prefetched_body: prefetched_body }
-    end
-
-    case protocol
-    when :activitypub
-      ActivityPub::FetchRemoteAccountService.new.call(resource_url, **resource_options)
-    end
-  end
-end
diff --git a/app/services/fetch_remote_status_service.rb b/app/services/fetch_remote_status_service.rb
index 208dc7809..eafde4d4a 100644
--- a/app/services/fetch_remote_status_service.rb
+++ b/app/services/fetch_remote_status_service.rb
@@ -1,17 +1,14 @@
 # frozen_string_literal: true
 
 class FetchRemoteStatusService < BaseService
-  def call(url, prefetched_body = nil, protocol = :ostatus)
+  def call(url, prefetched_body = nil)
     if prefetched_body.nil?
-      resource_url, resource_options, protocol = FetchResourceService.new.call(url)
+      resource_url, resource_options = FetchResourceService.new.call(url)
     else
       resource_url     = url
       resource_options = { prefetched_body: prefetched_body }
     end
 
-    case protocol
-    when :activitypub
-      ActivityPub::FetchRemoteStatusService.new.call(resource_url, **resource_options)
-    end
+    ActivityPub::FetchRemoteStatusService.new.call(resource_url, **resource_options) unless resource_url.nil?
   end
 end
diff --git a/app/services/fetch_resource_service.rb b/app/services/fetch_resource_service.rb
index 3676d899d..34382d279 100644
--- a/app/services/fetch_resource_service.rb
+++ b/app/services/fetch_resource_service.rb
@@ -33,7 +33,7 @@ class FetchResourceService < BaseService
       body = response.body_with_limit
       json = body_to_json(body)
 
-      [json['id'], { prefetched_body: body, id: true }, :activitypub] if supported_context?(json) && (equals_or_includes_any?(json['type'], ActivityPub::FetchRemoteAccountService::SUPPORTED_TYPES) || expected_type?(json))
+      [json['id'], { prefetched_body: body, id: true }] if supported_context?(json) && (equals_or_includes_any?(json['type'], ActivityPub::FetchRemoteAccountService::SUPPORTED_TYPES) || expected_type?(json))
     elsif !terminal
       link_header = response['Link'] && parse_link_header(response)
 
diff --git a/app/services/resolve_url_service.rb b/app/services/resolve_url_service.rb
index 4e971a4b8..79b1bad0c 100644
--- a/app/services/resolve_url_service.rb
+++ b/app/services/resolve_url_service.rb
@@ -19,9 +19,9 @@ class ResolveURLService < BaseService
 
   def process_url
     if equals_or_includes_any?(type, ActivityPub::FetchRemoteAccountService::SUPPORTED_TYPES)
-      FetchRemoteAccountService.new.call(resource_url, body, protocol)
+      ActivityPub::FetchRemoteAccountService.new.call(resource_url, prefetched_body: body)
     elsif equals_or_includes_any?(type, ActivityPub::Activity::Create::SUPPORTED_TYPES + ActivityPub::Activity::Create::CONVERTED_TYPES)
-      status = FetchRemoteStatusService.new.call(resource_url, body, protocol)
+      status = FetchRemoteStatusService.new.call(resource_url, body)
       authorize_with @on_behalf_of, status, :show? unless status.nil?
       status
     elsif fetched_resource.nil? && @on_behalf_of.present?
@@ -45,12 +45,8 @@ class ResolveURLService < BaseService
     fetched_resource.second[:prefetched_body]
   end
 
-  def protocol
-    fetched_resource.third
-  end
-
   def type
-    return json_data['type'] if protocol == :activitypub
+    json_data['type']
   end
 
   def json_data
diff --git a/app/views/admin/reports/index.html.haml b/app/views/admin/reports/index.html.haml
index b09472270..30c7549b0 100644
--- a/app/views/admin/reports/index.html.haml
+++ b/app/views/admin/reports/index.html.haml
@@ -28,7 +28,7 @@
     .report-card__profile
       = account_link_to target_account, '', size: 36, path: admin_account_path(target_account.id)
       .report-card__profile__stats
-        = link_to pluralize(target_account.targeted_moderation_notes.count, t('admin.reports.account.note')), admin_account_path(target_account.id)
+        = link_to t('admin.reports.account.notes', count: target_account.targeted_moderation_notes.count), admin_account_path(target_account.id)
         %br/
         - if target_account.suspended?
           %span.red= t('admin.accounts.suspended')
diff --git a/app/views/admin/reports/show.html.haml b/app/views/admin/reports/show.html.haml
index 0b84e1788..4321bb199 100644
--- a/app/views/admin/reports/show.html.haml
+++ b/app/views/admin/reports/show.html.haml
@@ -1,37 +1,28 @@
 - content_for :page_title do
   = t('admin.reports.report', id: @report.id)
 
-%div{ style: 'overflow: hidden; margin-bottom: 20px' }
+- content_for :page_heading_actions do
   - if @report.unresolved?
-    %div{ style: 'float: right' }
-      - if @report.target_account.local?
-        = link_to t('admin.accounts.warn'), new_admin_account_action_path(@report.target_account_id, type: 'none', report_id: @report.id), class: 'button'
-        = link_to t('admin.accounts.disable'), new_admin_account_action_path(@report.target_account_id, type: 'disable', report_id: @report.id), class: 'button button--destructive'
-      = link_to t('admin.accounts.silence'), new_admin_account_action_path(@report.target_account_id, type: 'silence', report_id: @report.id), class: 'button button--destructive'
-      = link_to t('admin.accounts.perform_full_suspension'), new_admin_account_action_path(@report.target_account_id, type: 'suspend', report_id: @report.id), class: 'button button--destructive'
-    %div{ style: 'float: left' }
-      = link_to t('admin.reports.mark_as_resolved'), resolve_admin_report_path(@report), method: :post, class: 'button'
+    = link_to t('admin.reports.mark_as_resolved'), resolve_admin_report_path(@report), method: :post, class: 'button'
   - else
     = link_to t('admin.reports.mark_as_unresolved'), reopen_admin_report_path(@report), method: :post, class: 'button'
 
-%hr.spacer
-
 .table-wrapper
   %table.table.inline-table
     %tbody
       %tr
         %th= t('admin.reports.reported_account')
         %td= admin_account_link_to @report.target_account
-        %td= table_link_to 'flag', pluralize(@report.target_account.targeted_reports.count, t('admin.reports.account.report')), admin_reports_path(target_account_id: @report.target_account.id)
-        %td= table_link_to 'file', pluralize(@report.target_account.targeted_moderation_notes.count, t('admin.reports.account.note')), admin_reports_path(target_account_id: @report.target_account.id)
+        %td= table_link_to 'flag', t('admin.reports.account.reports', count: @report.target_account.targeted_reports.count), admin_reports_path(target_account_id: @report.target_account.id)
+        %td= table_link_to 'file', t('admin.reports.account.notes', count: @report.target_account.targeted_moderation_notes.count), admin_reports_path(target_account_id: @report.target_account.id)
       %tr
         %th= t('admin.reports.reported_by')
         - if @report.account.instance_actor?
           %td{ colspan: 3 }= site_hostname
         - elsif @report.account.local?
           %td= admin_account_link_to @report.account
-          %td= table_link_to 'flag', pluralize(@report.account.targeted_reports.count, t('admin.reports.account.report')), admin_reports_path(target_account_id: @report.account.id)
-          %td= table_link_to 'file', pluralize(@report.account.targeted_moderation_notes.count, t('admin.reports.account.note')), admin_reports_path(target_account_id: @report.account.id)
+          %td= table_link_to 'flag', t('admin.reports.account.reports', count: @report.account.targeted_reports.count), admin_reports_path(target_account_id: @report.account.id)
+          %td= table_link_to 'file', t('admin.reports.account.notes', count: @report.account.targeted_moderation_notes.count), admin_reports_path(target_account_id: @report.account.id)
         - else
           %td{ colspan: 3 }= @report.account.domain
       %tr
@@ -74,6 +65,17 @@
 
 %hr.spacer
 
+%div{ style: 'overflow: hidden; margin-bottom: 20px; clear: both' }
+  - if @report.unresolved?
+    %div{ style: 'float: right' }
+      - if @report.target_account.local?
+        = link_to t('admin.accounts.warn'), new_admin_account_action_path(@report.target_account_id, type: 'none', report_id: @report.id), class: 'button'
+        = link_to t('admin.accounts.disable'), new_admin_account_action_path(@report.target_account_id, type: 'disable', report_id: @report.id), class: 'button button--destructive'
+      = link_to t('admin.accounts.silence'), new_admin_account_action_path(@report.target_account_id, type: 'silence', report_id: @report.id), class: 'button button--destructive'
+      = link_to t('admin.accounts.perform_full_suspension'), new_admin_account_action_path(@report.target_account_id, type: 'suspend', report_id: @report.id), class: 'button button--destructive'
+
+%hr.spacer
+
 .speech-bubble
   .speech-bubble__bubble= simple_format(@report.comment.presence || t('admin.reports.comment.none'))
   .speech-bubble__owner
diff --git a/app/views/admin/settings/edit.html.haml b/app/views/admin/settings/edit.html.haml
index ba66aeff8..b6815e80b 100644
--- a/app/views/admin/settings/edit.html.haml
+++ b/app/views/admin/settings/edit.html.haml
@@ -1,3 +1,6 @@
+- content_for :header_tags do
+  = javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous'
+
 - content_for :page_title do
   = t('admin.settings.title')
 
@@ -38,7 +41,9 @@
   %hr.spacer/
 
   .fields-group
-    = f.input :bootstrap_timeline_accounts, wrapper: :with_block_label, label: t('admin.settings.bootstrap_timeline_accounts.title'), hint: t('admin.settings.bootstrap_timeline_accounts.desc_html')
+    = f.input :enable_bootstrap_timeline_accounts, as: :boolean, wrapper: :with_label, label: t('admin.settings.enable_bootstrap_timeline_accounts.title')
+  .fields-group
+    = f.input :bootstrap_timeline_accounts, wrapper: :with_block_label, label: t('admin.settings.bootstrap_timeline_accounts.title'), hint: t('admin.settings.bootstrap_timeline_accounts.desc_html'), disabled: !Setting.enable_bootstrap_timeline_accounts
 
   %hr.spacer/
 
diff --git a/app/views/layouts/admin.html.haml b/app/views/layouts/admin.html.haml
index fc690409c..9fceb54eb 100644
--- a/app/views/layouts/admin.html.haml
+++ b/app/views/layouts/admin.html.haml
@@ -18,7 +18,12 @@
 
     .content-wrapper
       .content
-        %h2= yield :page_title
+        .content-heading
+          %h2= yield :page_title
+
+          - if :page_heading_actions
+            .content-heading-actions
+              = yield :page_heading_actions
 
         = render 'application/flashes'
 
diff --git a/app/views/settings/exports/show.html.haml b/app/views/settings/exports/show.html.haml
index 76ff76bd9..0bb80e937 100644
--- a/app/views/settings/exports/show.html.haml
+++ b/app/views/settings/exports/show.html.haml
@@ -9,11 +9,11 @@
         %td= number_to_human_size @export.total_storage
         %td
       %tr
-        %th= t('accounts.posts', count: @export.total_statuses)
+        %th= t('accounts.posts_tab_heading')
         %td= number_with_delimiter @export.total_statuses
         %td
       %tr
-        %th= t('exports.follows')
+        %th= t('admin.accounts.follows')
         %td= number_with_delimiter @export.total_follows
         %td= table_link_to 'download', t('exports.csv'), settings_exports_follows_path(format: :csv)
       %tr
@@ -21,7 +21,7 @@
         %td= number_with_delimiter @export.total_lists
         %td= table_link_to 'download', t('exports.csv'), settings_exports_lists_path(format: :csv)
       %tr
-        %th= t('accounts.followers', count: @export.total_followers)
+        %th= t('admin.accounts.followers')
         %td= number_with_delimiter @export.total_followers
         %td
       %tr