From 8c45cd0e3683b528b65f416681c8272d5650f32d Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 15 Jul 2017 03:01:39 +0200 Subject: Improve ActivityPub representations (#3844) * Improve webfinger templates and make tests more flexible * Clean up AS2 representation of actor * Refactor outbox * Create activities representation * Add representations of followers/following collections, do not redirect /users/:username route if format is empty * Remove unused translations * ActivityPub endpoint for single statuses, add ActivityPub::TagManager for better URL/URI generation * Add ActivityPub::TagManager#to * Represent all attachments as Document instead of Image/Video specifically (Because for remote ones we may not know for sure) Add mentions and hashtags representation to AP notes * Add AP-resolvable hashtag URIs * Use ActiveModelSerializers for ActivityPub * Clean up unused translations * Separate route for object and activity * Adjust cc/to matrices * Add to/cc to activities, ensure announce activity embeds target status and not the wrapper status, add "id" to all collections --- app/lib/activitypub/adapter.rb | 13 +++++++ app/lib/activitypub/tag_manager.rb | 69 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 app/lib/activitypub/adapter.rb create mode 100644 app/lib/activitypub/tag_manager.rb (limited to 'app/lib/activitypub') diff --git a/app/lib/activitypub/adapter.rb b/app/lib/activitypub/adapter.rb new file mode 100644 index 000000000..0a70207bc --- /dev/null +++ b/app/lib/activitypub/adapter.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +class ActivityPub::Adapter < ActiveModelSerializers::Adapter::Base + def self.default_key_transform + :camel_lower + end + + def serializable_hash(options = nil) + options = serialization_options(options) + serialized_hash = { '@context': 'https://www.w3.org/ns/activitystreams' }.merge(ActiveModelSerializers::Adapter::Attributes.new(serializer, instance_options).serializable_hash(options)) + self.class.transform_key_casing!(serialized_hash, instance_options) + end +end diff --git a/app/lib/activitypub/tag_manager.rb b/app/lib/activitypub/tag_manager.rb new file mode 100644 index 000000000..ec42bcad3 --- /dev/null +++ b/app/lib/activitypub/tag_manager.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +require 'singleton' + +class ActivityPub::TagManager + include Singleton + include RoutingHelper + + COLLECTIONS = { + public: 'https://www.w3.org/ns/activitystreams#Public', + }.freeze + + def url_for(target) + return target.url if target.respond_to?(:local?) && !target.local? + + case target.object_type + when :person + short_account_url(target) + when :note, :comment, :activity + short_account_status_url(target.account, target) + end + end + + def uri_for(target) + return target.uri if target.respond_to?(:local?) && !target.local? + + case target.object_type + when :person + account_url(target) + when :note, :comment, :activity + account_status_url(target.account, target) + end + end + + # Primary audience of a status + # Public statuses go out to primarily the public collection + # Unlisted and private statuses go out primarily to the followers collection + # Others go out only to the people they mention + def to(status) + case status.visibility + when 'public' + [COLLECTIONS[:public]] + when 'unlisted', 'private' + [account_followers_url(status.account)] + when 'direct' + status.mentions.map { |mention| uri_for(mention.account) } + end + end + + # Secondary audience of a status + # Public statuses go out to followers as well + # Unlisted statuses go to the public as well + # Both of those and private statuses also go to the people mentioned in them + # Direct ones don't have a secondary audience + def cc(status) + cc = [] + + case status.visibility + when 'public' + cc << account_followers_url(status.account) + when 'unlisted' + cc << COLLECTIONS[:public] + end + + cc.concat(status.mentions.map { |mention| uri_for(mention.account) }) unless status.direct_visibility? + + cc + end +end -- cgit