diff --git a/app/models/search.rb b/app/models/search.rb index 676c2a7f8e..66c9173e02 100644 --- a/app/models/search.rb +++ b/app/models/search.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true class Search < ActiveModelSerializers::Model - attributes :accounts, :statuses, :hashtags + attributes :accounts, :statuses, :hashtags, :collections end diff --git a/app/serializers/rest/search_serializer.rb b/app/serializers/rest/search_serializer.rb index ee9b421eb2..8141a4c69c 100644 --- a/app/serializers/rest/search_serializer.rb +++ b/app/serializers/rest/search_serializer.rb @@ -4,4 +4,5 @@ class REST::SearchSerializer < ActiveModel::Serializer has_many :accounts, serializer: REST::AccountSerializer has_many :statuses, serializer: REST::StatusSerializer has_many :hashtags, serializer: REST::TagSerializer + has_many :collections, serializer: REST::CollectionSerializer end diff --git a/app/services/activitypub/fetch_remote_featured_collection_service.rb b/app/services/activitypub/fetch_remote_featured_collection_service.rb index babad143e1..968dc6811e 100644 --- a/app/services/activitypub/fetch_remote_featured_collection_service.rb +++ b/app/services/activitypub/fetch_remote_featured_collection_service.rb @@ -3,8 +3,12 @@ class ActivityPub::FetchRemoteFeaturedCollectionService < BaseService include JsonLdHelper - def call(uri, request_id: nil, on_behalf_of: nil) - json = fetch_resource(uri, true, on_behalf_of) + def call(uri, request_id: nil, prefetched_body: nil, on_behalf_of: nil) + json = if prefetched_body.nil? + fetch_resource(uri, true, on_behalf_of) + else + body_to_json(prefetched_body, compare_id: uri) + end return unless supported_context?(json) return unless json['type'] == 'FeaturedCollection' diff --git a/app/services/fetch_resource_service.rb b/app/services/fetch_resource_service.rb index 514c838d64..eb3d000a1d 100644 --- a/app/services/fetch_resource_service.rb +++ b/app/services/fetch_resource_service.rb @@ -63,7 +63,7 @@ class FetchResourceService < BaseService end def expected_type?(json) - equals_or_includes_any?(json['type'], ActivityPub::Activity::Create::SUPPORTED_TYPES + ActivityPub::Activity::Create::CONVERTED_TYPES) + equals_or_includes_any?(json['type'], ActivityPub::Activity::Create::SUPPORTED_TYPES + ActivityPub::Activity::Create::CONVERTED_TYPES + %w(FeaturedCollection)) end def process_html(response) diff --git a/app/services/resolve_url_service.rb b/app/services/resolve_url_service.rb index 899f586b81..9a136439b0 100644 --- a/app/services/resolve_url_service.rb +++ b/app/services/resolve_url_service.rb @@ -28,6 +28,10 @@ class ResolveURLService < BaseService status = FetchRemoteStatusService.new.call(resource_url, prefetched_body: body) authorize_with @on_behalf_of, status, :show? unless status.nil? status + elsif type == 'FeaturedCollection' && Mastodon::Feature.collections_enabled? + collection = ActivityPub::FetchRemoteFeaturedCollectionService.new.call(resource_url, prefetched_body: body) + authorize_with @on_behalf_of, collection, :show? unless collection.nil? + collection end end @@ -111,9 +115,21 @@ class ResolveURLService < BaseService Account.find_remote(username, domain) end + when 'collections' + return unless recognized_params[:action] == 'show' + + check_collection(Collection.find_by(id: recognized_params[:id])) end end + def check_collection(collection) + return if collection.nil? + + authorize_with @on_behalf_of, collection, :show? + rescue Mastodon::NotPermittedError + nil + end + def check_local_status(status) return if status.nil? diff --git a/app/services/search_service.rb b/app/services/search_service.rb index ffe380c2e0..fd5a186cd3 100644 --- a/app/services/search_service.rb +++ b/app/services/search_service.rb @@ -64,7 +64,7 @@ class SearchService < BaseService end def default_results - { accounts: [], hashtags: [], statuses: [] } + { accounts: [], hashtags: [], statuses: [], collections: [] } end def url_query? diff --git a/spec/services/resolve_url_service_spec.rb b/spec/services/resolve_url_service_spec.rb index eaf00c1ed8..6174d8cac9 100644 --- a/spec/services/resolve_url_service_spec.rb +++ b/spec/services/resolve_url_service_spec.rb @@ -30,6 +30,49 @@ RSpec.describe ResolveURLService do expect(subject.call(url)).to eq known_account end + context 'when searching for a remote collection', feature: :collections do + let(:account) { Fabricate(:account) } + let(:collection_account) { Fabricate(:account, domain: 'example.com', protocol: :activitypub) } + + let(:uri) { 'https://example.com/featured_collections/1' } + + let(:payload) do + { + '@context' => 'https://www.w3.org/ns/activitystreams', + 'id' => uri, + 'type' => 'FeaturedCollection', + 'name' => 'Incredible people', + 'summary' => 'These are really amazing', + 'attributedTo' => collection_account.uri, + 'sensitive' => false, + 'discoverable' => true, + 'totalItems' => 0, + } + end + + before do + stub_request(:get, uri).to_return(status: 200, body: payload.to_json, headers: { 'Content-Type': 'application/activity+json' }) + end + + it 'returns the collection' do + expect(subject.call(uri, on_behalf_of: account)) + .to be_a(Collection) + .and have_attributes( + uri: uri + ) + end + end + + context 'when searching for a local collection', feature: :collections do + let(:account) { Fabricate(:account) } + let(:collection) { Fabricate(:collection) } + + it 'returns the collection' do + expect(subject.call(ActivityPub::TagManager.instance.uri_for(collection), on_behalf_of: account)) + .to eq(collection) + end + end + context 'when searching for a remote private status' do let(:account) { Fabricate(:account) } let(:poster) { Fabricate(:account, domain: 'example.com') } diff --git a/spec/services/search_service_spec.rb b/spec/services/search_service_spec.rb index 3260addb31..07aa061518 100644 --- a/spec/services/search_service_spec.rb +++ b/spec/services/search_service_spec.rb @@ -86,6 +86,6 @@ RSpec.describe SearchService do end def empty_results - { accounts: [], hashtags: [], statuses: [] } + { accounts: [], hashtags: [], statuses: [], collections: [] } end end