Remove Atom feeds and old URLs in the form of `GET /:username/updates/:id` (#11247)
parent
406b46395d
commit
b851456139
@ -1,64 +0,0 @@ |
|||||||
# frozen_string_literal: true |
|
||||||
|
|
||||||
class StreamEntriesController < ApplicationController |
|
||||||
include Authorization |
|
||||||
include SignatureVerification |
|
||||||
|
|
||||||
layout 'public' |
|
||||||
|
|
||||||
before_action :set_account |
|
||||||
before_action :set_stream_entry |
|
||||||
before_action :set_link_headers |
|
||||||
before_action :check_account_suspension |
|
||||||
before_action :set_cache_headers |
|
||||||
|
|
||||||
def show |
|
||||||
respond_to do |format| |
|
||||||
format.html do |
|
||||||
expires_in 5.minutes, public: true unless @stream_entry.hidden? |
|
||||||
|
|
||||||
redirect_to short_account_status_url(params[:account_username], @stream_entry.activity) |
|
||||||
end |
|
||||||
|
|
||||||
format.atom do |
|
||||||
expires_in 3.minutes, public: true unless @stream_entry.hidden? |
|
||||||
|
|
||||||
render xml: OStatus::AtomSerializer.render(OStatus::AtomSerializer.new.entry(@stream_entry, true)) |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
def embed |
|
||||||
redirect_to embed_short_account_status_url(@account, @stream_entry.activity), status: 301 |
|
||||||
end |
|
||||||
|
|
||||||
private |
|
||||||
|
|
||||||
def set_account |
|
||||||
@account = Account.find_local!(params[:account_username]) |
|
||||||
end |
|
||||||
|
|
||||||
def set_link_headers |
|
||||||
response.headers['Link'] = LinkHeader.new( |
|
||||||
[ |
|
||||||
[account_stream_entry_url(@account, @stream_entry, format: 'atom'), [%w(rel alternate), %w(type application/atom+xml)]], |
|
||||||
[ActivityPub::TagManager.instance.uri_for(@stream_entry.activity), [%w(rel alternate), %w(type application/activity+json)]], |
|
||||||
] |
|
||||||
) |
|
||||||
end |
|
||||||
|
|
||||||
def set_stream_entry |
|
||||||
@stream_entry = @account.stream_entries.where(activity_type: 'Status').find(params[:id]) |
|
||||||
@type = 'status' |
|
||||||
|
|
||||||
raise ActiveRecord::RecordNotFound if @stream_entry.activity.nil? |
|
||||||
authorize @stream_entry.activity, :show? if @stream_entry.hidden? |
|
||||||
rescue Mastodon::NotPermittedError |
|
||||||
# Reraise in order to get a 404 |
|
||||||
raise ActiveRecord::RecordNotFound |
|
||||||
end |
|
||||||
|
|
||||||
def check_account_suspension |
|
||||||
gone if @account.suspended? |
|
||||||
end |
|
||||||
end |
|
@ -1,376 +0,0 @@ |
|||||||
# frozen_string_literal: true |
|
||||||
|
|
||||||
class OStatus::AtomSerializer |
|
||||||
include RoutingHelper |
|
||||||
include ActionView::Helpers::SanitizeHelper |
|
||||||
|
|
||||||
class << self |
|
||||||
def render(element) |
|
||||||
document = Ox::Document.new(version: '1.0') |
|
||||||
document << element |
|
||||||
('<?xml version="1.0"?>' + Ox.dump(element, effort: :tolerant)).force_encoding('UTF-8') |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
def author(account) |
|
||||||
author = Ox::Element.new('author') |
|
||||||
|
|
||||||
uri = OStatus::TagManager.instance.uri_for(account) |
|
||||||
|
|
||||||
append_element(author, 'id', uri) |
|
||||||
append_element(author, 'activity:object-type', OStatus::TagManager::TYPES[:person]) |
|
||||||
append_element(author, 'uri', uri) |
|
||||||
append_element(author, 'name', account.username) |
|
||||||
append_element(author, 'email', account.local? ? account.local_username_and_domain : account.acct) |
|
||||||
append_element(author, 'summary', Formatter.instance.simplified_format(account).to_str, type: :html) if account.note? |
|
||||||
append_element(author, 'link', nil, rel: :alternate, type: 'text/html', href: ::TagManager.instance.url_for(account)) |
|
||||||
append_element(author, 'link', nil, rel: :avatar, type: account.avatar_content_type, 'media:width': 120, 'media:height': 120, href: full_asset_url(account.avatar.url(:original))) if account.avatar? |
|
||||||
append_element(author, 'link', nil, rel: :header, type: account.header_content_type, 'media:width': 700, 'media:height': 335, href: full_asset_url(account.header.url(:original))) if account.header? |
|
||||||
account.emojis.each do |emoji| |
|
||||||
append_element(author, 'link', nil, rel: :emoji, href: full_asset_url(emoji.image.url), name: emoji.shortcode) |
|
||||||
end |
|
||||||
append_element(author, 'poco:preferredUsername', account.username) |
|
||||||
append_element(author, 'poco:displayName', account.display_name) if account.display_name? |
|
||||||
append_element(author, 'poco:note', account.local? ? account.note : strip_tags(account.note)) if account.note? |
|
||||||
append_element(author, 'mastodon:scope', account.locked? ? :private : :public) |
|
||||||
|
|
||||||
author |
|
||||||
end |
|
||||||
|
|
||||||
def feed(account, stream_entries) |
|
||||||
feed = Ox::Element.new('feed') |
|
||||||
|
|
||||||
add_namespaces(feed) |
|
||||||
|
|
||||||
append_element(feed, 'id', account_url(account, format: 'atom')) |
|
||||||
append_element(feed, 'title', account.display_name.presence || account.username) |
|
||||||
append_element(feed, 'subtitle', account.note) |
|
||||||
append_element(feed, 'updated', account.updated_at.iso8601) |
|
||||||
append_element(feed, 'logo', full_asset_url(account.avatar.url(:original))) |
|
||||||
|
|
||||||
feed << author(account) |
|
||||||
|
|
||||||
append_element(feed, 'link', nil, rel: :alternate, type: 'text/html', href: ::TagManager.instance.url_for(account)) |
|
||||||
append_element(feed, 'link', nil, rel: :self, type: 'application/atom+xml', href: account_url(account, format: 'atom')) |
|
||||||
append_element(feed, 'link', nil, rel: :next, type: 'application/atom+xml', href: account_url(account, format: 'atom', max_id: stream_entries.last.id)) if stream_entries.size == 20 |
|
||||||
|
|
||||||
stream_entries.each do |stream_entry| |
|
||||||
feed << entry(stream_entry) |
|
||||||
end |
|
||||||
|
|
||||||
feed |
|
||||||
end |
|
||||||
|
|
||||||
def entry(stream_entry, root = false) |
|
||||||
entry = Ox::Element.new('entry') |
|
||||||
|
|
||||||
add_namespaces(entry) if root |
|
||||||
|
|
||||||
append_element(entry, 'id', OStatus::TagManager.instance.uri_for(stream_entry.status)) |
|
||||||
append_element(entry, 'published', stream_entry.created_at.iso8601) |
|
||||||
append_element(entry, 'updated', stream_entry.updated_at.iso8601) |
|
||||||
append_element(entry, 'title', stream_entry&.status&.title || "#{stream_entry.account.acct} deleted status") |
|
||||||
|
|
||||||
entry << author(stream_entry.account) if root |
|
||||||
|
|
||||||
append_element(entry, 'activity:object-type', OStatus::TagManager::TYPES[stream_entry.object_type]) |
|
||||||
append_element(entry, 'activity:verb', OStatus::TagManager::VERBS[stream_entry.verb]) |
|
||||||
|
|
||||||
entry << object(stream_entry.target) if stream_entry.targeted? |
|
||||||
|
|
||||||
if stream_entry.status.nil? |
|
||||||
append_element(entry, 'content', 'Deleted status') |
|
||||||
elsif stream_entry.status.destroyed? |
|
||||||
append_element(entry, 'content', 'Deleted status') |
|
||||||
append_element(entry, 'link', nil, rel: :alternate, type: 'application/activity+json', href: ActivityPub::TagManager.instance.uri_for(stream_entry.status)) if stream_entry.account.local? |
|
||||||
else |
|
||||||
serialize_status_attributes(entry, stream_entry.status) |
|
||||||
end |
|
||||||
|
|
||||||
append_element(entry, 'link', nil, rel: :alternate, type: 'text/html', href: ::TagManager.instance.url_for(stream_entry.status)) |
|
||||||
append_element(entry, 'link', nil, rel: :self, type: 'application/atom+xml', href: account_stream_entry_url(stream_entry.account, stream_entry, format: 'atom')) |
|
||||||
append_element(entry, 'thr:in-reply-to', nil, ref: OStatus::TagManager.instance.uri_for(stream_entry.thread), href: ::TagManager.instance.url_for(stream_entry.thread)) if stream_entry.threaded? |
|
||||||
append_element(entry, 'ostatus:conversation', nil, ref: conversation_uri(stream_entry.status.conversation)) unless stream_entry&.status&.conversation_id.nil? |
|
||||||
|
|
||||||
entry |
|
||||||
end |
|
||||||
|
|
||||||
def object(status) |
|
||||||
object = Ox::Element.new('activity:object') |
|
||||||
|
|
||||||
append_element(object, 'id', OStatus::TagManager.instance.uri_for(status)) |
|
||||||
append_element(object, 'published', status.created_at.iso8601) |
|
||||||
append_element(object, 'updated', status.updated_at.iso8601) |
|
||||||
append_element(object, 'title', status.title) |
|
||||||
|
|
||||||
object << author(status.account) |
|
||||||
|
|
||||||
append_element(object, 'activity:object-type', OStatus::TagManager::TYPES[status.object_type]) |
|
||||||
append_element(object, 'activity:verb', OStatus::TagManager::VERBS[status.verb]) |
|
||||||
|
|
||||||
serialize_status_attributes(object, status) |
|
||||||
|
|
||||||
append_element(object, 'link', nil, rel: :alternate, type: 'text/html', href: ::TagManager.instance.url_for(status)) |
|
||||||
append_element(object, 'thr:in-reply-to', nil, ref: OStatus::TagManager.instance.uri_for(status.thread), href: ::TagManager.instance.url_for(status.thread)) unless status.thread.nil? |
|
||||||
append_element(object, 'ostatus:conversation', nil, ref: conversation_uri(status.conversation)) unless status.conversation_id.nil? |
|
||||||
|
|
||||||
object |
|
||||||
end |
|
||||||
|
|
||||||
def follow_salmon(follow) |
|
||||||
entry = Ox::Element.new('entry') |
|
||||||
add_namespaces(entry) |
|
||||||
|
|
||||||
description = "#{follow.account.acct} started following #{follow.target_account.acct}" |
|
||||||
|
|
||||||
append_element(entry, 'id', OStatus::TagManager.instance.unique_tag(follow.created_at, follow.id, 'Follow')) |
|
||||||
append_element(entry, 'title', description) |
|
||||||
append_element(entry, 'content', description, type: :html) |
|
||||||
|
|
||||||
entry << author(follow.account) |
|
||||||
|
|
||||||
append_element(entry, 'activity:object-type', OStatus::TagManager::TYPES[:activity]) |
|
||||||
append_element(entry, 'activity:verb', OStatus::TagManager::VERBS[:follow]) |
|
||||||
|
|
||||||
object = author(follow.target_account) |
|
||||||
object.value = 'activity:object' |
|
||||||
|
|
||||||
entry << object |
|
||||||
entry |
|
||||||
end |
|
||||||
|
|
||||||
def follow_request_salmon(follow_request) |
|
||||||
entry = Ox::Element.new('entry') |
|
||||||
add_namespaces(entry) |
|
||||||
|
|
||||||
append_element(entry, 'id', OStatus::TagManager.instance.unique_tag(follow_request.created_at, follow_request.id, 'FollowRequest')) |
|
||||||
append_element(entry, 'title', "#{follow_request.account.acct} requested to follow #{follow_request.target_account.acct}") |
|
||||||
|
|
||||||
entry << author(follow_request.account) |
|
||||||
|
|
||||||
append_element(entry, 'activity:object-type', OStatus::TagManager::TYPES[:activity]) |
|
||||||
append_element(entry, 'activity:verb', OStatus::TagManager::VERBS[:request_friend]) |
|
||||||
|
|
||||||
object = author(follow_request.target_account) |
|
||||||
object.value = 'activity:object' |
|
||||||
|
|
||||||
entry << object |
|
||||||
entry |
|
||||||
end |
|
||||||
|
|
||||||
def authorize_follow_request_salmon(follow_request) |
|
||||||
entry = Ox::Element.new('entry') |
|
||||||
add_namespaces(entry) |
|
||||||
|
|
||||||
append_element(entry, 'id', OStatus::TagManager.instance.unique_tag(Time.now.utc, follow_request.id, 'FollowRequest')) |
|
||||||
append_element(entry, 'title', "#{follow_request.target_account.acct} authorizes follow request by #{follow_request.account.acct}") |
|
||||||
|
|
||||||
entry << author(follow_request.target_account) |
|
||||||
|
|
||||||
append_element(entry, 'activity:object-type', OStatus::TagManager::TYPES[:activity]) |
|
||||||
append_element(entry, 'activity:verb', OStatus::TagManager::VERBS[:authorize]) |
|
||||||
|
|
||||||
object = Ox::Element.new('activity:object') |
|
||||||
object << author(follow_request.account) |
|
||||||
|
|
||||||
append_element(object, 'activity:object-type', OStatus::TagManager::TYPES[:activity]) |
|
||||||
append_element(object, 'activity:verb', OStatus::TagManager::VERBS[:request_friend]) |
|
||||||
|
|
||||||
inner_object = author(follow_request.target_account) |
|
||||||
inner_object.value = 'activity:object' |
|
||||||
|
|
||||||
object << inner_object |
|
||||||
entry << object |
|
||||||
entry |
|
||||||
end |
|
||||||
|
|
||||||
def reject_follow_request_salmon(follow_request) |
|
||||||
entry = Ox::Element.new('entry') |
|
||||||
add_namespaces(entry) |
|
||||||
|
|
||||||
append_element(entry, 'id', OStatus::TagManager.instance.unique_tag(Time.now.utc, follow_request.id, 'FollowRequest')) |
|
||||||
append_element(entry, 'title', "#{follow_request.target_account.acct} rejects follow request by #{follow_request.account.acct}") |
|
||||||
|
|
||||||
entry << author(follow_request.target_account) |
|
||||||
|
|
||||||
append_element(entry, 'activity:object-type', OStatus::TagManager::TYPES[:activity]) |
|
||||||
append_element(entry, 'activity:verb', OStatus::TagManager::VERBS[:reject]) |
|
||||||
|
|
||||||
object = Ox::Element.new('activity:object') |
|
||||||
object << author(follow_request.account) |
|
||||||
|
|
||||||
append_element(object, 'activity:object-type', OStatus::TagManager::TYPES[:activity]) |
|
||||||
append_element(object, 'activity:verb', OStatus::TagManager::VERBS[:request_friend]) |
|
||||||
|
|
||||||
inner_object = author(follow_request.target_account) |
|
||||||
inner_object.value = 'activity:object' |
|
||||||
|
|
||||||
object << inner_object |
|
||||||
entry << object |
|
||||||
entry |
|
||||||
end |
|
||||||
|
|
||||||
def unfollow_salmon(follow) |
|
||||||
entry = Ox::Element.new('entry') |
|
||||||
add_namespaces(entry) |
|
||||||
|
|
||||||
description = "#{follow.account.acct} is no longer following #{follow.target_account.acct}" |
|
||||||
|
|
||||||
append_element(entry, 'id', OStatus::TagManager.instance.unique_tag(Time.now.utc, follow.id, 'Follow')) |
|
||||||
append_element(entry, 'title', description) |
|
||||||
append_element(entry, 'content', description, type: :html) |
|
||||||
|
|
||||||
entry << author(follow.account) |
|
||||||
|
|
||||||
append_element(entry, 'activity:object-type', OStatus::TagManager::TYPES[:activity]) |
|
||||||
append_element(entry, 'activity:verb', OStatus::TagManager::VERBS[:unfollow]) |
|
||||||
|
|
||||||
object = author(follow.target_account) |
|
||||||
object.value = 'activity:object' |
|
||||||
|
|
||||||
entry << object |
|
||||||
entry |
|
||||||
end |
|
||||||
|
|
||||||
def block_salmon(block) |
|
||||||
entry = Ox::Element.new('entry') |
|
||||||
add_namespaces(entry) |
|
||||||
|
|
||||||
description = "#{block.account.acct} no longer wishes to interact with #{block.target_account.acct}" |
|
||||||
|
|
||||||
append_element(entry, 'id', OStatus::TagManager.instance.unique_tag(Time.now.utc, block.id, 'Block')) |
|
||||||
append_element(entry, 'title', description) |
|
||||||
|
|
||||||
entry << author(block.account) |
|
||||||
|
|
||||||
append_element(entry, 'activity:object-type', OStatus::TagManager::TYPES[:activity]) |
|
||||||
append_element(entry, 'activity:verb', OStatus::TagManager::VERBS[:block]) |
|
||||||
|
|
||||||
object = author(block.target_account) |
|
||||||
object.value = 'activity:object' |
|
||||||
|
|
||||||
entry << object |
|
||||||
entry |
|
||||||
end |
|
||||||
|
|
||||||
def unblock_salmon(block) |
|
||||||
entry = Ox::Element.new('entry') |
|
||||||
add_namespaces(entry) |
|
||||||
|
|
||||||
description = "#{block.account.acct} no longer blocks #{block.target_account.acct}" |
|
||||||
|
|
||||||
append_element(entry, 'id', OStatus::TagManager.instance.unique_tag(Time.now.utc, block.id, 'Block')) |
|
||||||
append_element(entry, 'title', description) |
|
||||||
|
|
||||||
entry << author(block.account) |
|
||||||
|
|
||||||
append_element(entry, 'activity:object-type', OStatus::TagManager::TYPES[:activity]) |
|
||||||
append_element(entry, 'activity:verb', OStatus::TagManager::VERBS[:unblock]) |
|
||||||
|
|
||||||
object = author(block.target_account) |
|
||||||
object.value = 'activity:object' |
|
||||||
|
|
||||||
entry << object |
|
||||||
entry |
|
||||||
end |
|
||||||
|
|
||||||
def favourite_salmon(favourite) |
|
||||||
entry = Ox::Element.new('entry') |
|
||||||
add_namespaces(entry) |
|
||||||
|
|
||||||
description = "#{favourite.account.acct} favourited a status by #{favourite.status.account.acct}" |
|
||||||
|
|
||||||
append_element(entry, 'id', OStatus::TagManager.instance.unique_tag(favourite.created_at, favourite.id, 'Favourite')) |
|
||||||
append_element(entry, 'title', description) |
|
||||||
append_element(entry, 'content', description, type: :html) |
|
||||||
|
|
||||||
entry << author(favourite.account) |
|
||||||
|
|
||||||
append_element(entry, 'activity:object-type', OStatus::TagManager::TYPES[:activity]) |
|
||||||
append_element(entry, 'activity:verb', OStatus::TagManager::VERBS[:favorite]) |
|
||||||
|
|
||||||
entry << object(favourite.status) |
|
||||||
|
|
||||||
append_element(entry, 'thr:in-reply-to', nil, ref: OStatus::TagManager.instance.uri_for(favourite.status), href: ::TagManager.instance.url_for(favourite.status)) |
|
||||||
|
|
||||||
entry |
|
||||||
end |
|
||||||
|
|
||||||
def unfavourite_salmon(favourite) |
|
||||||
entry = Ox::Element.new('entry') |
|
||||||
add_namespaces(entry) |
|
||||||
|
|
||||||
description = "#{favourite.account.acct} no longer favourites a status by #{favourite.status.account.acct}" |
|
||||||
|
|
||||||
append_element(entry, 'id', OStatus::TagManager.instance.unique_tag(Time.now.utc, favourite.id, 'Favourite')) |
|
||||||
append_element(entry, 'title', description) |
|
||||||
append_element(entry, 'content', description, type: :html) |
|
||||||
|
|
||||||
entry << author(favourite.account) |
|
||||||
|
|
||||||
append_element(entry, 'activity:object-type', OStatus::TagManager::TYPES[:activity]) |
|
||||||
append_element(entry, 'activity:verb', OStatus::TagManager::VERBS[:unfavorite]) |
|
||||||
|
|
||||||
entry << object(favourite.status) |
|
||||||
|
|
||||||
append_element(entry, 'thr:in-reply-to', nil, ref: OStatus::TagManager.instance.uri_for(favourite.status), href: ::TagManager.instance.url_for(favourite.status)) |
|
||||||
|
|
||||||
entry |
|
||||||
end |
|
||||||
|
|
||||||
private |
|
||||||
|
|
||||||
def append_element(parent, name, content = nil, **attributes) |
|
||||||
element = Ox::Element.new(name) |
|
||||||
attributes.each { |k, v| element[k] = sanitize_str(v) } |
|
||||||
element << sanitize_str(content) unless content.nil? |
|
||||||
parent << element |
|
||||||
end |
|
||||||
|
|
||||||
def sanitize_str(raw_str) |
|
||||||
raw_str.to_s |
|
||||||
end |
|
||||||
|
|
||||||
def conversation_uri(conversation) |
|
||||||
return conversation.uri if conversation.uri? |
|
||||||
OStatus::TagManager.instance.unique_tag(conversation.created_at, conversation.id, 'Conversation') |
|
||||||
end |
|
||||||
|
|
||||||
def add_namespaces(parent) |
|
||||||
parent['xmlns'] = OStatus::TagManager::XMLNS |
|
||||||
parent['xmlns:thr'] = OStatus::TagManager::THR_XMLNS |
|
||||||
parent['xmlns:activity'] = OStatus::TagManager::AS_XMLNS |
|
||||||
parent['xmlns:poco'] = OStatus::TagManager::POCO_XMLNS |
|
||||||
parent['xmlns:media'] = OStatus::TagManager::MEDIA_XMLNS |
|
||||||
parent['xmlns:ostatus'] = OStatus::TagManager::OS_XMLNS |
|
||||||
parent['xmlns:mastodon'] = OStatus::TagManager::MTDN_XMLNS |
|
||||||
end |
|
||||||
|
|
||||||
def serialize_status_attributes(entry, status) |
|
||||||
append_element(entry, 'link', nil, rel: :alternate, type: 'application/activity+json', href: ActivityPub::TagManager.instance.uri_for(status)) if status.account.local? |
|
||||||
|
|
||||||
append_element(entry, 'summary', status.spoiler_text, 'xml:lang': status.language) if status.spoiler_text? |
|
||||||
append_element(entry, 'content', Formatter.instance.format(status, inline_poll_options: true).to_str || '.', type: 'html', 'xml:lang': status.language) |
|
||||||
|
|
||||||
status.active_mentions.sort_by(&:id).each do |mentioned| |
|
||||||
append_element(entry, 'link', nil, rel: :mentioned, 'ostatus:object-type': OStatus::TagManager::TYPES[:person], href: OStatus::TagManager.instance.uri_for(mentioned.account)) |
|
||||||
end |
|
||||||
|
|
||||||
append_element(entry, 'link', nil, rel: :mentioned, 'ostatus:object-type': OStatus::TagManager::TYPES[:collection], href: OStatus::TagManager::COLLECTIONS[:public]) if status.public_visibility? |
|
||||||
|
|
||||||
status.tags.each do |tag| |
|
||||||
append_element(entry, 'category', nil, term: tag.name) |
|
||||||
end |
|
||||||
|
|
||||||
status.media_attachments.each do |media| |
|
||||||
append_element(entry, 'link', nil, rel: :enclosure, type: media.file_content_type, length: media.file_file_size, href: full_asset_url(media.file.url(:original, false))) |
|
||||||
end |
|
||||||
|
|
||||||
append_element(entry, 'category', nil, term: 'nsfw') if status.sensitive? && status.media_attachments.any? |
|
||||||
append_element(entry, 'mastodon:scope', status.visibility) |
|
||||||
|
|
||||||
status.emojis.each do |emoji| |
|
||||||
append_element(entry, 'link', nil, rel: :emoji, href: full_asset_url(emoji.image.url), name: emoji.shortcode) |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
@ -1,43 +0,0 @@ |
|||||||
# frozen_string_literal: true |
|
||||||
|
|
||||||
module Streamable |
|
||||||
extend ActiveSupport::Concern |
|
||||||
|
|
||||||
included do |
|
||||||
has_one :stream_entry, as: :activity |
|
||||||
|
|
||||||
after_create do |
|
||||||
account.stream_entries.create!(activity: self, hidden: hidden?) if needs_stream_entry? |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
def title |
|
||||||
super |
|
||||||
end |
|
||||||
|
|
||||||
def content |
|
||||||
title |
|
||||||
end |
|
||||||
|
|
||||||
def target |
|
||||||
super |
|
||||||
end |
|
||||||
|
|
||||||
def object_type |
|
||||||
:activity |
|
||||||
end |
|
||||||
|
|
||||||
def thread |
|
||||||
super |
|
||||||
end |
|
||||||
|
|
||||||
def hidden? |
|
||||||
false |
|
||||||
end |
|
||||||
|
|
||||||
private |
|
||||||
|
|
||||||
def needs_stream_entry? |
|
||||||
account.local? |
|
||||||
end |
|
||||||
end |
|
@ -1,57 +0,0 @@ |
|||||||
# frozen_string_literal: true |
|
||||||
|
|
||||||
class RemoteProfile |
|
||||||
include ActiveModel::Model |
|
||||||
|
|
||||||
attr_reader :document |
|
||||||
|
|
||||||
def initialize(body) |
|
||||||
@document = Nokogiri::XML.parse(body, nil, 'utf-8') |
|
||||||
end |
|
||||||
|
|
||||||
def root |
|
||||||
@root ||= document.at_xpath('/atom:feed|/atom:entry', atom: OStatus::TagManager::XMLNS) |
|
||||||
end |
|
||||||
|
|
||||||
def author |
|
||||||
@author ||= root.at_xpath('./atom:author|./dfrn:owner', atom: OStatus::TagManager::XMLNS, dfrn: OStatus::TagManager::DFRN_XMLNS) |
|
||||||
end |
|
||||||
|
|
||||||
def hub_link |
|
||||||
@hub_link ||= link_href_from_xml(root, 'hub') |
|
||||||
end |
|
||||||
|
|
||||||
def display_name |
|
||||||
@display_name ||= author.at_xpath('./poco:displayName', poco: OStatus::TagManager::POCO_XMLNS)&.content |
|
||||||
end |
|
||||||
|
|
||||||
def note |
|
||||||
@note ||= author.at_xpath('./atom:summary|./poco:note', atom: OStatus::TagManager::XMLNS, poco: OStatus::TagManager::POCO_XMLNS)&.content |
|
||||||
end |
|
||||||
|
|
||||||
def scope |
|
||||||
@scope ||= author.at_xpath('./mastodon:scope', mastodon: OStatus::TagManager::MTDN_XMLNS)&.content |
|
||||||
end |
|
||||||
|
|
||||||
def avatar |
|
||||||
@avatar ||= link_href_from_xml(author, 'avatar') |
|
||||||
end |
|
||||||
|
|
||||||
def header |
|
||||||
@header ||= link_href_from_xml(author, 'header') |
|
||||||
end |
|
||||||
|
|
||||||
def emojis |
|
||||||
@emojis ||= author.xpath('./xmlns:link[@rel="emoji"]', xmlns: OStatus::TagManager::XMLNS) |
|
||||||
end |
|
||||||
|
|
||||||
def locked? |
|
||||||
scope == 'private' |
|
||||||
end |
|
||||||
|
|
||||||
private |
|
||||||
|
|
||||||
def link_href_from_xml(xml, type) |
|
||||||
xml.at_xpath(%(./atom:link[@rel="#{type}"]/@href), atom: OStatus::TagManager::XMLNS)&.content |
|
||||||
end |
|
||||||
end |
|
@ -1,59 +0,0 @@ |
|||||||
# frozen_string_literal: true |
|
||||||
# == Schema Information |
|
||||||
# |
|
||||||
# Table name: stream_entries |
|
||||||
# |
|
||||||
# id :bigint(8) not null, primary key |
|
||||||
# activity_id :bigint(8) |
|
||||||
# activity_type :string |
|
||||||
# created_at :datetime not null |
|
||||||
# updated_at :datetime not null |
|
||||||
# hidden :boolean default(FALSE), not null |
|
||||||
# account_id :bigint(8) |
|
||||||
# |
|
||||||
|
|
||||||
class StreamEntry < ApplicationRecord |
|
||||||
include Paginable |
|
||||||
|
|
||||||
belongs_to :account, inverse_of: :stream_entries |
|
||||||
belongs_to :activity, polymorphic: true |
|
||||||
belongs_to :status, foreign_type: 'Status', foreign_key: 'activity_id', inverse_of: :stream_entry |
|
||||||
|
|
||||||
validates :account, :activity, presence: true |
|
||||||
|
|
||||||
STATUS_INCLUDES = [:account, :stream_entry, :conversation, :media_attachments, :tags, mentions: :account, reblog: [:stream_entry, :account, :conversation, :media_attachments, :tags, mentions: :account], thread: [:stream_entry, :account]].freeze |
|
||||||
|
|
||||||
default_scope { where(activity_type: 'Status') } |
|
||||||
scope :recent, -> { reorder(id: :desc) } |
|
||||||
scope :with_includes, -> { includes(:account, status: STATUS_INCLUDES) } |
|
||||||
|
|
||||||
delegate :target, :title, :content, :thread, |
|
||||||
to: :status, |
|
||||||
allow_nil: true |
|
||||||
|
|
||||||
def object_type |
|
||||||
orphaned? || targeted? ? :activity : status.object_type |
|
||||||
end |
|
||||||
|
|
||||||
def verb |
|
||||||
orphaned? ? :delete : status.verb |
|
||||||
end |
|
||||||
|
|
||||||
def targeted? |
|
||||||
[:follow, :request_friend, :authorize, :reject, :unfollow, :block, :unblock, :share, :favorite].include? verb |
|
||||||
end |
|
||||||
|
|
||||||
def threaded? |
|
||||||
(verb == :favorite || object_type == :comment) && !thread.nil? |
|
||||||
end |
|
||||||
|
|
||||||
def mentions |
|
||||||
orphaned? ? [] : status.active_mentions.map(&:account) |
|
||||||
end |
|
||||||
|
|
||||||
private |
|
||||||
|
|
||||||
def orphaned? |
|
||||||
status.nil? |
|
||||||
end |
|
||||||
end |
|
@ -1,4 +1,4 @@ |
|||||||
.post-follow-actions |
.post-follow-actions |
||||||
%div= link_to t('authorize_follow.post_follow.web'), web_url("accounts/#{@resource.id}"), class: 'button button--block' |
%div= link_to t('authorize_follow.post_follow.web'), web_url("accounts/#{@resource.id}"), class: 'button button--block' |
||||||
%div= link_to t('authorize_follow.post_follow.return'), TagManager.instance.url_for(@resource), class: 'button button--block' |
%div= link_to t('authorize_follow.post_follow.return'), ActivityPub::TagManager.instance.url_for(@resource), class: 'button button--block' |
||||||
%div= t('authorize_follow.post_follow.close') |
%div= t('authorize_follow.post_follow.close') |
||||||
|
@ -1,4 +1,4 @@ |
|||||||
.post-follow-actions |
.post-follow-actions |
||||||
%div= link_to t('authorize_follow.post_follow.web'), web_url("accounts/#{@account.id}"), class: 'button button--block' |
%div= link_to t('authorize_follow.post_follow.web'), web_url("accounts/#{@account.id}"), class: 'button button--block' |
||||||
%div= link_to t('authorize_follow.post_follow.return'), TagManager.instance.url_for(@account), class: 'button button--block' |
%div= link_to t('authorize_follow.post_follow.return'), ActivityPub::TagManager.instance.url_for(@account), class: 'button button--block' |
||||||
%div= t('authorize_follow.post_follow.close') |
%div= t('authorize_follow.post_follow.close') |
||||||
|
@ -0,0 +1,3 @@ |
|||||||
|
- cache @status do |
||||||
|
.activity-stream.activity-stream--headless |
||||||
|
= render 'status', status: @status, centered: true, autoplay: @autoplay |
@ -0,0 +1,24 @@ |
|||||||
|
- content_for :page_title do |
||||||
|
= t('statuses.title', name: display_name(@account), quote: truncate(@status.spoiler_text.presence || @status.text, length: 50, omission: '…', escape: false)) |
||||||
|
|
||||||
|
- content_for :header_tags do |
||||||
|
- if @account.user&.setting_noindex |
||||||
|
%meta{ name: 'robots', content: 'noindex' }/ |
||||||
|
|
||||||
|
%link{ rel: 'alternate', type: 'application/json+oembed', href: api_oembed_url(url: short_account_status_url(@account, @status), format: 'json') }/ |
||||||
|
%link{ rel: 'alternate', type: 'application/activity+json', href: ActivityPub::TagManager.instance.uri_for(@status) }/ |
||||||
|
|
||||||
|
= opengraph 'og:site_name', site_title |
||||||
|
= opengraph 'og:type', 'article' |
||||||
|
= opengraph 'og:title', "#{display_name(@account)} (@#{@account.local_username_and_domain})" |
||||||
|
= opengraph 'og:url', short_account_status_url(@account, @status) |
||||||
|
|
||||||
|
= render 'og_description', activity: @status |
||||||
|
= render 'og_image', activity: @status, account: @account |
||||||
|
|
||||||
|
.grid |
||||||
|
.column-0 |
||||||
|
.activity-stream.h-entry |
||||||
|
= render partial: 'status', locals: { status: @status, include_threads: true } |
||||||
|
.column-1 |
||||||
|
= render 'application/sidebar' |
@ -1,3 +0,0 @@ |
|||||||
- cache @stream_entry.activity do |
|
||||||
.activity-stream.activity-stream--headless |
|
||||||
= render "stream_entries/#{@type}", @type.to_sym => @stream_entry.activity, centered: true, autoplay: @autoplay |
|
@ -1,25 +0,0 @@ |
|||||||
- content_for :page_title do |
|
||||||
= t('statuses.title', name: display_name(@account), quote: truncate(@stream_entry.activity.spoiler_text.presence || @stream_entry.activity.text, length: 50, omission: '…', escape: false)) |
|
||||||
|
|
||||||
- content_for :header_tags do |
|
||||||
- if @account.user&.setting_noindex |
|
||||||
%meta{ name: 'robots', content: 'noindex' }/ |
|
||||||
|
|
||||||
%link{ rel: 'alternate', type: 'application/atom+xml', href: account_stream_entry_url(@account, @stream_entry, format: 'atom') }/ |
|
||||||
%link{ rel: 'alternate', type: 'application/json+oembed', href: api_oembed_url(url: account_stream_entry_url(@account, @stream_entry), format: 'json') }/ |
|
||||||
%link{ rel: 'alternate', type: 'application/activity+json', href: ActivityPub::TagManager.instance.uri_for(@stream_entry.activity) }/ |
|
||||||
|
|
||||||
= opengraph 'og:site_name', site_title |
|
||||||
= opengraph 'og:type', 'article' |
|
||||||
= opengraph 'og:title', "#{display_name(@account)} (@#{@account.local_username_and_domain})" |
|
||||||
= opengraph 'og:url', short_account_status_url(@account, @stream_entry.activity) |
|
||||||
|
|
||||||
= render 'stream_entries/og_description', activity: @stream_entry.activity |
|
||||||
= render 'stream_entries/og_image', activity: @stream_entry.activity, account: @account |
|
||||||
|
|
||||||
.grid |
|
||||||
.column-0 |
|
||||||
.activity-stream.h-entry |
|
||||||
= render partial: "stream_entries/#{@type}", locals: { @type.to_sym => @stream_entry.activity, include_threads: true } |
|
||||||
.column-1 |
|
||||||
= render 'application/sidebar' |
|
@ -0,0 +1,13 @@ |
|||||||
|
# frozen_string_literal: true |
||||||
|
|
||||||
|
class DropStreamEntries < ActiveRecord::Migration[5.2] |
||||||
|
disable_ddl_transaction! |
||||||
|
|
||||||
|
def up |
||||||
|
drop_table :stream_entries |
||||||
|
end |
||||||
|
|
||||||
|
def down |
||||||
|
raise ActiveRecord::IrreversibleMigration |
||||||
|
end |
||||||
|
end |
@ -1,95 +0,0 @@ |
|||||||
require 'rails_helper' |
|
||||||
|
|
||||||
RSpec.describe StreamEntriesController, type: :controller do |
|
||||||
render_views |
|
||||||
|
|
||||||
shared_examples 'before_action' do |route| |
|
||||||
context 'when account is not suspended and stream_entry is available' do |
|
||||||
it 'assigns instance variables' do |
|
||||||
status = Fabricate(:status) |
|
||||||
|
|
||||||
get route, params: { account_username: status.account.username, id: status.stream_entry.id } |
|
||||||
|
|
||||||
expect(assigns(:account)).to eq status.account |
|
||||||
expect(assigns(:stream_entry)).to eq status.stream_entry |
|
||||||
expect(assigns(:type)).to eq 'status' |
|
||||||
end |
|
||||||
|
|
||||||
it 'sets Link headers' do |
|
||||||
alice = Fabricate(:account, username: 'alice') |
|
||||||
status = Fabricate(:status, account: alice) |
|
||||||
|
|
||||||
get route, params: { account_username: alice.username, id: status.stream_entry.id } |
|
||||||
|
|
||||||
expect(response.headers['Link'].to_s).to eq "<http://test.host/users/alice/updates/#{status.stream_entry.id}.atom>; rel=\"alternate\"; type=\"application/atom+xml\", <https://cb6e6126.ngrok.io/users/alice/statuses/#{status.id}>; rel=\"alternate\"; type=\"application/activity+json\"" |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
context 'when account is suspended' do |
|
||||||
it 'returns http status 410' do |
|
||||||
account = Fabricate(:account, suspended: true) |
|
||||||
status = Fabricate(:status, account: account) |
|
||||||
|
|
||||||
get route, params: { account_username: account.username, id: status.stream_entry.id } |
|
||||||
|
|
||||||
expect(response).to have_http_status(410) |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
context 'when activity is nil' do |
|
||||||
it 'raises ActiveRecord::RecordNotFound' do |
|
||||||
account = Fabricate(:account) |
|
||||||
stream_entry = Fabricate.build(:stream_entry, account: account, activity: nil, activity_type: 'Status') |
|
||||||
stream_entry.save!(validate: false) |
|
||||||
|
|
||||||
get route, params: { account_username: account.username, id: stream_entry.id } |
|
||||||
|
|
||||||
expect(response).to have_http_status(404) |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
context 'when it is hidden and it is not permitted' do |
|
||||||
it 'raises ActiveRecord::RecordNotFound' do |
|
||||||
status = Fabricate(:status) |
|
||||||
user = Fabricate(:user) |
|
||||||
status.account.block!(user.account) |
|
||||||
status.stream_entry.update!(hidden: true) |
|
||||||
|
|
||||||
sign_in(user) |
|
||||||
get route, params: { account_username: status.account.username, id: status.stream_entry.id } |
|
||||||
|
|
||||||
expect(response).to have_http_status(404) |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe 'GET #show' do |
|
||||||
include_examples 'before_action', :show |
|
||||||
|
|
||||||
it 'redirects to status page' do |
|
||||||
status = Fabricate(:status) |
|
||||||
|
|
||||||
get :show, params: { account_username: status.account.username, id: status.stream_entry.id } |
|
||||||
|
|
||||||
expect(response).to redirect_to(short_account_status_url(status.account, status)) |
|
||||||
end |
|
||||||
|
|
||||||
it 'returns http success with Atom' do |
|
||||||
status = Fabricate(:status) |
|
||||||
get :show, params: { account_username: status.account.username, id: status.stream_entry.id }, format: 'atom' |
|
||||||
expect(response).to have_http_status(200) |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe 'GET #embed' do |
|
||||||
include_examples 'before_action', :embed |
|
||||||
|
|
||||||
it 'redirects to new embed page' do |
|
||||||
status = Fabricate(:status) |
|
||||||
|
|
||||||
get :embed, params: { account_username: status.account.username, id: status.stream_entry.id } |
|
||||||
|
|
||||||
expect(response).to redirect_to(embed_short_account_status_url(status.account, status)) |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
@ -1,5 +0,0 @@ |
|||||||
Fabricator(:stream_entry) do |
|
||||||
account |
|
||||||
activity { Fabricate(:status) } |
|
||||||
hidden { [true, false].sample } |
|
||||||
end |
|
File diff suppressed because it is too large
Load Diff
@ -1,63 +0,0 @@ |
|||||||
# frozen_string_literal: true |
|
||||||
|
|
||||||
require 'rails_helper' |
|
||||||
|
|
||||||
RSpec.describe Streamable do |
|
||||||
class Parent |
|
||||||
def title; end |
|
||||||
|
|
||||||
def target; end |
|
||||||
|
|
||||||
def thread; end |
|
||||||
|
|
||||||
def self.has_one(*); end |
|
||||||
|
|
||||||
def self.after_create; end |
|
||||||
end |
|
||||||
|
|
||||||
class Child < Parent |
|
||||||
include Streamable |
|
||||||
end |
|
||||||
|
|
||||||
child = Child.new |
|
||||||
|
|
||||||
describe '#title' do |
|
||||||
it 'calls Parent#title' do |
|
||||||
expect_any_instance_of(Parent).to receive(:title) |
|
||||||
child.title |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe '#content' do |
|
||||||
it 'calls #title' do |
|
||||||
expect_any_instance_of(Parent).to receive(:title) |
|
||||||
child.content |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe '#target' do |
|
||||||
it 'calls Parent#target' do |
|
||||||
expect_any_instance_of(Parent).to receive(:target) |
|
||||||
child.target |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe '#object_type' do |
|
||||||
it 'returns :activity' do |
|
||||||
expect(child.object_type).to eq :activity |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe '#thread' do |
|
||||||
it 'calls Parent#thread' do |
|
||||||
expect_any_instance_of(Parent).to receive(:thread) |
|
||||||
child.thread |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe '#hidden?' do |
|
||||||
it 'returns false' do |
|
||||||
expect(child.hidden?).to be false |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
@ -1,143 +0,0 @@ |
|||||||
# frozen_string_literal: true |
|
||||||
|
|
||||||
require 'rails_helper' |
|
||||||
|
|
||||||
RSpec.describe RemoteProfile do |
|
||||||
let(:remote_profile) { RemoteProfile.new(body) } |
|
||||||
let(:body) do |
|
||||||
<<-XML |
|
||||||
<feed xmlns="http://www.w3.org/2005/Atom"> |
|
||||||
<author>John</author> |
|
||||||
XML |
|
||||||
end |
|
||||||
|
|
||||||
describe '.initialize' do |
|
||||||
it 'calls Nokogiri::XML.parse' do |
|
||||||
expect(Nokogiri::XML).to receive(:parse).with(body, nil, 'utf-8') |
|
||||||
RemoteProfile.new(body) |
|
||||||
end |
|
||||||
|
|
||||||
it 'sets document' do |
|
||||||
remote_profile = RemoteProfile.new(body) |
|
||||||
expect(remote_profile).not_to be nil |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe '#root' do |
|
||||||
let(:document) { remote_profile.document } |
|
||||||
|
|
||||||
it 'callse document.at_xpath' do |
|
||||||
expect(document).to receive(:at_xpath).with( |
|
||||||
'/atom:feed|/atom:entry', |
|
||||||
atom: OStatus::TagManager::XMLNS |
|
||||||
) |
|
||||||
|
|
||||||
remote_profile.root |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe '#author' do |
|
||||||
let(:root) { remote_profile.root } |
|
||||||
|
|
||||||
it 'calls root.at_xpath' do |
|
||||||
expect(root).to receive(:at_xpath).with( |
|
||||||
'./atom:author|./dfrn:owner', |
|
||||||
atom: OStatus::TagManager::XMLNS, |
|
||||||
dfrn: OStatus::TagManager::DFRN_XMLNS |
|
||||||
) |
|
||||||
|
|
||||||
remote_profile.author |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe '#hub_link' do |
|
||||||
let(:root) { remote_profile.root } |
|
||||||
|
|
||||||
it 'calls #link_href_from_xml' do |
|
||||||
expect(remote_profile).to receive(:link_href_from_xml).with(root, 'hub') |
|
||||||
remote_profile.hub_link |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe '#display_name' do |
|
||||||
let(:author) { remote_profile.author } |
|
||||||
|
|
||||||
it 'calls author.at_xpath.content' do |
|
||||||
expect(author).to receive_message_chain(:at_xpath, :content).with( |
|
||||||
'./poco:displayName', |
|
||||||
poco: OStatus::TagManager::POCO_XMLNS |
|
||||||
).with(no_args) |
|
||||||
|
|
||||||
remote_profile.display_name |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe '#note' do |
|
||||||
let(:author) { remote_profile.author } |
|
||||||
|
|
||||||
it 'calls author.at_xpath.content' do |
|
||||||
expect(author).to receive_message_chain(:at_xpath, :content).with( |
|
||||||
'./atom:summary|./poco:note', |
|
||||||
atom: OStatus::TagManager::XMLNS, |
|
||||||
poco: OStatus::TagManager::POCO_XMLNS |
|
||||||
).with(no_args) |
|
||||||
|
|
||||||
remote_profile.note |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe '#scope' do |
|
||||||
let(:author) { remote_profile.author } |
|
||||||
|
|
||||||
it 'calls author.at_xpath.content' do |
|
||||||
expect(author).to receive_message_chain(:at_xpath, :content).with( |
|
||||||
'./mastodon:scope', |
|
||||||
mastodon: OStatus::TagManager::MTDN_XMLNS |
|
||||||
).with(no_args) |
|
||||||
|
|
||||||
remote_profile.scope |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe '#avatar' do |
|
||||||
let(:author) { remote_profile.author } |
|
||||||
|
|
||||||
it 'calls #link_href_from_xml' do |
|
||||||
expect(remote_profile).to receive(:link_href_from_xml).with(author, 'avatar') |
|
||||||
remote_profile.avatar |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe '#header' do |
|
||||||
let(:author) { remote_profile.author } |
|
||||||
|
|
||||||
it 'calls #link_href_from_xml' do |
|
||||||
expect(remote_profile).to receive(:link_href_from_xml).with(author, 'header') |
|
||||||
remote_profile.header |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe '#locked?' do |
|
||||||
before do |
|
||||||
allow(remote_profile).to receive(:scope).and_return(scope) |
|
||||||
end |
|
||||||
|
|
||||||
subject { remote_profile.locked? } |
|
||||||
|
|
||||||
context 'scope is private' do |
|
||||||
let(:scope) { 'private' } |
|
||||||
|
|
||||||
it 'returns true' do |
|
||||||
is_expected.to be true |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
context 'scope is not private' do |
|
||||||
let(:scope) { 'public' } |
|
||||||
|
|
||||||
it 'returns false' do |
|
||||||
is_expected.to be false |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
@ -1,192 +0,0 @@ |
|||||||
require 'rails_helper' |
|
||||||
|
|
||||||
RSpec.describe StreamEntry, type: :model do |
|
||||||
let(:alice) { Fabricate(:account, username: 'alice') } |
|
||||||
let(:bob) { Fabricate(:account, username: 'bob') } |
|
||||||
let(:status) { Fabricate(:status, account: alice) } |
|
||||||
let(:reblog) { Fabricate(:status, account: bob, reblog: status) } |
|
||||||
let(:reply) { Fabricate(:status, account: bob, thread: status) } |
|
||||||
let(:stream_entry) { Fabricate(:stream_entry, activity: activity) } |
|
||||||
let(:activity) { reblog } |
|
||||||
|
|
||||||
describe '#object_type' do |
|
||||||
before do |
|
||||||
allow(stream_entry).to receive(:orphaned?).and_return(orphaned) |
|
||||||
allow(stream_entry).to receive(:targeted?).and_return(targeted) |
|
||||||
end |
|
||||||
|
|
||||||
subject { stream_entry.object_type } |
|
||||||
|
|
||||||
context 'orphaned? is true' do |
|
||||||
let(:orphaned) { true } |
|
||||||
let(:targeted) { false } |
|
||||||
|
|
||||||
it 'returns :activity' do |
|
||||||
is_expected.to be :activity |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
context 'targeted? is true' do |
|
||||||
let(:orphaned) { false } |
|
||||||
let(:targeted) { true } |
|
||||||
|
|
||||||
it 'returns :activity' do |
|
||||||
is_expected.to be :activity |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
context 'orphaned? and targeted? are false' do |
|
||||||
let(:orphaned) { false } |
|
||||||
let(:targeted) { false } |
|
||||||
|
|
||||||
context 'activity is reblog' do |
|
||||||
let(:activity) { reblog } |
|
||||||
|
|
||||||
it 'returns :note' do |
|
||||||
is_expected.to be :note |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
context 'activity is reply' do |
|
||||||
let(:activity) { reply } |
|
||||||
|
|
||||||
it 'returns :comment' do |
|
||||||
is_expected.to be :comment |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe '#verb' do |
|
||||||
before do |
|
||||||
allow(stream_entry).to receive(:orphaned?).and_return(orphaned) |
|
||||||
end |
|
||||||
|
|
||||||
subject { stream_entry.verb } |
|
||||||
|
|
||||||
context 'orphaned? is true' do |
|
||||||
let(:orphaned) { true } |
|
||||||
|
|
||||||
it 'returns :delete' do |
|
||||||
is_expected.to be :delete |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
context 'orphaned? is false' do |
|
||||||
let(:orphaned) { false } |
|
||||||
|
|
||||||
context 'activity is reblog' do |
|
||||||
let(:activity) { reblog } |
|
||||||
|
|
||||||
it 'returns :share' do |
|
||||||
is_expected.to be :share |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
context 'activity is reply' do |
|
||||||
let(:activity) { reply } |
|
||||||
|
|
||||||
it 'returns :post' do |
|
||||||
is_expected.to be :post |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe '#mentions' do |
|
||||||
before do |
|
||||||
allow(stream_entry).to receive(:orphaned?).and_return(orphaned) |
|
||||||
end |
|
||||||
|
|
||||||
subject { stream_entry.mentions } |
|
||||||
|
|
||||||
context 'orphaned? is true' do |
|
||||||
let(:orphaned) { true } |
|
||||||
|
|
||||||
it 'returns []' do |
|
||||||
is_expected.to eq [] |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
context 'orphaned? is false' do |
|
||||||
before do |
|
||||||
reblog.mentions << Fabricate(:mention, account: alice) |
|
||||||
reblog.mentions << Fabricate(:mention, account: bob) |
|
||||||
end |
|
||||||
|
|
||||||
let(:orphaned) { false } |
|
||||||
|
|
||||||
it 'returns [Account] includes alice and bob' do |
|
||||||
is_expected.to eq [alice, bob] |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe '#targeted?' do |
|
||||||
it 'returns true for a reblog' do |
|
||||||
expect(reblog.stream_entry.targeted?).to be true |
|
||||||
end |
|
||||||
|
|
||||||
it 'returns false otherwise' do |
|
||||||
expect(status.stream_entry.targeted?).to be false |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe '#threaded?' do |
|
||||||
it 'returns true for a reply' do |
|
||||||
expect(reply.stream_entry.threaded?).to be true |
|
||||||
end |
|
||||||
|
|
||||||
it 'returns false otherwise' do |
|
||||||
expect(status.stream_entry.threaded?).to be false |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe 'delegated methods' do |
|
||||||
context 'with a nil status' do |
|
||||||
subject { described_class.new(status: nil) } |
|
||||||
|
|
||||||
it 'returns nil for target' do |
|
||||||
expect(subject.target).to be_nil |
|
||||||
end |
|
||||||
|
|
||||||
it 'returns nil for title' do |
|
||||||
expect(subject.title).to be_nil |
|
||||||
end |
|
||||||
|
|
||||||
it 'returns nil for content' do |
|
||||||
expect(subject.content).to be_nil |
|
||||||
end |
|
||||||
|
|
||||||
it 'returns nil for thread' do |
|
||||||
expect(subject.thread).to be_nil |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
context 'with a real status' do |
|
||||||
let(:original) { Fabricate(:status, text: 'Test status') } |
|
||||||
let(:status) { Fabricate(:status, reblog: original, thread: original) } |
|
||||||
subject { described_class.new(status: status) } |
|
||||||
|
|
||||||
it 'delegates target' do |
|
||||||
expect(status.target).not_to be_nil |
|
||||||
expect(subject.target).to eq(status.target) |
|
||||||
end |
|
||||||
|
|
||||||
it 'delegates title' do |
|
||||||
expect(status.title).not_to be_nil |
|
||||||
expect(subject.title).to eq(status.title) |
|
||||||
end |
|
||||||
|
|
||||||
it 'delegates content' do |
|
||||||
expect(status.content).not_to be_nil |
|
||||||
expect(subject.content).to eq(status.content) |
|
||||||
end |
|
||||||
|
|
||||||
it 'delegates thread' do |
|
||||||
expect(status.thread).not_to be_nil |
|
||||||
expect(subject.thread).to eq(status.thread) |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
Loading…
Reference in new issue