Refactor controllers for statuses, accounts, and more (#11249)
This commit is contained in:
		@@ -3,11 +3,11 @@
 | 
				
			|||||||
class AboutController < ApplicationController
 | 
					class AboutController < ApplicationController
 | 
				
			||||||
  layout 'public'
 | 
					  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
 | 
					  def show; end
 | 
				
			||||||
    @hide_navbar = true
 | 
					 | 
				
			||||||
  end
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def more; end
 | 
					  def more; end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -27,4 +27,12 @@ class AboutController < ApplicationController
 | 
				
			|||||||
  def set_instance_presenter
 | 
					  def set_instance_presenter
 | 
				
			||||||
    @instance_presenter = InstancePresenter.new
 | 
					    @instance_presenter = InstancePresenter.new
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def set_body_classes
 | 
				
			||||||
 | 
					    @hide_navbar = true
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def set_expires_in
 | 
				
			||||||
 | 
					    expires_in 0, public: true
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,13 +6,13 @@ class AccountsController < ApplicationController
 | 
				
			|||||||
  include AccountControllerConcern
 | 
					  include AccountControllerConcern
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  before_action :set_cache_headers
 | 
					  before_action :set_cache_headers
 | 
				
			||||||
 | 
					  before_action :set_body_classes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def show
 | 
					  def show
 | 
				
			||||||
    respond_to do |format|
 | 
					    respond_to do |format|
 | 
				
			||||||
      format.html do
 | 
					      format.html do
 | 
				
			||||||
        mark_cacheable! unless user_signed_in?
 | 
					        expires_in 0, public: true unless user_signed_in?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        @body_classes      = 'with-modals'
 | 
					 | 
				
			||||||
        @pinned_statuses   = []
 | 
					        @pinned_statuses   = []
 | 
				
			||||||
        @endorsed_accounts = @account.endorsed_accounts.to_a.sample(4)
 | 
					        @endorsed_accounts = @account.endorsed_accounts.to_a.sample(4)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -32,22 +32,25 @@ class AccountsController < ApplicationController
 | 
				
			|||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      format.rss do
 | 
					      format.rss do
 | 
				
			||||||
        mark_cacheable!
 | 
					        expires_in 0, public: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        @statuses = cache_collection(default_statuses.without_reblogs.without_replies.limit(PAGE_SIZE), Status)
 | 
					        @statuses = cache_collection(default_statuses.without_reblogs.without_replies.limit(PAGE_SIZE), Status)
 | 
				
			||||||
        render xml: RSS::AccountSerializer.render(@account, @statuses)
 | 
					        render xml: RSS::AccountSerializer.render(@account, @statuses)
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      format.json do
 | 
					      format.json do
 | 
				
			||||||
        render_cached_json(['activitypub', 'actor', @account], content_type: 'application/activity+json') do
 | 
					        expires_in 3.minutes, public: true
 | 
				
			||||||
          ActiveModelSerializers::SerializableResource.new(@account, serializer: ActivityPub::ActorSerializer, adapter: ActivityPub::Adapter)
 | 
					        render json: @account, content_type: 'application/activity+json', serializer: ActivityPub::ActorSerializer, adapter: ActivityPub::Adapter
 | 
				
			||||||
        end
 | 
					 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private
 | 
					  private
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def set_body_classes
 | 
				
			||||||
 | 
					    @body_classes = 'with-modals'
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def show_pinned_statuses?
 | 
					  def show_pinned_statuses?
 | 
				
			||||||
    [replies_requested?, media_requested?, tag_requested?, params[:max_id].present?, params[:min_id].present?].none?
 | 
					    [replies_requested?, media_requested?, tag_requested?, params[:max_id].present?, params[:min_id].present?].none?
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,29 +2,19 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class ActivityPub::CollectionsController < Api::BaseController
 | 
					class ActivityPub::CollectionsController < Api::BaseController
 | 
				
			||||||
  include SignatureVerification
 | 
					  include SignatureVerification
 | 
				
			||||||
 | 
					  include AccountOwnedConcern
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  before_action :set_account
 | 
					 | 
				
			||||||
  before_action :set_size
 | 
					  before_action :set_size
 | 
				
			||||||
  before_action :set_statuses
 | 
					  before_action :set_statuses
 | 
				
			||||||
  before_action :set_cache_headers
 | 
					  before_action :set_cache_headers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def show
 | 
					  def show
 | 
				
			||||||
    render_cached_json(['activitypub', 'collection', @account, params[:id]], content_type: 'application/activity+json') do
 | 
					    expires_in 3.minutes, public: true
 | 
				
			||||||
      ActiveModelSerializers::SerializableResource.new(
 | 
					    render json: collection_presenter, content_type: 'application/activity+json', serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter, skip_activities: true
 | 
				
			||||||
        collection_presenter,
 | 
					 | 
				
			||||||
        serializer: ActivityPub::CollectionSerializer,
 | 
					 | 
				
			||||||
        adapter: ActivityPub::Adapter,
 | 
					 | 
				
			||||||
        skip_activities: true
 | 
					 | 
				
			||||||
      )
 | 
					 | 
				
			||||||
    end
 | 
					 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private
 | 
					  private
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def set_account
 | 
					 | 
				
			||||||
    @account = Account.find_local!(params[:account_username])
 | 
					 | 
				
			||||||
  end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  def set_statuses
 | 
					  def set_statuses
 | 
				
			||||||
    @statuses = scope_for_collection
 | 
					    @statuses = scope_for_collection
 | 
				
			||||||
    @statuses = cache_collection(@statuses, Status)
 | 
					    @statuses = cache_collection(@statuses, Status)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,8 +3,7 @@
 | 
				
			|||||||
class ActivityPub::InboxesController < Api::BaseController
 | 
					class ActivityPub::InboxesController < Api::BaseController
 | 
				
			||||||
  include SignatureVerification
 | 
					  include SignatureVerification
 | 
				
			||||||
  include JsonLdHelper
 | 
					  include JsonLdHelper
 | 
				
			||||||
 | 
					  include AccountOwnedConcern
 | 
				
			||||||
  before_action :set_account
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def create
 | 
					  def create
 | 
				
			||||||
    if unknown_deleted_account?
 | 
					    if unknown_deleted_account?
 | 
				
			||||||
@@ -27,8 +26,8 @@ class ActivityPub::InboxesController < Api::BaseController
 | 
				
			|||||||
    false
 | 
					    false
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def set_account
 | 
					  def account_required?
 | 
				
			||||||
    @account = Account.find_local!(params[:account_username]) if params[:account_username]
 | 
					    params[:account_username].present?
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def body
 | 
					  def body
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,8 +4,8 @@ class ActivityPub::OutboxesController < Api::BaseController
 | 
				
			|||||||
  LIMIT = 20
 | 
					  LIMIT = 20
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  include SignatureVerification
 | 
					  include SignatureVerification
 | 
				
			||||||
 | 
					  include AccountOwnedConcern
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  before_action :set_account
 | 
					 | 
				
			||||||
  before_action :set_statuses
 | 
					  before_action :set_statuses
 | 
				
			||||||
  before_action :set_cache_headers
 | 
					  before_action :set_cache_headers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -17,10 +17,6 @@ class ActivityPub::OutboxesController < Api::BaseController
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  private
 | 
					  private
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def set_account
 | 
					 | 
				
			||||||
    @account = Account.find_local!(params[:account_username])
 | 
					 | 
				
			||||||
  end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  def outbox_presenter
 | 
					  def outbox_presenter
 | 
				
			||||||
    if page_requested?
 | 
					    if page_requested?
 | 
				
			||||||
      ActivityPub::CollectionPresenter.new(
 | 
					      ActivityPub::CollectionPresenter.new(
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										68
									
								
								app/controllers/activitypub/replies_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								app/controllers/activitypub/replies_controller.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,68 @@
 | 
				
			|||||||
 | 
					# frozen_string_literal: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ActivityPub::RepliesController < Api::BaseController
 | 
				
			||||||
 | 
					  include SignatureAuthentication
 | 
				
			||||||
 | 
					  include Authorization
 | 
				
			||||||
 | 
					  include AccountOwnedConcern
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  DESCENDANTS_LIMIT = 60
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  before_action :set_status
 | 
				
			||||||
 | 
					  before_action :set_cache_headers
 | 
				
			||||||
 | 
					  before_action :set_replies
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def index
 | 
				
			||||||
 | 
					    render json: replies_collection_presenter, serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json', skip_activities: true
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def set_status
 | 
				
			||||||
 | 
					    @status = @account.statuses.find(params[:status_id])
 | 
				
			||||||
 | 
					    authorize @status, :show?
 | 
				
			||||||
 | 
					  rescue Mastodon::NotPermittedError
 | 
				
			||||||
 | 
					    raise ActiveRecord::RecordNotFound
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def set_replies
 | 
				
			||||||
 | 
					    @replies = page_params[:other_accounts] ? Status.where.not(account_id: @account.id) : @account.statuses
 | 
				
			||||||
 | 
					    @replies = @replies.where(in_reply_to_id: @status.id, visibility: [:public, :unlisted])
 | 
				
			||||||
 | 
					    @replies = @replies.paginate_by_min_id(DESCENDANTS_LIMIT, params[:min_id])
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def replies_collection_presenter
 | 
				
			||||||
 | 
					    page = ActivityPub::CollectionPresenter.new(
 | 
				
			||||||
 | 
					      id: account_status_replies_url(@account, @status, page_params),
 | 
				
			||||||
 | 
					      type: :unordered,
 | 
				
			||||||
 | 
					      part_of: account_status_replies_url(@account, @status),
 | 
				
			||||||
 | 
					      next: next_page,
 | 
				
			||||||
 | 
					      items: @replies.map { |status| status.local ? status : status.id }
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return page if page_requested?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ActivityPub::CollectionPresenter.new(
 | 
				
			||||||
 | 
					      id: account_status_replies_url(@account, @status),
 | 
				
			||||||
 | 
					      type: :unordered,
 | 
				
			||||||
 | 
					      first: page
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def page_requested?
 | 
				
			||||||
 | 
					    params[:page] == 'true'
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def next_page
 | 
				
			||||||
 | 
					    account_status_replies_url(
 | 
				
			||||||
 | 
					      @account,
 | 
				
			||||||
 | 
					      @status,
 | 
				
			||||||
 | 
					      page: true,
 | 
				
			||||||
 | 
					      min_id: @replies&.last&.id,
 | 
				
			||||||
 | 
					      other_accounts: !(@replies&.last&.account_id == @account.id && @replies.size == DESCENDANTS_LIMIT)
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def page_params
 | 
				
			||||||
 | 
					    params_slice(:other_accounts, :min_id).merge(page: true)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
@@ -1,10 +1,9 @@
 | 
				
			|||||||
# frozen_string_literal: true
 | 
					# frozen_string_literal: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Api::ProofsController < Api::BaseController
 | 
					class Api::ProofsController < Api::BaseController
 | 
				
			||||||
  before_action :set_account
 | 
					  include AccountOwnedConcern
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  before_action :set_provider
 | 
					  before_action :set_provider
 | 
				
			||||||
  before_action :check_account_approval
 | 
					 | 
				
			||||||
  before_action :check_account_suspension
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def index
 | 
					  def index
 | 
				
			||||||
    render json: @account, serializer: @provider.serializer_class
 | 
					    render json: @account, serializer: @provider.serializer_class
 | 
				
			||||||
@@ -16,15 +15,7 @@ class Api::ProofsController < Api::BaseController
 | 
				
			|||||||
    @provider = ProofProvider.find(params[:provider]) || raise(ActiveRecord::RecordNotFound)
 | 
					    @provider = ProofProvider.find(params[:provider]) || raise(ActiveRecord::RecordNotFound)
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def set_account
 | 
					  def username_param
 | 
				
			||||||
    @account = Account.find_local!(params[:username])
 | 
					    params[:username]
 | 
				
			||||||
  end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  def check_account_approval
 | 
					 | 
				
			||||||
    not_found if @account.user_pending?
 | 
					 | 
				
			||||||
  end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  def check_account_suspension
 | 
					 | 
				
			||||||
    gone if @account.suspended?
 | 
					 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -154,8 +154,4 @@ class ApplicationController < ActionController::Base
 | 
				
			|||||||
  def set_cache_headers
 | 
					  def set_cache_headers
 | 
				
			||||||
    response.headers['Vary'] = 'Accept'
 | 
					    response.headers['Vary'] = 'Accept'
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					 | 
				
			||||||
  def mark_cacheable!
 | 
					 | 
				
			||||||
    expires_in 0, public: true
 | 
					 | 
				
			||||||
  end
 | 
					 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,24 +3,19 @@
 | 
				
			|||||||
module AccountControllerConcern
 | 
					module AccountControllerConcern
 | 
				
			||||||
  extend ActiveSupport::Concern
 | 
					  extend ActiveSupport::Concern
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  include AccountOwnedConcern
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  FOLLOW_PER_PAGE = 12
 | 
					  FOLLOW_PER_PAGE = 12
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  included do
 | 
					  included do
 | 
				
			||||||
    layout 'public'
 | 
					    layout 'public'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    before_action :set_account
 | 
					 | 
				
			||||||
    before_action :check_account_approval
 | 
					 | 
				
			||||||
    before_action :check_account_suspension
 | 
					 | 
				
			||||||
    before_action :set_instance_presenter
 | 
					    before_action :set_instance_presenter
 | 
				
			||||||
    before_action :set_link_headers
 | 
					    before_action :set_link_headers
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private
 | 
					  private
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def set_account
 | 
					 | 
				
			||||||
    @account = Account.find_local!(username_param)
 | 
					 | 
				
			||||||
  end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  def set_instance_presenter
 | 
					  def set_instance_presenter
 | 
				
			||||||
    @instance_presenter = InstancePresenter.new
 | 
					    @instance_presenter = InstancePresenter.new
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
@@ -29,27 +24,15 @@ module AccountControllerConcern
 | 
				
			|||||||
    response.headers['Link'] = LinkHeader.new(
 | 
					    response.headers['Link'] = LinkHeader.new(
 | 
				
			||||||
      [
 | 
					      [
 | 
				
			||||||
        webfinger_account_link,
 | 
					        webfinger_account_link,
 | 
				
			||||||
        atom_account_url_link,
 | 
					 | 
				
			||||||
        actor_url_link,
 | 
					        actor_url_link,
 | 
				
			||||||
      ]
 | 
					      ]
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def username_param
 | 
					 | 
				
			||||||
    params[:account_username]
 | 
					 | 
				
			||||||
  end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  def webfinger_account_link
 | 
					  def webfinger_account_link
 | 
				
			||||||
    [
 | 
					    [
 | 
				
			||||||
      webfinger_account_url,
 | 
					      webfinger_account_url,
 | 
				
			||||||
      [%w(rel lrdd), %w(type application/xrd+xml)],
 | 
					      [%w(rel lrdd), %w(type application/jrd+json)],
 | 
				
			||||||
    ]
 | 
					 | 
				
			||||||
  end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  def atom_account_url_link
 | 
					 | 
				
			||||||
    [
 | 
					 | 
				
			||||||
      account_url(@account, format: 'atom'),
 | 
					 | 
				
			||||||
      [%w(rel alternate), %w(type application/atom+xml)],
 | 
					 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -63,15 +46,4 @@ module AccountControllerConcern
 | 
				
			|||||||
  def webfinger_account_url
 | 
					  def webfinger_account_url
 | 
				
			||||||
    webfinger_url(resource: @account.to_webfinger_s)
 | 
					    webfinger_url(resource: @account.to_webfinger_s)
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					 | 
				
			||||||
  def check_account_approval
 | 
					 | 
				
			||||||
    not_found if @account.user_pending?
 | 
					 | 
				
			||||||
  end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  def check_account_suspension
 | 
					 | 
				
			||||||
    if @account.suspended?
 | 
					 | 
				
			||||||
      expires_in(3.minutes, public: true)
 | 
					 | 
				
			||||||
      gone
 | 
					 | 
				
			||||||
    end
 | 
					 | 
				
			||||||
  end
 | 
					 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										33
									
								
								app/controllers/concerns/account_owned_concern.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								app/controllers/concerns/account_owned_concern.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
				
			|||||||
 | 
					# frozen_string_literal: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module AccountOwnedConcern
 | 
				
			||||||
 | 
					  extend ActiveSupport::Concern
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  included do
 | 
				
			||||||
 | 
					    before_action :set_account, if: :account_required?
 | 
				
			||||||
 | 
					    before_action :check_account_approval, if: :account_required?
 | 
				
			||||||
 | 
					    before_action :check_account_suspension, if: :account_required?
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def account_required?
 | 
				
			||||||
 | 
					    true
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def set_account
 | 
				
			||||||
 | 
					    @account = Account.find_local!(username_param)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def username_param
 | 
				
			||||||
 | 
					    params[:account_username]
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def check_account_approval
 | 
				
			||||||
 | 
					    not_found if @account.local? && @account.user_pending?
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def check_account_suspension
 | 
				
			||||||
 | 
					    expires_in(3.minutes, public: true) && gone if @account.suspended?
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
							
								
								
									
										87
									
								
								app/controllers/concerns/status_controller_concern.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								app/controllers/concerns/status_controller_concern.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,87 @@
 | 
				
			|||||||
 | 
					# frozen_string_literal: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module StatusControllerConcern
 | 
				
			||||||
 | 
					  extend ActiveSupport::Concern
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ANCESTORS_LIMIT         = 40
 | 
				
			||||||
 | 
					  DESCENDANTS_LIMIT       = 60
 | 
				
			||||||
 | 
					  DESCENDANTS_DEPTH_LIMIT = 20
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def create_descendant_thread(starting_depth, statuses)
 | 
				
			||||||
 | 
					    depth = starting_depth + statuses.size
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if depth < DESCENDANTS_DEPTH_LIMIT
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        statuses: statuses,
 | 
				
			||||||
 | 
					        starting_depth: starting_depth,
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					      next_status = statuses.pop
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        statuses: statuses,
 | 
				
			||||||
 | 
					        starting_depth: starting_depth,
 | 
				
			||||||
 | 
					        next_status: next_status,
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def set_ancestors
 | 
				
			||||||
 | 
					    @ancestors     = @status.reply? ? cache_collection(@status.ancestors(ANCESTORS_LIMIT, current_account), Status) : []
 | 
				
			||||||
 | 
					    @next_ancestor = @ancestors.size < ANCESTORS_LIMIT ? nil : @ancestors.shift
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def set_descendants
 | 
				
			||||||
 | 
					    @max_descendant_thread_id   = params[:max_descendant_thread_id]&.to_i
 | 
				
			||||||
 | 
					    @since_descendant_thread_id = params[:since_descendant_thread_id]&.to_i
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    descendants = cache_collection(
 | 
				
			||||||
 | 
					      @status.descendants(
 | 
				
			||||||
 | 
					        DESCENDANTS_LIMIT,
 | 
				
			||||||
 | 
					        current_account,
 | 
				
			||||||
 | 
					        @max_descendant_thread_id,
 | 
				
			||||||
 | 
					        @since_descendant_thread_id,
 | 
				
			||||||
 | 
					        DESCENDANTS_DEPTH_LIMIT
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					      Status
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @descendant_threads = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if descendants.present?
 | 
				
			||||||
 | 
					      statuses       = [descendants.first]
 | 
				
			||||||
 | 
					      starting_depth = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      descendants.drop(1).each_with_index do |descendant, index|
 | 
				
			||||||
 | 
					        if descendants[index].id == descendant.in_reply_to_id
 | 
				
			||||||
 | 
					          statuses << descendant
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					          @descendant_threads << create_descendant_thread(starting_depth, statuses)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          # The thread is broken, assume it's a reply to the root status
 | 
				
			||||||
 | 
					          starting_depth = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          # ... unless we can find its ancestor in one of the already-processed threads
 | 
				
			||||||
 | 
					          @descendant_threads.reverse_each do |descendant_thread|
 | 
				
			||||||
 | 
					            statuses = descendant_thread[:statuses]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            index = statuses.find_index do |thread_status|
 | 
				
			||||||
 | 
					              thread_status.id == descendant.in_reply_to_id
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if index.present?
 | 
				
			||||||
 | 
					              starting_depth = descendant_thread[:starting_depth] + index + 1
 | 
				
			||||||
 | 
					              break
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					          end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          statuses = [descendant]
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      @descendant_threads << create_descendant_thread(starting_depth, statuses)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @max_descendant_thread_id = @descendant_threads.pop[:statuses].first.id if descendants.size >= DESCENDANTS_LIMIT
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
@@ -6,6 +6,7 @@ class CustomCssController < ApplicationController
 | 
				
			|||||||
  before_action :set_cache_headers
 | 
					  before_action :set_cache_headers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def show
 | 
					  def show
 | 
				
			||||||
 | 
					    expires 3.minutes, public: true
 | 
				
			||||||
    render plain: Setting.custom_css || '', content_type: 'text/css'
 | 
					    render plain: Setting.custom_css || '', content_type: 'text/css'
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,9 +7,8 @@ class EmojisController < ApplicationController
 | 
				
			|||||||
  def show
 | 
					  def show
 | 
				
			||||||
    respond_to do |format|
 | 
					    respond_to do |format|
 | 
				
			||||||
      format.json do
 | 
					      format.json do
 | 
				
			||||||
        render_cached_json(['activitypub', 'emoji', @emoji], content_type: 'application/activity+json') do
 | 
					        expires_in 3.minutes, public: true
 | 
				
			||||||
          ActiveModelSerializers::SerializableResource.new(@emoji, serializer: ActivityPub::EmojiSerializer, adapter: ActivityPub::Adapter)
 | 
					        render json: @emoji, content_type: 'application/activity+json', serializer: ActivityPub::EmojiSerializer, adapter: ActivityPub::Adapter
 | 
				
			||||||
        end
 | 
					 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,7 +8,7 @@ class FollowerAccountsController < ApplicationController
 | 
				
			|||||||
  def index
 | 
					  def index
 | 
				
			||||||
    respond_to do |format|
 | 
					    respond_to do |format|
 | 
				
			||||||
      format.html do
 | 
					      format.html do
 | 
				
			||||||
        mark_cacheable! unless user_signed_in?
 | 
					        expires_in 0, public: true unless user_signed_in?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        next if @account.user_hides_network?
 | 
					        next if @account.user_hides_network?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,7 +8,7 @@ class FollowingAccountsController < ApplicationController
 | 
				
			|||||||
  def index
 | 
					  def index
 | 
				
			||||||
    respond_to do |format|
 | 
					    respond_to do |format|
 | 
				
			||||||
      format.html do
 | 
					      format.html do
 | 
				
			||||||
        mark_cacheable! unless user_signed_in?
 | 
					        expires_in 0, public: true unless user_signed_in?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        next if @account.user_hides_network?
 | 
					        next if @account.user_hides_network?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,7 +21,7 @@ class HomeController < ApplicationController
 | 
				
			|||||||
      when 'statuses'
 | 
					      when 'statuses'
 | 
				
			||||||
        status = Status.find_by(id: matches[2])
 | 
					        status = Status.find_by(id: matches[2])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if status && (status.public_visibility? || status.unlisted_visibility?)
 | 
					        if status&.distributable?
 | 
				
			||||||
          redirect_to(ActivityPub::TagManager.instance.url_for(status))
 | 
					          redirect_to(ActivityPub::TagManager.instance.url_for(status))
 | 
				
			||||||
          return
 | 
					          return
 | 
				
			||||||
        end
 | 
					        end
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class IntentsController < ApplicationController
 | 
					class IntentsController < ApplicationController
 | 
				
			||||||
  before_action :check_uri
 | 
					  before_action :check_uri
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  rescue_from Addressable::URI::InvalidURIError, with: :handle_invalid_uri
 | 
					  rescue_from Addressable::URI::InvalidURIError, with: :handle_invalid_uri
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def show
 | 
					  def show
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,7 @@ class ManifestsController < ApplicationController
 | 
				
			|||||||
  skip_before_action :store_current_location
 | 
					  skip_before_action :store_current_location
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def show
 | 
					  def show
 | 
				
			||||||
 | 
					    expires_in 3.minutes, public: true
 | 
				
			||||||
    render json: InstancePresenter.new, serializer: ManifestSerializer
 | 
					    render json: InstancePresenter.new, serializer: ManifestSerializer
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,7 +31,6 @@ class MediaController < ApplicationController
 | 
				
			|||||||
  def verify_permitted_status!
 | 
					  def verify_permitted_status!
 | 
				
			||||||
    authorize @media_attachment.status, :show?
 | 
					    authorize @media_attachment.status, :show?
 | 
				
			||||||
  rescue Mastodon::NotPermittedError
 | 
					  rescue Mastodon::NotPermittedError
 | 
				
			||||||
    # Reraise in order to get a 404 instead of a 403 error code
 | 
					 | 
				
			||||||
    raise ActiveRecord::RecordNotFound
 | 
					    raise ActiveRecord::RecordNotFound
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,20 +8,16 @@ class PublicTimelinesController < ApplicationController
 | 
				
			|||||||
  before_action :set_instance_presenter
 | 
					  before_action :set_instance_presenter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def show
 | 
					  def show
 | 
				
			||||||
    respond_to do |format|
 | 
					    @initial_state_json = ActiveModelSerializers::SerializableResource.new(
 | 
				
			||||||
      format.html do
 | 
					      InitialStatePresenter.new(settings: { known_fediverse: Setting.show_known_fediverse_at_about_page }, token: current_session&.token),
 | 
				
			||||||
        @initial_state_json = ActiveModelSerializers::SerializableResource.new(
 | 
					      serializer: InitialStateSerializer
 | 
				
			||||||
          InitialStatePresenter.new(settings: { known_fediverse: Setting.show_known_fediverse_at_about_page }, token: current_session&.token),
 | 
					    ).to_json
 | 
				
			||||||
          serializer: InitialStateSerializer
 | 
					 | 
				
			||||||
        ).to_json
 | 
					 | 
				
			||||||
      end
 | 
					 | 
				
			||||||
    end
 | 
					 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private
 | 
					  private
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def check_enabled
 | 
					  def check_enabled
 | 
				
			||||||
    raise ActiveRecord::RecordNotFound unless Setting.timeline_preview
 | 
					    not_found unless Setting.timeline_preview
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def set_body_classes
 | 
					  def set_body_classes
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,10 +1,10 @@
 | 
				
			|||||||
# frozen_string_literal: true
 | 
					# frozen_string_literal: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class RemoteFollowController < ApplicationController
 | 
					class RemoteFollowController < ApplicationController
 | 
				
			||||||
 | 
					  include AccountOwnedConcern
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  layout 'modal'
 | 
					  layout 'modal'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  before_action :set_account
 | 
					 | 
				
			||||||
  before_action :gone, if: :suspended_account?
 | 
					 | 
				
			||||||
  before_action :set_body_classes
 | 
					  before_action :set_body_classes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def new
 | 
					  def new
 | 
				
			||||||
@@ -32,14 +32,6 @@ class RemoteFollowController < ApplicationController
 | 
				
			|||||||
    { acct: session[:remote_follow] }
 | 
					    { acct: session[:remote_follow] }
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def set_account
 | 
					 | 
				
			||||||
    @account = Account.find_local!(params[:account_username])
 | 
					 | 
				
			||||||
  end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  def suspended_account?
 | 
					 | 
				
			||||||
    @account.suspended?
 | 
					 | 
				
			||||||
  end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  def set_body_classes
 | 
					  def set_body_classes
 | 
				
			||||||
    @body_classes = 'modal-layout'
 | 
					    @body_classes = 'modal-layout'
 | 
				
			||||||
    @hide_header  = true
 | 
					    @hide_header  = true
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,24 +1,21 @@
 | 
				
			|||||||
# frozen_string_literal: true
 | 
					# frozen_string_literal: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class StatusesController < ApplicationController
 | 
					class StatusesController < ApplicationController
 | 
				
			||||||
 | 
					  include StatusControllerConcern
 | 
				
			||||||
  include SignatureAuthentication
 | 
					  include SignatureAuthentication
 | 
				
			||||||
  include Authorization
 | 
					  include Authorization
 | 
				
			||||||
 | 
					  include AccountOwnedConcern
 | 
				
			||||||
  ANCESTORS_LIMIT         = 40
 | 
					 | 
				
			||||||
  DESCENDANTS_LIMIT       = 60
 | 
					 | 
				
			||||||
  DESCENDANTS_DEPTH_LIMIT = 20
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  layout 'public'
 | 
					  layout 'public'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  before_action :set_account
 | 
					 | 
				
			||||||
  before_action :set_status
 | 
					  before_action :set_status
 | 
				
			||||||
  before_action :set_instance_presenter
 | 
					  before_action :set_instance_presenter
 | 
				
			||||||
  before_action :set_link_headers
 | 
					  before_action :set_link_headers
 | 
				
			||||||
  before_action :check_account_suspension
 | 
					 | 
				
			||||||
  before_action :redirect_to_original, only: [:show]
 | 
					  before_action :redirect_to_original, only: [:show]
 | 
				
			||||||
  before_action :set_referrer_policy_header, only: [:show]
 | 
					  before_action :set_referrer_policy_header, only: [:show]
 | 
				
			||||||
  before_action :set_cache_headers
 | 
					  before_action :set_cache_headers
 | 
				
			||||||
  before_action :set_replies, only: [:replies]
 | 
					  before_action :set_body_classes
 | 
				
			||||||
 | 
					  before_action :set_autoplay, only: :embed
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  content_security_policy only: :embed do |p|
 | 
					  content_security_policy only: :embed do |p|
 | 
				
			||||||
    p.frame_ancestors(false)
 | 
					    p.frame_ancestors(false)
 | 
				
			||||||
@@ -28,25 +25,20 @@ class StatusesController < ApplicationController
 | 
				
			|||||||
    respond_to do |format|
 | 
					    respond_to do |format|
 | 
				
			||||||
      format.html do
 | 
					      format.html do
 | 
				
			||||||
        expires_in 10.seconds, public: true if current_account.nil?
 | 
					        expires_in 10.seconds, public: true if current_account.nil?
 | 
				
			||||||
 | 
					 | 
				
			||||||
        @body_classes = 'with-modals'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        set_ancestors
 | 
					        set_ancestors
 | 
				
			||||||
        set_descendants
 | 
					        set_descendants
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      format.json do
 | 
					      format.json do
 | 
				
			||||||
        render_cached_json(['activitypub', 'note', @status], content_type: 'application/activity+json', public: @status.distributable?) do
 | 
					        expires_in 3.minutes, public: @status.distributable?
 | 
				
			||||||
          ActiveModelSerializers::SerializableResource.new(@status, serializer: ActivityPub::NoteSerializer, adapter: ActivityPub::Adapter)
 | 
					        render json: @status, content_type: 'application/activity+json', serializer: ActivityPub::NoteSerializer, adapter: ActivityPub::Adapter
 | 
				
			||||||
        end
 | 
					 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def activity
 | 
					  def activity
 | 
				
			||||||
    render_cached_json(['activitypub', 'activity', @status], content_type: 'application/activity+json', public: @status.distributable?) do
 | 
					    expires_in 3.minutes, public: @status.distributable?
 | 
				
			||||||
      ActiveModelSerializers::SerializableResource.new(@status, serializer: ActivityPub::ActivitySerializer, adapter: ActivityPub::Adapter)
 | 
					    render json: @status, content_type: 'application/activity+json', serializer: ActivityPub::ActivitySerializer, adapter: ActivityPub::Adapter
 | 
				
			||||||
    end
 | 
					 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def embed
 | 
					  def embed
 | 
				
			||||||
@@ -54,120 +46,14 @@ class StatusesController < ApplicationController
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    expires_in 180, public: true
 | 
					    expires_in 180, public: true
 | 
				
			||||||
    response.headers['X-Frame-Options'] = 'ALLOWALL'
 | 
					    response.headers['X-Frame-Options'] = 'ALLOWALL'
 | 
				
			||||||
    @autoplay = ActiveModel::Type::Boolean.new.cast(params[:autoplay])
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    render layout: 'embedded'
 | 
					    render layout: 'embedded'
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def replies
 | 
					 | 
				
			||||||
    render json: replies_collection_presenter,
 | 
					 | 
				
			||||||
           serializer: ActivityPub::CollectionSerializer,
 | 
					 | 
				
			||||||
           adapter: ActivityPub::Adapter,
 | 
					 | 
				
			||||||
           content_type: 'application/activity+json',
 | 
					 | 
				
			||||||
           skip_activities: true
 | 
					 | 
				
			||||||
  end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  private
 | 
					  private
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def replies_collection_presenter
 | 
					  def set_body_classes
 | 
				
			||||||
    page = ActivityPub::CollectionPresenter.new(
 | 
					    @body_classes = 'with-modals'
 | 
				
			||||||
      id: replies_account_status_url(@account, @status, page_params),
 | 
					 | 
				
			||||||
      type: :unordered,
 | 
					 | 
				
			||||||
      part_of: replies_account_status_url(@account, @status),
 | 
					 | 
				
			||||||
      next: next_page,
 | 
					 | 
				
			||||||
      items: @replies.map { |status| status.local ? status : status.id }
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    if page_requested?
 | 
					 | 
				
			||||||
      page
 | 
					 | 
				
			||||||
    else
 | 
					 | 
				
			||||||
      ActivityPub::CollectionPresenter.new(
 | 
					 | 
				
			||||||
        id: replies_account_status_url(@account, @status),
 | 
					 | 
				
			||||||
        type: :unordered,
 | 
					 | 
				
			||||||
        first: page
 | 
					 | 
				
			||||||
      )
 | 
					 | 
				
			||||||
    end
 | 
					 | 
				
			||||||
  end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  def create_descendant_thread(starting_depth, statuses)
 | 
					 | 
				
			||||||
    depth = starting_depth + statuses.size
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if depth < DESCENDANTS_DEPTH_LIMIT
 | 
					 | 
				
			||||||
      {
 | 
					 | 
				
			||||||
        statuses: statuses,
 | 
					 | 
				
			||||||
        starting_depth: starting_depth,
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    else
 | 
					 | 
				
			||||||
      next_status = statuses.pop
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      {
 | 
					 | 
				
			||||||
        statuses: statuses,
 | 
					 | 
				
			||||||
        starting_depth: starting_depth,
 | 
					 | 
				
			||||||
        next_status: next_status,
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    end
 | 
					 | 
				
			||||||
  end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  def set_account
 | 
					 | 
				
			||||||
    @account = Account.find_local!(params[:account_username])
 | 
					 | 
				
			||||||
  end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  def set_ancestors
 | 
					 | 
				
			||||||
    @ancestors     = @status.reply? ? cache_collection(@status.ancestors(ANCESTORS_LIMIT, current_account), Status) : []
 | 
					 | 
				
			||||||
    @next_ancestor = @ancestors.size < ANCESTORS_LIMIT ? nil : @ancestors.shift
 | 
					 | 
				
			||||||
  end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  def set_descendants
 | 
					 | 
				
			||||||
    @max_descendant_thread_id   = params[:max_descendant_thread_id]&.to_i
 | 
					 | 
				
			||||||
    @since_descendant_thread_id = params[:since_descendant_thread_id]&.to_i
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    descendants = cache_collection(
 | 
					 | 
				
			||||||
      @status.descendants(
 | 
					 | 
				
			||||||
        DESCENDANTS_LIMIT,
 | 
					 | 
				
			||||||
        current_account,
 | 
					 | 
				
			||||||
        @max_descendant_thread_id,
 | 
					 | 
				
			||||||
        @since_descendant_thread_id,
 | 
					 | 
				
			||||||
        DESCENDANTS_DEPTH_LIMIT
 | 
					 | 
				
			||||||
      ),
 | 
					 | 
				
			||||||
      Status
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @descendant_threads = []
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if descendants.present?
 | 
					 | 
				
			||||||
      statuses       = [descendants.first]
 | 
					 | 
				
			||||||
      starting_depth = 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      descendants.drop(1).each_with_index do |descendant, index|
 | 
					 | 
				
			||||||
        if descendants[index].id == descendant.in_reply_to_id
 | 
					 | 
				
			||||||
          statuses << descendant
 | 
					 | 
				
			||||||
        else
 | 
					 | 
				
			||||||
          @descendant_threads << create_descendant_thread(starting_depth, statuses)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          # The thread is broken, assume it's a reply to the root status
 | 
					 | 
				
			||||||
          starting_depth = 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          # ... unless we can find its ancestor in one of the already-processed threads
 | 
					 | 
				
			||||||
          @descendant_threads.reverse_each do |descendant_thread|
 | 
					 | 
				
			||||||
            statuses = descendant_thread[:statuses]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            index = statuses.find_index do |thread_status|
 | 
					 | 
				
			||||||
              thread_status.id == descendant.in_reply_to_id
 | 
					 | 
				
			||||||
            end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if index.present?
 | 
					 | 
				
			||||||
              starting_depth = descendant_thread[:starting_depth] + index + 1
 | 
					 | 
				
			||||||
              break
 | 
					 | 
				
			||||||
            end
 | 
					 | 
				
			||||||
          end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          statuses = [descendant]
 | 
					 | 
				
			||||||
        end
 | 
					 | 
				
			||||||
      end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      @descendant_threads << create_descendant_thread(starting_depth, statuses)
 | 
					 | 
				
			||||||
    end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @max_descendant_thread_id = @descendant_threads.pop[:statuses].first.id if descendants.size >= DESCENDANTS_LIMIT
 | 
					 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def set_link_headers
 | 
					  def set_link_headers
 | 
				
			||||||
@@ -185,39 +71,15 @@ class StatusesController < ApplicationController
 | 
				
			|||||||
    @instance_presenter = InstancePresenter.new
 | 
					    @instance_presenter = InstancePresenter.new
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def check_account_suspension
 | 
					 | 
				
			||||||
    gone if @account.suspended?
 | 
					 | 
				
			||||||
  end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  def redirect_to_original
 | 
					  def redirect_to_original
 | 
				
			||||||
    redirect_to ActivityPub::TagManager.instance.url_for(@status.reblog) if @status.reblog?
 | 
					    redirect_to ActivityPub::TagManager.instance.url_for(@status.reblog) if @status.reblog?
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def set_referrer_policy_header
 | 
					  def set_referrer_policy_header
 | 
				
			||||||
    return if @status.public_visibility? || @status.unlisted_visibility?
 | 
					    response.headers['Referrer-Policy'] = 'origin' unless @status.distributable?
 | 
				
			||||||
    response.headers['Referrer-Policy'] = 'origin'
 | 
					 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def page_requested?
 | 
					  def set_autoplay
 | 
				
			||||||
    params[:page] == 'true'
 | 
					    @autoplay = truthy_param?(:autoplay)
 | 
				
			||||||
  end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  def set_replies
 | 
					 | 
				
			||||||
    @replies = page_params[:other_accounts] ? Status.where.not(account_id: @account.id) : @account.statuses
 | 
					 | 
				
			||||||
    @replies = @replies.where(in_reply_to_id: @status.id, visibility: [:public, :unlisted])
 | 
					 | 
				
			||||||
    @replies = @replies.paginate_by_min_id(DESCENDANTS_LIMIT, params[:min_id])
 | 
					 | 
				
			||||||
  end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  def next_page
 | 
					 | 
				
			||||||
    last_reply = @replies.last
 | 
					 | 
				
			||||||
    return if last_reply.nil?
 | 
					 | 
				
			||||||
    same_account = last_reply.account_id == @account.id
 | 
					 | 
				
			||||||
    return unless same_account || @replies.size == DESCENDANTS_LIMIT
 | 
					 | 
				
			||||||
    same_account = false unless @replies.size == DESCENDANTS_LIMIT
 | 
					 | 
				
			||||||
    replies_account_status_url(@account, @status, page: true, min_id: last_reply.id, other_accounts: !same_account)
 | 
					 | 
				
			||||||
  end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  def page_params
 | 
					 | 
				
			||||||
    { page: true, other_accounts: params[:other_accounts], min_id: params[:min_id] }.compact
 | 
					 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,14 +5,15 @@ class TagsController < ApplicationController
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  layout 'public'
 | 
					  layout 'public'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  before_action :set_tag
 | 
				
			||||||
  before_action :set_body_classes
 | 
					  before_action :set_body_classes
 | 
				
			||||||
  before_action :set_instance_presenter
 | 
					  before_action :set_instance_presenter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def show
 | 
					  def show
 | 
				
			||||||
    @tag = Tag.find_normalized!(params[:id])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    respond_to do |format|
 | 
					    respond_to do |format|
 | 
				
			||||||
      format.html do
 | 
					      format.html do
 | 
				
			||||||
 | 
					        expires_in 0, public: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        @initial_state_json = ActiveModelSerializers::SerializableResource.new(
 | 
					        @initial_state_json = ActiveModelSerializers::SerializableResource.new(
 | 
				
			||||||
          InitialStatePresenter.new(settings: {}, token: current_session&.token),
 | 
					          InitialStatePresenter.new(settings: {}, token: current_session&.token),
 | 
				
			||||||
          serializer: InitialStateSerializer
 | 
					          serializer: InitialStateSerializer
 | 
				
			||||||
@@ -20,6 +21,8 @@ class TagsController < ApplicationController
 | 
				
			|||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      format.rss do
 | 
					      format.rss do
 | 
				
			||||||
 | 
					        expires_in 0, public: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        @statuses = HashtagQueryService.new.call(@tag, params.slice(:any, :all, :none)).limit(PAGE_SIZE)
 | 
					        @statuses = HashtagQueryService.new.call(@tag, params.slice(:any, :all, :none)).limit(PAGE_SIZE)
 | 
				
			||||||
        @statuses = cache_collection(@statuses, Status)
 | 
					        @statuses = cache_collection(@statuses, Status)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -27,19 +30,22 @@ class TagsController < ApplicationController
 | 
				
			|||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      format.json do
 | 
					      format.json do
 | 
				
			||||||
 | 
					        expires_in 3.minutes, public: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        @statuses = HashtagQueryService.new.call(@tag, params.slice(:any, :all, :none), current_account, params[:local]).paginate_by_max_id(PAGE_SIZE, params[:max_id])
 | 
					        @statuses = HashtagQueryService.new.call(@tag, params.slice(:any, :all, :none), current_account, params[:local]).paginate_by_max_id(PAGE_SIZE, params[:max_id])
 | 
				
			||||||
        @statuses = cache_collection(@statuses, Status)
 | 
					        @statuses = cache_collection(@statuses, Status)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        render json: collection_presenter,
 | 
					        render json: collection_presenter, serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json'
 | 
				
			||||||
               serializer: ActivityPub::CollectionSerializer,
 | 
					 | 
				
			||||||
               adapter: ActivityPub::Adapter,
 | 
					 | 
				
			||||||
               content_type: 'application/activity+json'
 | 
					 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private
 | 
					  private
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def set_tag
 | 
				
			||||||
 | 
					    @tag = Tag.find_normalized!(params[:id])
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def set_body_classes
 | 
					  def set_body_classes
 | 
				
			||||||
    @body_classes = 'with-modals'
 | 
					    @body_classes = 'with-modals'
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,7 +13,7 @@ module WellKnown
 | 
				
			|||||||
        format.xml { render content_type: 'application/xrd+xml' }
 | 
					        format.xml { render content_type: 'application/xrd+xml' }
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      expires_in(3.days, public: true)
 | 
					      expires_in 3.days, public: true
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,7 +19,7 @@ module WellKnown
 | 
				
			|||||||
        end
 | 
					        end
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      expires_in(3.days, public: true)
 | 
					      expires_in 3.days, public: true
 | 
				
			||||||
    rescue ActiveRecord::RecordNotFound
 | 
					    rescue ActiveRecord::RecordNotFound
 | 
				
			||||||
      head 404
 | 
					      head 404
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
@@ -27,12 +27,9 @@ module WellKnown
 | 
				
			|||||||
    private
 | 
					    private
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def username_from_resource
 | 
					    def username_from_resource
 | 
				
			||||||
      resource_user = resource_param
 | 
					      resource_user    = resource_param
 | 
				
			||||||
 | 
					 | 
				
			||||||
      username, domain = resource_user.split('@')
 | 
					      username, domain = resource_user.split('@')
 | 
				
			||||||
      if Rails.configuration.x.alternate_domains.include?(domain)
 | 
					      resource_user    = "#{username}@#{Rails.configuration.x.local_domain}" if Rails.configuration.x.alternate_domains.include?(domain)
 | 
				
			||||||
        resource_user = "#{username}@#{Rails.configuration.x.local_domain}"
 | 
					 | 
				
			||||||
      end
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      WebfingerResource.new(resource_user).username
 | 
					      WebfingerResource.new(resource_user).username
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -40,7 +40,7 @@ class ActivityPub::Activity::Announce < ActivityPub::Activity
 | 
				
			|||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def announceable?(status)
 | 
					  def announceable?(status)
 | 
				
			||||||
    status.account_id == @account.id || status.public_visibility? || status.unlisted_visibility?
 | 
					    status.account_id == @account.id || status.distributable?
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def related_to_local_activity?
 | 
					  def related_to_local_activity?
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -42,7 +42,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
 | 
				
			|||||||
    resolve_thread(@status)
 | 
					    resolve_thread(@status)
 | 
				
			||||||
    fetch_replies(@status)
 | 
					    fetch_replies(@status)
 | 
				
			||||||
    distribute(@status)
 | 
					    distribute(@status)
 | 
				
			||||||
    forward_for_reply if @status.public_visibility? || @status.unlisted_visibility?
 | 
					    forward_for_reply if @status.distributable?
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def find_existing_status
 | 
					  def find_existing_status
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,7 +31,7 @@ class ActivityPub::Activity::Delete < ActivityPub::Activity
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    return if @status.nil?
 | 
					    return if @status.nil?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if @status.public_visibility? || @status.unlisted_visibility?
 | 
					    if @status.distributable?
 | 
				
			||||||
      forward_for_reply
 | 
					      forward_for_reply
 | 
				
			||||||
      forward_for_reblogs
 | 
					      forward_for_reblogs
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -51,7 +51,7 @@ class ActivityPub::TagManager
 | 
				
			|||||||
  def replies_uri_for(target, page_params = nil)
 | 
					  def replies_uri_for(target, page_params = nil)
 | 
				
			||||||
    raise ArgumentError, 'target must be a local activity' unless %i(note comment activity).include?(target.object_type) && target.local?
 | 
					    raise ArgumentError, 'target must be a local activity' unless %i(note comment activity).include?(target.object_type) && target.local?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    replies_account_status_url(target.account, target, page_params)
 | 
					    account_status_replies_url(target.account, target, page_params)
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  # Primary audience of a status
 | 
					  # Primary audience of a status
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -193,7 +193,7 @@ class Status < ApplicationRecord
 | 
				
			|||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def hidden?
 | 
					  def hidden?
 | 
				
			||||||
    private_visibility? || direct_visibility? || limited_visibility?
 | 
					    !distributable?
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def distributable?
 | 
					  def distributable?
 | 
				
			||||||
@@ -446,7 +446,8 @@ class Status < ApplicationRecord
 | 
				
			|||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def update_statistics
 | 
					  def update_statistics
 | 
				
			||||||
    return unless public_visibility? || unlisted_visibility?
 | 
					    return unless distributable?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ActivityTracker.increment('activity:statuses:local')
 | 
					    ActivityTracker.increment('activity:statuses:local')
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -455,7 +456,7 @@ class Status < ApplicationRecord
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    account&.increment_count!(:statuses_count)
 | 
					    account&.increment_count!(:statuses_count)
 | 
				
			||||||
    reblog&.increment_count!(:reblogs_count) if reblog?
 | 
					    reblog&.increment_count!(:reblogs_count) if reblog?
 | 
				
			||||||
    thread&.increment_count!(:replies_count) if in_reply_to_id.present? && (public_visibility? || unlisted_visibility?)
 | 
					    thread&.increment_count!(:replies_count) if in_reply_to_id.present? && distributable?
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def decrement_counter_caches
 | 
					  def decrement_counter_caches
 | 
				
			||||||
@@ -463,7 +464,7 @@ class Status < ApplicationRecord
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    account&.decrement_count!(:statuses_count)
 | 
					    account&.decrement_count!(:statuses_count)
 | 
				
			||||||
    reblog&.decrement_count!(:reblogs_count) if reblog?
 | 
					    reblog&.decrement_count!(:reblogs_count) if reblog?
 | 
				
			||||||
    thread&.decrement_count!(:replies_count) if in_reply_to_id.present? && (public_visibility? || unlisted_visibility?)
 | 
					    thread&.decrement_count!(:replies_count) if in_reply_to_id.present? && distributable?
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def unlink_from_conversations
 | 
					  def unlink_from_conversations
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,12 @@
 | 
				
			|||||||
# frozen_string_literal: true
 | 
					# frozen_string_literal: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ActivityPub::ActivitySerializer < ActivityPub::Serializer
 | 
					class ActivityPub::ActivitySerializer < ActivityPub::Serializer
 | 
				
			||||||
 | 
					  cache key: 'activity', expires_in: 3.minutes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  attributes :id, :type, :actor, :published, :to, :cc
 | 
					  attributes :id, :type, :actor, :published, :to, :cc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  has_one :proper, key: :object, serializer: ActivityPub::NoteSerializer, if: :serialize_object?
 | 
					  has_one :proper, key: :object, serializer: ActivityPub::NoteSerializer, if: :serialize_object?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  attribute :proper_uri, key: :object, unless: :serialize_object?
 | 
					  attribute :proper_uri, key: :object, unless: :serialize_object?
 | 
				
			||||||
  attribute :atom_uri, if: :announce?
 | 
					  attribute :atom_uri, if: :announce?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,8 @@
 | 
				
			|||||||
class ActivityPub::ActorSerializer < ActivityPub::Serializer
 | 
					class ActivityPub::ActorSerializer < ActivityPub::Serializer
 | 
				
			||||||
  include RoutingHelper
 | 
					  include RoutingHelper
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  cache key: 'actor', expires_in: 3.minutes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  context :security
 | 
					  context :security
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  context_extensions :manually_approves_followers, :featured, :also_known_as,
 | 
					  context_extensions :manually_approves_followers, :featured, :also_known_as,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,6 +7,8 @@ class ActivityPub::CollectionSerializer < ActivityPub::Serializer
 | 
				
			|||||||
    super
 | 
					    super
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  cache key: 'collection', expires_in: 3.minutes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  attribute :id, if: -> { object.id.present? }
 | 
					  attribute :id, if: -> { object.id.present? }
 | 
				
			||||||
  attribute :type
 | 
					  attribute :type
 | 
				
			||||||
  attribute :total_items, if: -> { object.size.present? }
 | 
					  attribute :total_items, if: -> { object.size.present? }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,8 @@
 | 
				
			|||||||
class ActivityPub::EmojiSerializer < ActivityPub::Serializer
 | 
					class ActivityPub::EmojiSerializer < ActivityPub::Serializer
 | 
				
			||||||
  include RoutingHelper
 | 
					  include RoutingHelper
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  cache key: 'emoji', expires_in: 3.minutes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  context_extensions :emoji
 | 
					  context_extensions :emoji
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  attributes :id, :type, :name, :updated
 | 
					  attributes :id, :type, :name, :updated
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,8 @@
 | 
				
			|||||||
# frozen_string_literal: true
 | 
					# frozen_string_literal: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ActivityPub::NoteSerializer < ActivityPub::Serializer
 | 
					class ActivityPub::NoteSerializer < ActivityPub::Serializer
 | 
				
			||||||
 | 
					  cache key: 'note', expires_in: 3.minutes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  context_extensions :atom_uri, :conversation, :sensitive,
 | 
					  context_extensions :atom_uri, :conversation, :sensitive,
 | 
				
			||||||
                     :hashtag, :emoji, :focal_point, :blurhash
 | 
					                     :hashtag, :emoji, :focal_point, :blurhash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,7 @@ class ProcessHashtagsService < BaseService
 | 
				
			|||||||
      TrendingTags.record_use!(tag, status.account, status.created_at) if status.public_visibility?
 | 
					      TrendingTags.record_use!(tag, status.account, status.created_at) if status.public_visibility?
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return unless status.public_visibility? || status.unlisted_visibility?
 | 
					    return unless status.distributable?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    status.account.featured_tags.where(tag_id: records.map(&:id)).each do |featured_tag|
 | 
					    status.account.featured_tags.where(tag_id: records.map(&:id)).each do |featured_tag|
 | 
				
			||||||
      featured_tag.increment(status.created_at)
 | 
					      featured_tag.increment(status.created_at)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -50,9 +50,9 @@
 | 
				
			|||||||
          = fa_icon 'reply-all fw'
 | 
					          = fa_icon 'reply-all fw'
 | 
				
			||||||
      .status__action-bar__counter__label= obscured_counter status.replies_count
 | 
					      .status__action-bar__counter__label= obscured_counter status.replies_count
 | 
				
			||||||
    = link_to remote_interaction_path(status, type: :reblog), class: 'status__action-bar-button icon-button modal-button', style: 'font-size: 18px; width: 23.1429px; height: 23.1429px; line-height: 23.15px;' do
 | 
					    = link_to remote_interaction_path(status, type: :reblog), class: 'status__action-bar-button icon-button modal-button', style: 'font-size: 18px; width: 23.1429px; height: 23.1429px; line-height: 23.15px;' do
 | 
				
			||||||
      - if status.public_visibility? || status.unlisted_visibility?
 | 
					      - if status.distributable?
 | 
				
			||||||
        = fa_icon 'retweet fw'
 | 
					        = fa_icon 'retweet fw'
 | 
				
			||||||
      - elsif status.private_visibility?
 | 
					      - elsif status.private_visibility? || status.limited_visibility?
 | 
				
			||||||
        = fa_icon 'lock fw'
 | 
					        = fa_icon 'lock fw'
 | 
				
			||||||
      - else
 | 
					      - else
 | 
				
			||||||
        = fa_icon 'envelope fw'
 | 
					        = fa_icon 'envelope fw'
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -52,8 +52,9 @@ Rails.application.routes.draw do
 | 
				
			|||||||
      member do
 | 
					      member do
 | 
				
			||||||
        get :activity
 | 
					        get :activity
 | 
				
			||||||
        get :embed
 | 
					        get :embed
 | 
				
			||||||
        get :replies
 | 
					 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      resources :replies, only: [:index], module: :activitypub
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    resources :followers, only: [:index], controller: :follower_accounts
 | 
					    resources :followers, only: [:index], controller: :follower_accounts
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -41,7 +41,7 @@ describe ApplicationController, type: :controller do
 | 
				
			|||||||
    it 'sets link headers' do
 | 
					    it 'sets link headers' do
 | 
				
			||||||
      account = Fabricate(:account, username: 'username', user: Fabricate(:user))
 | 
					      account = Fabricate(:account, username: 'username', user: Fabricate(:user))
 | 
				
			||||||
      get 'success', params: { account_username: 'username' }
 | 
					      get 'success', params: { account_username: 'username' }
 | 
				
			||||||
      expect(response.headers['Link'].to_s).to eq '<http://test.host/.well-known/webfinger?resource=acct%3Ausername%40cb6e6126.ngrok.io>; rel="lrdd"; type="application/xrd+xml", <http://test.host/users/username.atom>; rel="alternate"; type="application/atom+xml", <https://cb6e6126.ngrok.io/users/username>; rel="alternate"; type="application/activity+json"'
 | 
					      expect(response.headers['Link'].to_s).to eq '<http://test.host/.well-known/webfinger?resource=acct%3Ausername%40cb6e6126.ngrok.io>; rel="lrdd"; type="application/jrd+json", <https://cb6e6126.ngrok.io/users/username>; rel="alternate"; type="application/activity+json"'
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it 'returns http success' do
 | 
					    it 'returns http success' do
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -92,7 +92,7 @@ describe StatusesController do
 | 
				
			|||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      it 'assigns @max_descendant_thread_id for the last thread if it is hitting the status limit' do
 | 
					      it 'assigns @max_descendant_thread_id for the last thread if it is hitting the status limit' do
 | 
				
			||||||
        stub_const 'StatusesController::DESCENDANTS_LIMIT', 1
 | 
					        stub_const 'StatusControllerConcern::DESCENDANTS_LIMIT', 1
 | 
				
			||||||
        status = Fabricate(:status)
 | 
					        status = Fabricate(:status)
 | 
				
			||||||
        child = Fabricate(:status, in_reply_to_id: status.id)
 | 
					        child = Fabricate(:status, in_reply_to_id: status.id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -103,7 +103,7 @@ describe StatusesController do
 | 
				
			|||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      it 'assigns @descendant_threads for threads with :next_status key if they are hitting the depth limit' do
 | 
					      it 'assigns @descendant_threads for threads with :next_status key if they are hitting the depth limit' do
 | 
				
			||||||
        stub_const 'StatusesController::DESCENDANTS_DEPTH_LIMIT', 2
 | 
					        stub_const 'StatusControllerConcern::DESCENDANTS_DEPTH_LIMIT', 2
 | 
				
			||||||
        status = Fabricate(:status)
 | 
					        status = Fabricate(:status)
 | 
				
			||||||
        child0 = Fabricate(:status, in_reply_to_id: status.id)
 | 
					        child0 = Fabricate(:status, in_reply_to_id: status.id)
 | 
				
			||||||
        child1 = Fabricate(:status, in_reply_to_id: child0.id)
 | 
					        child1 = Fabricate(:status, in_reply_to_id: child0.id)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,16 +11,16 @@ describe 'Link headers' do
 | 
				
			|||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it 'contains webfinger url in link header' do
 | 
					    it 'contains webfinger url in link header' do
 | 
				
			||||||
      link_header = link_header_with_type('application/xrd+xml')
 | 
					      link_header = link_header_with_type('application/jrd+json')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      expect(link_header.href).to match 'http://www.example.com/.well-known/webfinger?resource=acct%3Atest%40cb6e6126.ngrok.io'
 | 
					      expect(link_header.href).to match 'http://www.example.com/.well-known/webfinger?resource=acct%3Atest%40cb6e6126.ngrok.io'
 | 
				
			||||||
      expect(link_header.attr_pairs.first).to eq %w(rel lrdd)
 | 
					      expect(link_header.attr_pairs.first).to eq %w(rel lrdd)
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it 'contains atom url in link header' do
 | 
					    it 'contains activitypub url in link header' do
 | 
				
			||||||
      link_header = link_header_with_type('application/atom+xml')
 | 
					      link_header = link_header_with_type('application/activity+json')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      expect(link_header.href).to eq 'http://www.example.com/users/test.atom'
 | 
					      expect(link_header.href).to eq 'https://cb6e6126.ngrok.io/users/test'
 | 
				
			||||||
      expect(link_header.attr_pairs.first).to eq %w(rel alternate)
 | 
					      expect(link_header.attr_pairs.first).to eq %w(rel alternate)
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user