forked from treehouse/mastodon
Refactor controllers for statuses, accounts, and more (#11249)
parent
f14776475d
commit
63c7fe8e48
|
@ -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(
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue