class AboutController < ApplicationController
layout 'public'
- before_action :set_instance_presenter, only: [:show, :more, :terms]
+ before_action :set_body_classes, only: :show
+ before_action :set_instance_presenter
+ before_action :set_expires_in
- def show
- @hide_navbar = true
- end
+ def show; end
def more; end
def set_instance_presenter
@instance_presenter = InstancePresenter.new
end
+
+ def set_body_classes
+ @hide_navbar = true
+ end
+
+ def set_expires_in
+ expires_in 0, public: true
+ end
end
include AccountControllerConcern
before_action :set_cache_headers
+ before_action :set_body_classes
def show
respond_to do |format|
format.html do
- mark_cacheable! unless user_signed_in?
+ expires_in 0, public: true unless user_signed_in?
- @body_classes = 'with-modals'
@pinned_statuses = []
@endorsed_accounts = @account.endorsed_accounts.to_a.sample(4)
end
format.rss do
- mark_cacheable!
+ expires_in 0, public: true
@statuses = cache_collection(default_statuses.without_reblogs.without_replies.limit(PAGE_SIZE), Status)
render xml: RSS::AccountSerializer.render(@account, @statuses)
end
format.json do
- render_cached_json(['activitypub', 'actor', @account], content_type: 'application/activity+json') do
- ActiveModelSerializers::SerializableResource.new(@account, serializer: ActivityPub::ActorSerializer, adapter: ActivityPub::Adapter)
- end
+ expires_in 3.minutes, public: true
+ render json: @account, content_type: 'application/activity+json', serializer: ActivityPub::ActorSerializer, adapter: ActivityPub::Adapter
end
end
end
private
+ def set_body_classes
+ @body_classes = 'with-modals'
+ end
+
def show_pinned_statuses?
[replies_requested?, media_requested?, tag_requested?, params[:max_id].present?, params[:min_id].present?].none?
end
class ActivityPub::CollectionsController < Api::BaseController
include SignatureVerification
+ include AccountOwnedConcern
- before_action :set_account
before_action :set_size
before_action :set_statuses
before_action :set_cache_headers
def show
- render_cached_json(['activitypub', 'collection', @account, params[:id]], content_type: 'application/activity+json') do
- ActiveModelSerializers::SerializableResource.new(
- collection_presenter,
- serializer: ActivityPub::CollectionSerializer,
- adapter: ActivityPub::Adapter,
- skip_activities: true
- )
- end
+ expires_in 3.minutes, public: true
+ render json: collection_presenter, content_type: 'application/activity+json', serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter, skip_activities: true
end
private
- def set_account
- @account = Account.find_local!(params[:account_username])
- end
-
def set_statuses
@statuses = scope_for_collection
@statuses = cache_collection(@statuses, Status)
class ActivityPub::InboxesController < Api::BaseController
include SignatureVerification
include JsonLdHelper
-
- before_action :set_account
+ include AccountOwnedConcern
def create
if unknown_deleted_account?
false
end
- def set_account
- @account = Account.find_local!(params[:account_username]) if params[:account_username]
+ def account_required?
+ params[:account_username].present?
end
def body
LIMIT = 20
include SignatureVerification
+ include AccountOwnedConcern
- before_action :set_account
before_action :set_statuses
before_action :set_cache_headers
private
- def set_account
- @account = Account.find_local!(params[:account_username])
- end
-
def outbox_presenter
if page_requested?
ActivityPub::CollectionPresenter.new(
--- /dev/null
+# 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
# frozen_string_literal: true
class Api::ProofsController < Api::BaseController
- before_action :set_account
+ include AccountOwnedConcern
+
before_action :set_provider
- before_action :check_account_approval
- before_action :check_account_suspension
def index
render json: @account, serializer: @provider.serializer_class
@provider = ProofProvider.find(params[:provider]) || raise(ActiveRecord::RecordNotFound)
end
- def set_account
- @account = Account.find_local!(params[:username])
- end
-
- def check_account_approval
- not_found if @account.user_pending?
- end
-
- def check_account_suspension
- gone if @account.suspended?
+ def username_param
+ params[:username]
end
end
def set_cache_headers
response.headers['Vary'] = 'Accept'
end
-
- def mark_cacheable!
- expires_in 0, public: true
- end
end
module AccountControllerConcern
extend ActiveSupport::Concern
+ include AccountOwnedConcern
+
FOLLOW_PER_PAGE = 12
included do
layout 'public'
- before_action :set_account
- before_action :check_account_approval
- before_action :check_account_suspension
before_action :set_instance_presenter
before_action :set_link_headers
end
private
- def set_account
- @account = Account.find_local!(username_param)
- end
-
def set_instance_presenter
@instance_presenter = InstancePresenter.new
end
response.headers['Link'] = LinkHeader.new(
[
webfinger_account_link,
- atom_account_url_link,
actor_url_link,
]
)
end
- def username_param
- params[:account_username]
- end
-
def webfinger_account_link
[
webfinger_account_url,
- [%w(rel lrdd), %w(type application/xrd+xml)],
- ]
- end
-
- def atom_account_url_link
- [
- account_url(@account, format: 'atom'),
- [%w(rel alternate), %w(type application/atom+xml)],
+ [%w(rel lrdd), %w(type application/jrd+json)],
]
end
def webfinger_account_url
webfinger_url(resource: @account.to_webfinger_s)
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
--- /dev/null
+# 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
--- /dev/null
+# 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
before_action :set_cache_headers
def show
+ expires 3.minutes, public: true
render plain: Setting.custom_css || '', content_type: 'text/css'
end
end
def show
respond_to do |format|
format.json do
- render_cached_json(['activitypub', 'emoji', @emoji], content_type: 'application/activity+json') do
- ActiveModelSerializers::SerializableResource.new(@emoji, serializer: ActivityPub::EmojiSerializer, adapter: ActivityPub::Adapter)
- end
+ expires_in 3.minutes, public: true
+ render json: @emoji, content_type: 'application/activity+json', serializer: ActivityPub::EmojiSerializer, adapter: ActivityPub::Adapter
end
end
end
def index
respond_to do |format|
format.html do
- mark_cacheable! unless user_signed_in?
+ expires_in 0, public: true unless user_signed_in?
next if @account.user_hides_network?
def index
respond_to do |format|
format.html do
- mark_cacheable! unless user_signed_in?
+ expires_in 0, public: true unless user_signed_in?
next if @account.user_hides_network?
when 'statuses'
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))
return
end
class IntentsController < ApplicationController
before_action :check_uri
+
rescue_from Addressable::URI::InvalidURIError, with: :handle_invalid_uri
def show
skip_before_action :store_current_location
def show
+ expires_in 3.minutes, public: true
render json: InstancePresenter.new, serializer: ManifestSerializer
end
end
def verify_permitted_status!
authorize @media_attachment.status, :show?
rescue Mastodon::NotPermittedError
- # Reraise in order to get a 404 instead of a 403 error code
raise ActiveRecord::RecordNotFound
end
before_action :set_instance_presenter
def show
- respond_to do |format|
- format.html do
- @initial_state_json = ActiveModelSerializers::SerializableResource.new(
- InitialStatePresenter.new(settings: { known_fediverse: Setting.show_known_fediverse_at_about_page }, token: current_session&.token),
- serializer: InitialStateSerializer
- ).to_json
- end
- end
+ @initial_state_json = ActiveModelSerializers::SerializableResource.new(
+ InitialStatePresenter.new(settings: { known_fediverse: Setting.show_known_fediverse_at_about_page }, token: current_session&.token),
+ serializer: InitialStateSerializer
+ ).to_json
end
private
def check_enabled
- raise ActiveRecord::RecordNotFound unless Setting.timeline_preview
+ not_found unless Setting.timeline_preview
end
def set_body_classes
# frozen_string_literal: true
class RemoteFollowController < ApplicationController
+ include AccountOwnedConcern
+
layout 'modal'
- before_action :set_account
- before_action :gone, if: :suspended_account?
before_action :set_body_classes
def new
{ acct: session[:remote_follow] }
end
- def set_account
- @account = Account.find_local!(params[:account_username])
- end
-
- def suspended_account?
- @account.suspended?
- end
-
def set_body_classes
@body_classes = 'modal-layout'
@hide_header = true
# frozen_string_literal: true
class StatusesController < ApplicationController
+ include StatusControllerConcern
include SignatureAuthentication
include Authorization
-
- ANCESTORS_LIMIT = 40
- DESCENDANTS_LIMIT = 60
- DESCENDANTS_DEPTH_LIMIT = 20
+ include AccountOwnedConcern
layout 'public'
- before_action :set_account
before_action :set_status
before_action :set_instance_presenter
before_action :set_link_headers
- before_action :check_account_suspension
before_action :redirect_to_original, only: [:show]
before_action :set_referrer_policy_header, only: [:show]
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|
p.frame_ancestors(false)
respond_to do |format|
format.html do
expires_in 10.seconds, public: true if current_account.nil?
-
- @body_classes = 'with-modals'
-
set_ancestors
set_descendants
end
format.json do
- render_cached_json(['activitypub', 'note', @status], content_type: 'application/activity+json', public: @status.distributable?) do
- ActiveModelSerializers::SerializableResource.new(@status, serializer: ActivityPub::NoteSerializer, adapter: ActivityPub::Adapter)
- end
+ expires_in 3.minutes, public: @status.distributable?
+ render json: @status, content_type: 'application/activity+json', serializer: ActivityPub::NoteSerializer, adapter: ActivityPub::Adapter
end
end
end
def activity
- render_cached_json(['activitypub', 'activity', @status], content_type: 'application/activity+json', public: @status.distributable?) do
- ActiveModelSerializers::SerializableResource.new(@status, serializer: ActivityPub::ActivitySerializer, adapter: ActivityPub::Adapter)
- end
+ expires_in 3.minutes, public: @status.distributable?
+ render json: @status, content_type: 'application/activity+json', serializer: ActivityPub::ActivitySerializer, adapter: ActivityPub::Adapter
end
def embed
expires_in 180, public: true
response.headers['X-Frame-Options'] = 'ALLOWALL'
- @autoplay = ActiveModel::Type::Boolean.new.cast(params[:autoplay])
render layout: 'embedded'
end
- def replies
- render json: replies_collection_presenter,
- serializer: ActivityPub::CollectionSerializer,
- adapter: ActivityPub::Adapter,
- content_type: 'application/activity+json',
- skip_activities: true
- end
-
private
- def replies_collection_presenter
- page = ActivityPub::CollectionPresenter.new(
- 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
+ def set_body_classes
+ @body_classes = 'with-modals'
end
def set_link_headers
@instance_presenter = InstancePresenter.new
end
- def check_account_suspension
- gone if @account.suspended?
- end
-
def redirect_to_original
redirect_to ActivityPub::TagManager.instance.url_for(@status.reblog) if @status.reblog?
end
def set_referrer_policy_header
- return if @status.public_visibility? || @status.unlisted_visibility?
- response.headers['Referrer-Policy'] = 'origin'
- end
-
- def page_requested?
- params[:page] == 'true'
- 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)
+ response.headers['Referrer-Policy'] = 'origin' unless @status.distributable?
end
- def page_params
- { page: true, other_accounts: params[:other_accounts], min_id: params[:min_id] }.compact
+ def set_autoplay
+ @autoplay = truthy_param?(:autoplay)
end
end
layout 'public'
+ before_action :set_tag
before_action :set_body_classes
before_action :set_instance_presenter
def show
- @tag = Tag.find_normalized!(params[:id])
-
respond_to do |format|
format.html do
+ expires_in 0, public: true
+
@initial_state_json = ActiveModelSerializers::SerializableResource.new(
InitialStatePresenter.new(settings: {}, token: current_session&.token),
serializer: InitialStateSerializer
end
format.rss do
+ expires_in 0, public: true
+
@statuses = HashtagQueryService.new.call(@tag, params.slice(:any, :all, :none)).limit(PAGE_SIZE)
@statuses = cache_collection(@statuses, Status)
end
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 = cache_collection(@statuses, Status)
- render json: collection_presenter,
- serializer: ActivityPub::CollectionSerializer,
- adapter: ActivityPub::Adapter,
- content_type: 'application/activity+json'
+ render json: collection_presenter, serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json'
end
end
end
private
+ def set_tag
+ @tag = Tag.find_normalized!(params[:id])
+ end
+
def set_body_classes
@body_classes = 'with-modals'
end
format.xml { render content_type: 'application/xrd+xml' }
end
- expires_in(3.days, public: true)
+ expires_in 3.days, public: true
end
end
end
end
end
- expires_in(3.days, public: true)
+ expires_in 3.days, public: true
rescue ActiveRecord::RecordNotFound
head 404
end
private
def username_from_resource
- resource_user = resource_param
-
+ resource_user = resource_param
username, domain = resource_user.split('@')
- if Rails.configuration.x.alternate_domains.include?(domain)
- resource_user = "#{username}@#{Rails.configuration.x.local_domain}"
- end
+ resource_user = "#{username}@#{Rails.configuration.x.local_domain}" if Rails.configuration.x.alternate_domains.include?(domain)
WebfingerResource.new(resource_user).username
end
end
def announceable?(status)
- status.account_id == @account.id || status.public_visibility? || status.unlisted_visibility?
+ status.account_id == @account.id || status.distributable?
end
def related_to_local_activity?
resolve_thread(@status)
fetch_replies(@status)
distribute(@status)
- forward_for_reply if @status.public_visibility? || @status.unlisted_visibility?
+ forward_for_reply if @status.distributable?
end
def find_existing_status
return if @status.nil?
- if @status.public_visibility? || @status.unlisted_visibility?
+ if @status.distributable?
forward_for_reply
forward_for_reblogs
end
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?
- replies_account_status_url(target.account, target, page_params)
+ account_status_replies_url(target.account, target, page_params)
end
# Primary audience of a status
end
def hidden?
- private_visibility? || direct_visibility? || limited_visibility?
+ !distributable?
end
def distributable?
end
def update_statistics
- return unless public_visibility? || unlisted_visibility?
+ return unless distributable?
+
ActivityTracker.increment('activity:statuses:local')
end
account&.increment_count!(:statuses_count)
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
def decrement_counter_caches
account&.decrement_count!(:statuses_count)
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
def unlink_from_conversations
# frozen_string_literal: true
class ActivityPub::ActivitySerializer < ActivityPub::Serializer
+ cache key: 'activity', expires_in: 3.minutes
+
attributes :id, :type, :actor, :published, :to, :cc
has_one :proper, key: :object, serializer: ActivityPub::NoteSerializer, if: :serialize_object?
+
attribute :proper_uri, key: :object, unless: :serialize_object?
attribute :atom_uri, if: :announce?
class ActivityPub::ActorSerializer < ActivityPub::Serializer
include RoutingHelper
+ cache key: 'actor', expires_in: 3.minutes
+
context :security
context_extensions :manually_approves_followers, :featured, :also_known_as,
super
end
+ cache key: 'collection', expires_in: 3.minutes
+
attribute :id, if: -> { object.id.present? }
attribute :type
attribute :total_items, if: -> { object.size.present? }
class ActivityPub::EmojiSerializer < ActivityPub::Serializer
include RoutingHelper
+ cache key: 'emoji', expires_in: 3.minutes
+
context_extensions :emoji
attributes :id, :type, :name, :updated
# frozen_string_literal: true
class ActivityPub::NoteSerializer < ActivityPub::Serializer
+ cache key: 'note', expires_in: 3.minutes
+
context_extensions :atom_uri, :conversation, :sensitive,
:hashtag, :emoji, :focal_point, :blurhash
TrendingTags.record_use!(tag, status.account, status.created_at) if status.public_visibility?
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|
featured_tag.increment(status.created_at)
= fa_icon 'reply-all fw'
.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
- - if status.public_visibility? || status.unlisted_visibility?
+ - if status.distributable?
= fa_icon 'retweet fw'
- - elsif status.private_visibility?
+ - elsif status.private_visibility? || status.limited_visibility?
= fa_icon 'lock fw'
- else
= fa_icon 'envelope fw'
member do
get :activity
get :embed
- get :replies
end
+
+ resources :replies, only: [:index], module: :activitypub
end
resources :followers, only: [:index], controller: :follower_accounts
it 'sets link headers' do
account = Fabricate(:account, username: 'username', user: Fabricate(:user))
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
it 'returns http success' do
end
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)
child = Fabricate(:status, in_reply_to_id: status.id)
end
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)
child0 = Fabricate(:status, in_reply_to_id: status.id)
child1 = Fabricate(:status, in_reply_to_id: child0.id)
end
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.attr_pairs.first).to eq %w(rel lrdd)
end
- it 'contains atom url in link header' do
- link_header = link_header_with_type('application/atom+xml')
+ it 'contains activitypub url in link header' do
+ 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)
end