]> cat aescling's git repositories - mastodon.git/commitdiff
Merge branch 'master' into glitch-soc/merge-upstream
authorThibaut Girka <thib@sitedethib.com>
Fri, 19 Jul 2019 16:26:49 +0000 (18:26 +0200)
committerThibaut Girka <thib@sitedethib.com>
Fri, 19 Jul 2019 16:26:49 +0000 (18:26 +0200)
Conflicts:
- Gemfile.lock
- app/controllers/accounts_controller.rb
- app/controllers/admin/dashboard_controller.rb
- app/controllers/follower_accounts_controller.rb
- app/controllers/following_accounts_controller.rb
- app/controllers/remote_follow_controller.rb
- app/controllers/stream_entries_controller.rb
- app/controllers/tags_controller.rb
- app/javascript/packs/public.js
- app/lib/sanitize_config.rb
- app/models/account.rb
- app/models/form/admin_settings.rb
- app/models/media_attachment.rb
- app/models/stream_entry.rb
- app/models/user.rb
- app/serializers/initial_state_serializer.rb
- app/services/batched_remove_status_service.rb
- app/services/post_status_service.rb
- app/services/process_mentions_service.rb
- app/services/reblog_service.rb
- app/services/remove_status_service.rb
- app/views/admin/settings/edit.html.haml
- config/locales/simple_form.pl.yml
- config/settings.yml
- docker-compose.yml

51 files changed:
1  2 
Gemfile
Gemfile.lock
app/controllers/about_controller.rb
app/controllers/accounts_controller.rb
app/controllers/admin/dashboard_controller.rb
app/controllers/application_controller.rb
app/controllers/follower_accounts_controller.rb
app/controllers/following_accounts_controller.rb
app/controllers/home_controller.rb
app/controllers/public_timelines_controller.rb
app/controllers/remote_follow_controller.rb
app/controllers/statuses_controller.rb
app/controllers/tags_controller.rb
app/helpers/statuses_helper.rb
app/javascript/core/public.js
app/javascript/mastodon/locales/defaultMessages.json
app/javascript/mastodon/locales/en.json
app/javascript/mastodon/locales/ja.json
app/javascript/mastodon/locales/pl.json
app/javascript/packs/public.js
app/javascript/styles/mastodon/components.scss
app/lib/formatter.rb
app/models/account.rb
app/models/concerns/account_associations.rb
app/models/form/admin_settings.rb
app/models/media_attachment.rb
app/models/status.rb
app/models/user.rb
app/policies/status_policy.rb
app/serializers/rest/account_serializer.rb
app/serializers/rest/status_serializer.rb
app/services/batched_remove_status_service.rb
app/services/post_status_service.rb
app/services/process_mentions_service.rb
app/services/reblog_service.rb
app/services/remove_status_service.rb
app/views/about/more.html.haml
app/views/admin/dashboard/index.html.haml
app/views/admin/settings/edit.html.haml
app/views/statuses/_simple_status.html.haml
config/locales/en.yml
config/locales/ja.yml
config/locales/pl.yml
config/locales/simple_form.ja.yml
config/locales/simple_form.pl.yml
config/navigation.rb
config/routes.rb
config/settings.yml
db/schema.rb
spec/controllers/application_controller_spec.rb
spec/lib/sanitize_config_spec.rb

diff --cc Gemfile
Simple merge
diff --cc Gemfile.lock
Simple merge
index 5850bd56d641d1c199449ba1892d0d9fd1150591,33bac9bbc72b2ddde0ee9dc1974188d9fbe8eab0..a6e33a5d9bcbde1922adaf3ecdbf4b6427666476
@@@ -1,16 -1,19 +1,20 @@@
  # frozen_string_literal: true
  
  class AboutController < ApplicationController
 +  before_action :set_pack
    layout 'public'
  
-   before_action :set_instance_presenter, only: [:show, :more, :terms]
+   before_action :set_body_classes, only: :show
+   before_action :set_instance_presenter
+   before_action :set_expires_in
  
-   def show
-     @hide_navbar = true
-   end
+   skip_before_action :check_user_permissions, only: [:more, :terms]
  
-   def more; end
+   def show; end
+   def more
+     flash.now[:notice] = I18n.t('about.instance_actor_flash') if params[:instance_actor]
+   end
  
    def terms; end
  
index 051b6ecbd16510b2be988d052acb4758c2738ff2,fc913c2ecda707f5da3896f184d909bf3ee392c4..ff684e31e4c5794a1791a3ea16863c7a9ac98662
@@@ -10,10 -12,8 +12,9 @@@ class AccountsController < ApplicationC
    def show
      respond_to do |format|
        format.html do
-         mark_cacheable! unless user_signed_in?
 +        use_pack 'public'
+         expires_in 0, public: true unless user_signed_in?
  
-         @body_classes      = 'with-modals'
          @pinned_statuses   = []
          @endorsed_accounts = @account.endorsed_accounts.to_a.sample(4)
  
index aedfeb70e43671b21af8abfea406f23a06b99b44,e74e4755f851c4ff2193e6b0375dcc65ce9ef817..faa2df1b51838d1c2fa5013af1dbf4bc2592b194
@@@ -30,7 -30,7 +30,8 @@@ module Admi
        @trending_hashtags     = TrendingTags.get(7)
        @profile_directory     = Setting.profile_directory
        @timeline_preview      = Setting.timeline_preview
 +      @keybase_integration   = Setting.enable_keybase
+       @spam_check_enabled    = Setting.spam_check_enabled
      end
  
      private
index fab9c8462072cccb82038ce4e86d36c9313776b3,6e873de5b6265cb067e95a72afb7e659a93c80f8..e2ba9bf00126c9553a08643fd2198ec270c2ce0b
@@@ -8,8 -10,7 +10,8 @@@ class FollowerAccountsController < Appl
    def index
      respond_to do |format|
        format.html do
-         mark_cacheable! unless user_signed_in?
 +        use_pack 'public'
+         expires_in 0, public: true unless user_signed_in?
  
          next if @account.user_hides_network?
  
    end
  
    def collection_presenter
-     if params[:page].present?
 +    options = { type: :ordered }
 +    options[:size] = @account.followers_count unless Setting.hide_followers_count || @account.user&.setting_hide_followers_count
+     if page_requested?
        ActivityPub::CollectionPresenter.new(
          id: account_followers_url(@account, page: params.fetch(:page, 1)),
 -        type: :ordered,
 -        size: @account.followers_count,
          items: follows.map { |f| ActivityPub::TagManager.instance.uri_for(f.account) },
          part_of: account_followers_url(@account),
          next: page_url(follows.next_page),
index 27211604064c29894bfb0b32e0523dbe89fbfc07,07d62f7dd1bd183ee0b6561d636733baa8c32757..49f1f32189bf1a54dd70716018b78fdf173e9619
@@@ -8,8 -10,7 +10,8 @@@ class FollowingAccountsController < App
    def index
      respond_to do |format|
        format.html do
-         mark_cacheable! unless user_signed_in?
 +        use_pack 'public'
+         expires_in 0, public: true unless user_signed_in?
  
          next if @account.user_hides_network?
  
Simple merge
index 17bc1940ad39868bb2688ad991b9a267a4566be4,0fb71d3352472b9eee566537f63bc8c050594923..46dd444a4d2df9643a39987616f2e0dcfe145120
@@@ -1,11 -1,10 +1,11 @@@
  # frozen_string_literal: true
  
  class RemoteFollowController < ApplicationController
+   include AccountOwnedConcern
    layout 'modal'
  
-   before_action :set_account
 +  before_action :set_pack
-   before_action :gone, if: :suspended_account?
    before_action :set_body_classes
  
    def new
      { acct: session[:remote_follow] }
    end
  
-   def set_account
-     @account = Account.find_local!(params[:account_username])
-   end
-   def suspended_account?
-     @account.suspended?
-   end
 +  def set_pack
 +    use_pack 'modal'
 +  end
 +
    def set_body_classes
      @body_classes = 'modal-layout'
      @hide_header  = true
index 66ba260aada2408f8f84fbd02fef52938ef589be,22e7519f9a10e3c5dfd26abe9019daffa23683fd..0190a3c54566f5a9acadc8723108120b306412c2
@@@ -27,16 -25,9 +25,11 @@@ class StatusesController < ApplicationC
    def show
      respond_to do |format|
        format.html do
 +        use_pack 'public'
 +
          expires_in 10.seconds, public: true if current_account.nil?
-         @body_classes = 'with-modals'
          set_ancestors
          set_descendants
-         render 'stream_entries/show'
        end
  
        format.json do
index 5cb048c1a7dcfa5965af510d399f56223b835243,d08e5a61a5c36c42bca09efc534d35f3f2bf131e..b4e6dbc925cb735086751d00c47b8f57246306f8
@@@ -9,11 -13,10 +13,11 @@@ class TagsController < ApplicationContr
    before_action :set_instance_presenter
  
    def show
-     @tag = Tag.find_normalized!(params[:id])
      respond_to do |format|
        format.html do
 +        use_pack 'about'
+         expires_in 0, public: true
          @initial_state_json = ActiveModelSerializers::SerializableResource.new(
            InitialStatePresenter.new(settings: {}, token: current_session&.token),
            serializer: InitialStateSerializer
Simple merge
index 33b7a207d060c650f5863e3bc51a1aae0e6e6410,0000000000000000000000000000000000000000..0f422213980c81f4d872dc257b81f60dbf4a4a5c
mode 100644,000000..100644
--- /dev/null
@@@ -1,66 -1,0 +1,66 @@@
-     if(target.getAttribute('data-autoplay') === 'false' && target.src !== swapSrc) {
 +//  This file will be loaded on public pages, regardless of theme.
 +
 +import createHistory from 'history/createBrowserHistory';
 +import ready from '../mastodon/ready';
 +
 +const { delegate } = require('rails-ujs');
 +const { length } = require('stringz');
 +
 +delegate(document, '.webapp-btn', 'click', ({ target, button }) => {
 +  if (button !== 0) {
 +    return true;
 +  }
 +  window.location.href = target.href;
 +  return false;
 +});
 +
 +delegate(document, '.status__content__spoiler-link', 'click', function() {
 +  const contentEl = this.parentNode.parentNode.querySelector('.e-content');
 +
 +  if (contentEl.style.display === 'block') {
 +    contentEl.style.display = 'none';
 +    this.parentNode.style.marginBottom = 0;
 +  } else {
 +    contentEl.style.display = 'block';
 +    this.parentNode.style.marginBottom = null;
 +  }
 +
 +  return false;
 +});
 +
 +delegate(document, '.modal-button', 'click', e => {
 +  e.preventDefault();
 +
 +  let href;
 +
 +  if (e.target.nodeName !== 'A') {
 +    href = e.target.parentNode.href;
 +  } else {
 +    href = e.target.href;
 +  }
 +
 +  window.open(href, 'mastodon-intent', 'width=445,height=600,resizable=no,menubar=no,status=no,scrollbars=yes');
 +});
 +
 +const getProfileAvatarAnimationHandler = (swapTo) => {
 +  //animate avatar gifs on the profile page when moused over
 +  return ({ target }) => {
 +    const swapSrc = target.getAttribute(swapTo);
 +    //only change the img source if autoplay is off and the image src is actually different
++    if(target.getAttribute('data-autoplay') !== 'true' && target.src !== swapSrc) {
 +      target.src = swapSrc;
 +    }
 +  };
 +};
 +
 +delegate(document, 'img#profile_page_avatar', 'mouseover', getProfileAvatarAnimationHandler('data-original'));
 +
 +delegate(document, 'img#profile_page_avatar', 'mouseout', getProfileAvatarAnimationHandler('data-static'));
 +
 +delegate(document, '#account_header', 'change', ({ target }) => {
 +  const header = document.querySelector('.card .card__img img');
 +  const [file] = target.files || [];
 +  const url = file ? URL.createObjectURL(file) : header.dataset.originalSrc;
 +
 +  header.src = url;
 +});
index 69e6ba0eccc1185fcfcb1f204ddf07062786a02b,0c60d828e9a6697714edcae5a2d6f39855d655fd..69441d315027e6ff992abb0c2d76b27ac248eafe
@@@ -91,15 -108,127 +91,7 @@@ function main() 
      if (parallaxComponents.length > 0 ) {
        new Rellax('.parallax', { speed: -1 });
      }
-     if (document.body.classList.contains('with-modals')) {
-       const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
-       const scrollbarWidthStyle = document.createElement('style');
-       scrollbarWidthStyle.id = 'scrollbar-width';
-       document.head.appendChild(scrollbarWidthStyle);
-       scrollbarWidthStyle.sheet.insertRule(`body.with-modals--active { margin-right: ${scrollbarWidth}px; }`, 0);
-     }
    });
 -
 -  delegate(document, '.webapp-btn', 'click', ({ target, button }) => {
 -    if (button !== 0) {
 -      return true;
 -    }
 -    window.location.href = target.href;
 -    return false;
 -  });
 -
 -  delegate(document, '.status__content__spoiler-link', 'click', function() {
 -    const contentEl = this.parentNode.parentNode.querySelector('.e-content');
 -
 -    if (contentEl.style.display === 'block') {
 -      contentEl.style.display = 'none';
 -      this.parentNode.style.marginBottom = 0;
 -    } else {
 -      contentEl.style.display = 'block';
 -      this.parentNode.style.marginBottom = null;
 -    }
 -
 -    return false;
 -  });
 -
 -  delegate(document, '.modal-button', 'click', e => {
 -    e.preventDefault();
 -
 -    let href;
 -
 -    if (e.target.nodeName !== 'A') {
 -      href = e.target.parentNode.href;
 -    } else {
 -      href = e.target.href;
 -    }
 -
 -    window.open(href, 'mastodon-intent', 'width=445,height=600,resizable=no,menubar=no,status=no,scrollbars=yes');
 -  });
 -
 -  delegate(document, '#account_display_name', 'input', ({ target }) => {
 -    const name = document.querySelector('.card .display-name strong');
 -    if (name) {
 -      if (target.value) {
 -        name.innerHTML = emojify(escapeTextContentForBrowser(target.value));
 -      } else {
 -        name.textContent = document.querySelector('#default_account_display_name').textContent;
 -      }
 -    }
 -  });
 -
 -  delegate(document, '#account_avatar', 'change', ({ target }) => {
 -    const avatar = document.querySelector('.card .avatar img');
 -    const [file] = target.files || [];
 -    const url = file ? URL.createObjectURL(file) : avatar.dataset.originalSrc;
 -
 -    avatar.src = url;
 -  });
 -
 -  const getProfileAvatarAnimationHandler = (swapTo) => {
 -    //animate avatar gifs on the profile page when moused over
 -    return ({ target }) => {
 -      const swapSrc = target.getAttribute(swapTo);
 -      //only change the img source if autoplay is off and the image src is actually different
 -      if(target.getAttribute('data-autoplay') !== 'true' && target.src !== swapSrc) {
 -        target.src = swapSrc;
 -      }
 -    };
 -  };
 -
 -  delegate(document, 'img#profile_page_avatar', 'mouseover', getProfileAvatarAnimationHandler('data-original'));
 -
 -  delegate(document, 'img#profile_page_avatar', 'mouseout', getProfileAvatarAnimationHandler('data-static'));
 -
 -  delegate(document, '#account_header', 'change', ({ target }) => {
 -    const header = document.querySelector('.card .card__img img');
 -    const [file] = target.files || [];
 -    const url = file ? URL.createObjectURL(file) : header.dataset.originalSrc;
 -
 -    header.src = url;
 -  });
 -
 -  delegate(document, '#account_locked', 'change', ({ target }) => {
 -    const lock = document.querySelector('.card .display-name i');
 -
 -    if (target.checked) {
 -      lock.style.display = 'inline';
 -    } else {
 -      lock.style.display = 'none';
 -    }
 -  });
 -
 -  delegate(document, '.input-copy input', 'click', ({ target }) => {
 -    target.focus();
 -    target.select();
 -    target.setSelectionRange(0, target.value.length);
 -  });
 -
 -  delegate(document, '.input-copy button', 'click', ({ target }) => {
 -    const input = target.parentNode.querySelector('.input-copy__wrapper input');
 -
 -    const oldReadOnly = input.readonly;
 -
 -    input.readonly = false;
 -    input.focus();
 -    input.select();
 -    input.setSelectionRange(0, input.value.length);
 -
 -    try {
 -      if (document.execCommand('copy')) {
 -        input.blur();
 -        target.parentNode.classList.add('copied');
 -
 -        setTimeout(() => {
 -          target.parentNode.classList.remove('copied');
 -        }, 700);
 -      }
 -    } catch (err) {
 -      console.error(err);
 -    }
 -
 -    input.readonly = oldReadOnly;
 -  });
  }
  
  loadPolyfills().then(main).catch(error => {
Simple merge
index 3d7b0dda3be069288122e8b2d6a85bdb4f89ee90,ccd116d6e2e3656007c574c1b644a749364272fc..3370fbc5e6f7850227534c23015663729a081a20
@@@ -62,10 -63,11 +63,15 @@@ class Account < ApplicationRecor
    include AccountCounters
    include DomainNormalizable
  
 +  MAX_DISPLAY_NAME_LENGTH = (ENV['MAX_DISPLAY_NAME_CHARS'] || 30).to_i
 +  MAX_NOTE_LENGTH = (ENV['MAX_BIO_CHARS'] || 500).to_i
 +  MAX_FIELDS = (ENV['MAX_PROFILE_FIELDS'] || 4).to_i
 +
+   TRUST_LEVELS = {
+     untrusted: 0,
+     trusted: 1,
+   }.freeze
    enum protocol: [:ostatus, :activitypub]
  
    validates :username, presence: true
    validates :username, format: { with: /\A#{USERNAME_RE}\z/i }, if: -> { !local? && will_save_change_to_username? }
  
    # Local user validations
-   validates :username, format: { with: /\A[a-z0-9_]+\z/i }, length: { maximum: 30 }, if: -> { local? && will_save_change_to_username? }
+   validates :username, format: { with: /\A[a-z0-9_]+\z/i }, length: { maximum: 30 }, if: -> { local? && will_save_change_to_username? && actor_type != 'Application' }
    validates_with UniqueUsernameValidator, if: -> { local? && will_save_change_to_username? }
    validates_with UnreservedUsernameValidator, if: -> { local? && will_save_change_to_username? }
 -  validates :display_name, length: { maximum: 30 }, if: -> { local? && will_save_change_to_display_name? }
 -  validates :note, note_length: { maximum: 500 }, if: -> { local? && will_save_change_to_note? }
 -  validates :fields, length: { maximum: 4 }, if: -> { local? && will_save_change_to_fields? }
 +  validates :display_name, length: { maximum: MAX_DISPLAY_NAME_LENGTH }, if: -> { local? && will_save_change_to_display_name? }
 +  validates :note, note_length: { maximum: MAX_NOTE_LENGTH }, if: -> { local? && will_save_change_to_note? }
 +  validates :fields, length: { maximum: MAX_FIELDS }, if: -> { local? && will_save_change_to_fields? }
  
    scope :remote, -> { where.not(domain: nil) }
    scope :local, -> { where(domain: nil) }
index ecccaf35ef4d7449250a5757dd82186aa802c876,0921e325225155eee141c6260a5b8990e86296d6..2877b9c254774aeef633bf4ceb5874007fc48870
@@@ -11,10 -11,8 +11,9 @@@ module AccountAssociation
      has_many :identity_proofs, class_name: 'AccountIdentityProof', dependent: :destroy, inverse_of: :account
  
      # Timelines
-     has_many :stream_entries, inverse_of: :account, dependent: :destroy
      has_many :statuses, inverse_of: :account, dependent: :destroy
      has_many :favourites, inverse_of: :account, dependent: :destroy
 +    has_many :bookmarks, inverse_of: :account, dependent: :destroy
      has_many :mentions, inverse_of: :account, dependent: :destroy
      has_many :notifications, inverse_of: :account, dependent: :destroy
      has_many :conversations, class_name: 'AccountConversation', dependent: :destroy, inverse_of: :account
index 0e9bfb265ed17f3efa7d3e04e7db333fbf0db5bc,2c03c88a8cf16488ec15e0905f6eecb9c967b1d3..ecaed44f60be59150a02991e1089ba727025f474
@@@ -32,8 -28,7 +32,9 @@@ class Form::AdminSetting
      thumbnail
      hero
      mascot
 +    show_reblogs_in_public_timelines
 +    show_replies_in_public_timelines
+     spam_check_enabled
    ).freeze
  
    BOOLEAN_KEYS = %i(
      show_known_fediverse_at_about_page
      preview_sensitive_media
      profile_directory
 +    hide_followers_count
 +    enable_keybase
 +    show_reblogs_in_public_timelines
 +    show_replies_in_public_timelines
+     spam_check_enabled
    ).freeze
  
    UPLOAD_KEYS = %i(
Simple merge
Simple merge
index bb903a45cb4e07bff706b06750aa4285a10a9d4f,31c99630c3bc18095a0b72454399bce88bb519c6..72fc921956f818d9530ab575b4d030c70c3a81e5
@@@ -103,10 -103,10 +103,10 @@@ class User < ApplicationRecor
  
    has_many :session_activations, dependent: :destroy
  
 -  delegate :auto_play_gif, :default_sensitive, :unfollow_modal, :boost_modal, :delete_modal,
 -           :reduce_motion, :system_font_ui, :noindex, :theme, :display_media, :hide_network,
 +  delegate :auto_play_gif, :default_sensitive, :unfollow_modal, :boost_modal, :favourite_modal, :delete_modal,
 +           :reduce_motion, :system_font_ui, :noindex, :flavour, :skin, :display_media, :hide_network, :hide_followers_count,
             :expand_spoilers, :default_language, :aggregate_reblogs, :show_application,
-            :advanced_layout, :default_content_type, :use_blurhash, :use_pending_items, to: :settings, prefix: :setting, allow_nil: false
 -           :advanced_layout, :use_blurhash, :use_pending_items, to: :settings, prefix: :setting, allow_nil: false
++           :advanced_layout, :default_content_type, :use_blurhash, :use_pending_items, :use_pending_items, to: :settings, prefix: :setting, allow_nil: false
  
    attr_reader :invite_code
    attr_writer :external
Simple merge
index 2fe009c9135f14314f307d4c3b5cc653c86dcf2b,27dc460a6b4b352f77cc66cd14a2cdaeeb003263..bbee47cb701774e6c92e3a44b412155244d31cae
@@@ -46,12 -40,7 +40,8 @@@ class BatchedRemoveStatusService < Base
      # Cannot be batched
      statuses.each do |status|
        unpush_from_public_timelines(status)
-       batch_salmon_slaps(status) if status.local?
 +      unpush_from_direct_timelines(status) if status.direct_visibility?
      end
-     Pubsubhubbub::RawDistributionWorker.push_bulk(@stream_entry_batches) { |batch| batch }
-     NotificationWorker.push_bulk(@salmon_batches) { |batch| batch }
    end
  
    private
        end
      end
    end
-   def batch_salmon_slaps(status)
-     return if @mentions[status.id].empty?
-     recipients = @mentions[status.id].map(&:account).reject(&:local?).select(&:ostatus?).uniq(&:domain).map(&:id)
-     recipients.each do |recipient_id|
-       @salmon_batches << [build_xml(status.stream_entry), status.account_id, recipient_id]
-     end
-   end
-   def build_xml(stream_entry)
-     return @activity_xml[stream_entry.id] if @activity_xml.key?(stream_entry.id)
-     @activity_xml[stream_entry.id] = stream_entry_to_xml(stream_entry)
-   end
 +
 +  def unpush_from_direct_timelines(status)
 +    payload = @json_payloads[status.id]
 +    redis.pipelined do
 +      @mentions[status.id].each do |mention|
 +        FeedManager.instance.unpush_from_direct(mention.account, status) if mention.account.local?
 +      end
 +      FeedManager.instance.unpush_from_direct(status.account, status) if status.account.local?
 +    end
 +  end
  end
index 6d7c44913b7fea5b1548dd816d636c72c5bb3d65,34ec6d50486e62f1710dc31a4bfefe9e75d56e9d..b3647133940ce9a35a52696da4ba2657825d67c7
@@@ -91,12 -88,7 +91,7 @@@ class PostStatusService < BaseServic
    def postprocess_status!
      LinkCrawlWorker.perform_async(@status.id) unless @status.spoiler_text?
      DistributionWorker.perform_async(@status.id)
-     unless @status.local_only?
-       Pubsubhubbub::DistributionWorker.perform_async(@status.stream_entry.id)
-       ActivityPub::DistributionWorker.perform_async(@status.id)
-     end
 -    ActivityPub::DistributionWorker.perform_async(@status.id)
++    ActivityPub::DistributionWorker.perform_async(@status.id) unless @status.local_only?
      PollExpirationNotifyWorker.perform_at(@status.poll.expires_at, @status.poll.id) if @status.poll
    end
  
index 1804e0c93ff53fae4bd8812948f1a405b2fa0a83,90dca9740ef9ef402208cb59ebe021c30ef962ea..a374206eb8304ab6714d50d5f685cb4767a6f646
@@@ -49,9 -48,7 +48,7 @@@ class ProcessMentionsService < BaseServ
  
      if mentioned_account.local?
        LocalNotificationWorker.perform_async(mentioned_account.id, mention.id, mention.class.name)
-     elsif mentioned_account.ostatus? && !@status.stream_entry.hidden? && !@status.local_only?
-       NotificationWorker.perform_async(ostatus_xml, @status.account_id, mentioned_account.id)
 -    elsif mentioned_account.activitypub?
 +    elsif mentioned_account.activitypub? && !@status.local_only?
        ActivityPub::DeliveryWorker.perform_async(activitypub_json, mention.status.account_id, mentioned_account.inbox_url)
      end
    end
index 09403bae0f45e0cb2c94d97e29602cc0d9ab4657,3bb460fcaf946e6db5a998b3c68d6c12bc4a84d9..0b12f143ca817fe3e6cf25bdf805cd04e1a37472
@@@ -24,11 -23,7 +23,7 @@@ class ReblogService < BaseServic
      reblog = account.statuses.create!(reblog: reblogged_status, text: '', visibility: visibility)
  
      DistributionWorker.perform_async(reblog.id)
-     unless reblogged_status.local_only?
-       Pubsubhubbub::DistributionWorker.perform_async(reblog.stream_entry.id)
-       ActivityPub::DistributionWorker.perform_async(reblog.id)
-     end
 -    ActivityPub::DistributionWorker.perform_async(reblog.id)
++    ActivityPub::DistributionWorker.perform_async(reblog.id) unless reblogged_status.local_only?
  
      create_notification(reblog)
      bump_potential_friendship(account, reblog)
index 9d5d0fc147508510ff18782800e72ab3ff19361a,a69fce8b85846727ff5d5469d2d69ff311d36265..958a67e8f14bd19720969af247975edbb4244a46
@@@ -25,7 -23,7 +23,8 @@@ class RemoveStatusService < BaseServic
          remove_from_hashtags
          remove_from_public
          remove_from_media if status.media_attachments.any?
 +        remove_from_direct if status.direct_visibility?
+         remove_from_spam_check
  
          @status.destroy!
        else
      redis.publish('timeline:public:local:media', @payload) if @status.local?
    end
  
 +  def remove_from_direct
 +    @mentions.each do |mention|
 +      FeedManager.instance.unpush_from_direct(mention.account, @status) if mention.account.local?
 +    end
 +  end
 +
+   def remove_from_spam_check
+     redis.zremrangebyscore("spam_check:#{@status.account_id}", @status.id, @status.id)
+   end
    def lock_options
      { redis: Redis.current, key: "distribute:#{@status.id}" }
    end
Simple merge
index 76dbf4388a17586ac2eb56d498afb886e8322a54,77cc1a2a00825a3753987216caee5a9e0c15b687..54cf9af5d445640a686fd61e9ce809526c9a08d8
            = feature_hint(link_to(t('admin.dashboard.feature_profile_directory'), edit_admin_settings_path), @profile_directory)
          %li
            = feature_hint(link_to(t('admin.dashboard.feature_timeline_preview'), edit_admin_settings_path), @timeline_preview)
 +        %li
 +          = feature_hint(link_to(t('admin.dashboard.keybase'), edit_admin_settings_path), @keybase_integration)
          %li
            = feature_hint(link_to(t('admin.dashboard.feature_relay'), admin_relays_path), @relay_enabled)
+         %li
+           = feature_hint(link_to(t('admin.dashboard.feature_spam_check'), edit_admin_settings_path), @spam_check_enabled)
  
    .dashboard__widgets__versions
      %div
index a8c9f6a583c5c43d8ec64aa846d11b453fb87ffd,b3bf3849c0621ebd2942f681a136cd6ce8edcefa..854f4cf8708f0801b733f1f72fa9873fac2da9af
    .fields-group
      = f.input :profile_directory, as: :boolean, wrapper: :with_label, label: t('admin.settings.profile_directory.title'), hint: t('admin.settings.profile_directory.desc_html')
  
 +  .fields-group
 +    = f.input :hide_followers_count, as: :boolean, wrapper: :with_label, label: t('admin.settings.hide_followers_count.title'), hint: t('admin.settings.hide_followers_count.desc_html')
 +
 +  .fields-group
 +    = f.input :enable_keybase, as: :boolean, wrapper: :with_label, label: t('admin.settings.enable_keybase.title'), hint: t('admin.settings.enable_keybase.desc_html')
 +
 +  .fields-group
 +    = f.input :show_reblogs_in_public_timelines, as: :boolean, wrapper: :with_label, label: t('admin.settings.show_reblogs_in_public_timelines.title'), hint: t('admin.settings.show_reblogs_in_public_timelines.desc_html')
 +
 +  .fields-group
 +    = f.input :show_replies_in_public_timelines, as: :boolean, wrapper: :with_label, label: t('admin.settings.show_replies_in_public_timelines.title'), hint: t('admin.settings.show_replies_in_public_timelines.desc_html')
 +
+   .fields-group
+     = f.input :spam_check_enabled, as: :boolean, wrapper: :with_label, label: t('admin.settings.spam_check_enabled.title'), hint: t('admin.settings.spam_check_enabled.desc_html')
    %hr.spacer/
  
    .fields-group
Simple merge
Simple merge
Simple merge
Simple merge
index b74bbc2f545651d00894f01cd5cda3bb0d93a73f,553900855ee5c47695467a165cb624bbde29e2af..6a9cf13eb882a6cfe5a10bb7cdf5bc602c605692
@@@ -34,7 -34,7 +34,8 @@@ pl
          setting_hide_network: Informacje o tym, kto Cię śledzi i kogo śledzisz nie będą widoczne
          setting_noindex: Wpływa na widoczność strony profilu i Twoich wpisów
          setting_show_application: W informacjach o wpisie będzie widoczna informacja o aplikacji, z której został wysłany
 +        setting_skin: Zmienia wygląd używanej odmiany Mastodona
+         setting_use_blurhash: Gradienty są oparte na kolorach ukrywanej zawartości, ale uniewidaczniają wszystkie szczegóły
          username: Twoja nazwa użytkownika będzie niepowtarzalna na %{domain}
          whole_word: Jeśli słowo lub fraza składa się jedynie z liter lub cyfr, filtr będzie zastosowany tylko do pełnych wystąpień
        featured_tag:
          setting_noindex: Nie indeksuj mojego profilu w wyszukiwarkach internetowych
          setting_reduce_motion: Ogranicz ruch w animacjach
          setting_show_application: Informuj o aplikacji z której wysłano wpisy
 +        setting_skin: Motyw
          setting_system_font_ui: Używaj domyślnej czcionki systemu
 -        setting_theme: Motyw strony
          setting_unfollow_modal: Pytaj o potwierdzenie przed cofnięciem śledzenia
+         setting_use_blurhash: Pokazuj kolorowe gradienty dla ukrytej zawartości multimedialnej
          severity: Priorytet
          type: Importowane dane
          username: Nazwa użytkownika
Simple merge
index 1b88fe5e3e922bc6447536a227a30414f92ecd35,27b53664196efbf0952e268eba5273e0332b1375..66be635a514d260bc257aa32ccb17999a161f7fb
@@@ -324,16 -302,10 +308,15 @@@ Rails.application.routes.draw d
  
        get '/search', to: 'search#index', as: :search
  
-       resources :follows,      only: [:create]
        resources :media,        only: [:create, :update]
        resources :blocks,       only: [:index]
 -      resources :mutes,        only: [:index]
 +      resources :mutes,        only: [:index] do
 +        collection do
 +          get 'details'
 +        end
 +      end
        resources :favourites,   only: [:index]
 +      resources :bookmarks,    only: [:index]
        resources :reports,      only: [:create]
        resources :filters,      only: [:index, :create, :show, :update, :destroy]
        resources :endorsements, only: [:index]
index bc97f650b693ec39f6458acac9bdf86b6142c26c,ad2970bb7ec66dd264991b47a07f589e16f92975..328a25a5ad28c9ff002f4ed57f30e8ffe2ab8ff1
@@@ -65,9 -61,7 +65,10 @@@ defaults: &default
    activity_api_enabled: true
    peers_api_enabled: true
    show_known_fediverse_at_about_page: true
 +  show_reblogs_in_public_timelines: false
 +  show_replies_in_public_timelines: false
 +  default_content_type: 'text/plain'
+   spam_check_enabled: true
  
  development:
    <<: *defaults
diff --cc db/schema.rb
Simple merge
index c5143bcefa73d875e102bf9928ed8bdf4aaba27a,54bd8693cc871103b6f45026c536843701019a3f..faefac803f859ace16f86c4ed42564528c5a0879
@@@ -7,12 -7,24 +7,16 @@@ describe Sanitize::Config d
    describe '::MASTODON_STRICT' do
      subject { Sanitize::Config::MASTODON_STRICT }
  
 -    it 'converts h1 to p' do
 -      expect(Sanitize.fragment('<h1>Foo</h1>', subject)).to eq '<p>Foo</p>'
 +    it 'keeps h1' do
 +      expect(Sanitize.fragment('<h1>Foo</h1>', subject)).to eq '<h1>Foo</h1>'
      end
  
 -    it 'converts ul to p' do
 -      expect(Sanitize.fragment('<p>Check out:</p><ul><li>Foo</li><li>Bar</li></ul>', subject)).to eq '<p>Check out:</p><p>Foo<br>Bar</p>'
 -    end
 -
 -    it 'converts p inside ul' do
 -      expect(Sanitize.fragment('<ul><li><p>Foo</p><p>Bar</p></li><li>Baz</li></ul>', subject)).to eq '<p>Foo<br>Bar<br>Baz</p>'
 -    end
 -
 -    it 'converts ul inside ul' do
 -      expect(Sanitize.fragment('<ul><li>Foo</li><li><ul><li>Bar</li><li>Baz</li></ul></li></ul>', subject)).to eq '<p>Foo<br>Bar<br>Baz</p>'
 +    it 'keeps ul' do
 +      expect(Sanitize.fragment('<p>Check out:</p><ul><li>Foo</li><li>Bar</li></ul>', subject)).to eq '<p>Check out:</p><ul><li>Foo</li><li>Bar</li></ul>'
      end
+     it 'keep links in lists' do
+       expect(Sanitize.fragment('<p>Check out:</p><ul><li><a href="https://joinmastodon.org" rel="nofollow noopener" target="_blank">joinmastodon.org</a></li><li>Bar</li></ul>', subject)).to eq '<p>Check out:</p><p><a href="https://joinmastodon.org" rel="nofollow noopener" target="_blank">joinmastodon.org</a><br>Bar</p>'
+     end
    end
  end