From b1e6e6957e62f3da3857f42ec6c343cb9660434d Mon Sep 17 00:00:00 2001 From: Fire Demon Date: Sun, 16 Aug 2020 23:18:38 -0500 Subject: [Federation] Include and dereference URI to the root post of threads; dynamically update thread permissions --- app/lib/activitypub/activity/create.rb | 19 ++++++++++++++++--- app/lib/activitypub/adapter.rb | 1 + app/models/conversation.rb | 3 ++- app/models/status.rb | 10 ++++++++++ app/models/status_mute.rb | 2 +- app/serializers/activitypub/note_serializer.rb | 8 ++++++-- app/serializers/rest/status_serializer.rb | 6 +++++- .../20200816200108_add_root_to_conversations.rb | 7 +++++++ .../20200816200239_backfill_root_to_conversations.rb | 19 +++++++++++++++++++ .../20200817003033_add_defaults_to_conversations.rb | 8 ++++++++ db/schema.rb | 5 +++-- 11 files changed, 78 insertions(+), 10 deletions(-) create mode 100644 db/migrate/20200816200108_add_root_to_conversations.rb create mode 100644 db/migrate/20200816200239_backfill_root_to_conversations.rb create mode 100644 db/migrate/20200817003033_add_defaults_to_conversations.rb diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb index 12c3b0d19..91f0ab146 100644 --- a/app/lib/activitypub/activity/create.rb +++ b/app/lib/activitypub/activity/create.rb @@ -373,6 +373,8 @@ class ActivityPub::Activity::Create < ActivityPub::Activity end def fetch_replies(status) + FetchReplyWorker.perform_async(@object['root']) unless @object['root'].blank? || [@object['id'], @object['url']].include?(@object['root']) || status_from_uri(@object['root']) + collection = @object['replies'] return if collection.nil? @@ -385,11 +387,22 @@ class ActivityPub::Activity::Create < ActivityPub::Activity def conversation_from_uri(uri) return nil if uri.nil? - return Conversation.find_by(id: OStatus::TagManager.instance.unique_tag_to_local_id(uri, 'Conversation')) if OStatus::TagManager.instance.local_id?(uri) + conversation = OStatus::TagManager.instance.local_id?(uri) ? Conversation.find_by(id: OStatus::TagManager.instance.unique_tag_to_local_id(uri, 'Conversation')) : nil begin - conversation = Conversation.find_by(uri: uri) - Conversation.create!(uri: uri, account: @account, public: %i(public unlisted).include?(visibility_from_audience)) if conversation.nil? + conversation = Conversation.find_by(uri: uri) if conversation.blank? + + if @object['inReplyTo'].blank? && replied_to_status.blank? + if conversation.blank? + conversation = Conversation.create!(uri: uri, root: @object['id'], account: @account, public: %i(public unlisted).include?(visibility_from_audience)) + elsif conversation.root.blank? + conversation.update!(root: @object['id'], account: @account, public: %i(public unlisted).include?(visibility_from_audience)) + end + elsif conversation.blank? + conversation = Conversation.create!(uri: uri, account_id: nil, public: false) + end + + conversation rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotUnique retry end diff --git a/app/lib/activitypub/adapter.rb b/app/lib/activitypub/adapter.rb index b02059430..107b93b44 100644 --- a/app/lib/activitypub/adapter.rb +++ b/app/lib/activitypub/adapter.rb @@ -16,6 +16,7 @@ class ActivityPub::Adapter < ActiveModelSerializers::Adapter::Base require_auth: { 'mp' => 'http://the.monsterpit.net/ns#', 'requireAuth' => 'mp:requireAuth' }, metadata: { 'mp' => 'https://the.monsterpit.net/ns#', 'metadata' => { '@id' => 'mp:metadata', '@type' => '@id' } }, server_metadata: { 'mp' => 'https://the.monsterpit.net/ns#', 'serverMetadata' => { '@id' => 'mp:serverMetadata', '@type' => '@id' } }, + root: { 'mp' => 'https://the.monsterpit.net/ns#', 'root' => { '@id' => 'mp:root', '@type' => '@id' } }, manually_approves_followers: { 'manuallyApprovesFollowers' => 'as:manuallyApprovesFollowers' }, sensitive: { 'sensitive' => 'as:sensitive' }, hashtag: { 'Hashtag' => 'as:Hashtag' }, diff --git a/app/models/conversation.rb b/app/models/conversation.rb index d1674fe4e..e065c34c8 100644 --- a/app/models/conversation.rb +++ b/app/models/conversation.rb @@ -9,6 +9,7 @@ # updated_at :datetime not null # account_id :bigint(8) # public :boolean default(FALSE), not null +# root :string # class Conversation < ApplicationRecord @@ -16,7 +17,7 @@ class Conversation < ApplicationRecord has_many :statuses has_many :mutes, class_name: 'ConversationMute', inverse_of: :conversation, dependent: :destroy - belongs_to :account, inverse_of: :threads + belongs_to :account, inverse_of: :threads, optional: true def local? uri.nil? diff --git a/app/models/status.rb b/app/models/status.rb index 9067f44df..9c061bf85 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -376,6 +376,7 @@ class Status < ApplicationRecord after_destroy_commit :decrement_counter_caches after_create_commit :store_uri, if: :local? + after_create_commit :store_url, if: :local? after_create_commit :update_statistics, if: :local? around_create Mastodon::Snowflake::Callbacks @@ -392,6 +393,7 @@ class Status < ApplicationRecord after_save :set_domain_permissions, if: :local? after_save :set_semiprivate, if: :local? + after_save :set_conversation_root class << self def selectable_visibilities @@ -676,6 +678,10 @@ class Status < ApplicationRecord update_column(:uri, ActivityPub::TagManager.instance.uri_for(self)) if uri.nil? end + def store_url + update_column(:url, ActivityPub::TagManager.instance.url_for(self)) if url.nil? + end + def prepare_contents text&.strip! spoiler_text&.strip! @@ -717,6 +723,10 @@ class Status < ApplicationRecord end end + def set_conversation_root + conversation.update!(root: uri, account_id: account_id, public: distributable?) if !reply && conversation.root.blank? + end + def carried_over_reply_to_account_id if thread.account_id == account_id && thread.reply? thread.in_reply_to_account_id diff --git a/app/models/status_mute.rb b/app/models/status_mute.rb index 223893604..1e01f0278 100644 --- a/app/models/status_mute.rb +++ b/app/models/status_mute.rb @@ -4,7 +4,7 @@ # Table name: status_mutes # # id :bigint(8) not null, primary key -# account_id :integer not null +# account_id :bigint(8) not null # status_id :bigint(8) not null # diff --git a/app/serializers/activitypub/note_serializer.rb b/app/serializers/activitypub/note_serializer.rb index 163f25560..b86ab932e 100644 --- a/app/serializers/activitypub/note_serializer.rb +++ b/app/serializers/activitypub/note_serializer.rb @@ -3,7 +3,7 @@ class ActivityPub::NoteSerializer < ActivityPub::Serializer context_extensions :atom_uri, :conversation, :sensitive, :voters_count, :direct_message - context_extensions :edited, :server_metadata + context_extensions :edited, :server_metadata, :root attributes :id, :type, :summary, :in_reply_to, :published, :url, @@ -11,7 +11,7 @@ class ActivityPub::NoteSerializer < ActivityPub::Serializer :atom_uri, :in_reply_to_atom_uri, :conversation - attribute :updated + attributes :updated, :root attribute :title, key: :name, if: :title_present? attribute :content @@ -50,6 +50,10 @@ class ActivityPub::NoteSerializer < ActivityPub::Serializer end end + def root + object.conversation&.root + end + def summary return Formatter.instance.format(object) if title_present? diff --git a/app/serializers/rest/status_serializer.rb b/app/serializers/rest/status_serializer.rb index dec39ec24..1182d8e80 100644 --- a/app/serializers/rest/status_serializer.rb +++ b/app/serializers/rest/status_serializer.rb @@ -7,7 +7,7 @@ class REST::StatusSerializer < ActiveModel::Serializer :favourites_count # Monsterfork additions - attributes :updated_at, :edited, :nest_level + attributes :updated_at, :edited, :nest_level, :root attribute :favourited, if: :current_user? attribute :reblogged, if: :current_user? @@ -110,6 +110,10 @@ class REST::StatusSerializer < ActiveModel::Serializer ActivityPub::TagManager.instance.url_for(object) end + def root + object.conversation&.root + end + def favourited if instance_options && instance_options[:relationships] instance_options[:relationships].favourites_map[object.id] || false diff --git a/db/migrate/20200816200108_add_root_to_conversations.rb b/db/migrate/20200816200108_add_root_to_conversations.rb new file mode 100644 index 000000000..f45a3b476 --- /dev/null +++ b/db/migrate/20200816200108_add_root_to_conversations.rb @@ -0,0 +1,7 @@ +class AddRootToConversations < ActiveRecord::Migration[5.2] + def change + safety_assured do + add_column :conversations, :root, :string, index: true + end + end +end diff --git a/db/migrate/20200816200239_backfill_root_to_conversations.rb b/db/migrate/20200816200239_backfill_root_to_conversations.rb new file mode 100644 index 000000000..2056e0765 --- /dev/null +++ b/db/migrate/20200816200239_backfill_root_to_conversations.rb @@ -0,0 +1,19 @@ +class BackfillRootToConversations < ActiveRecord::Migration[5.2] + disable_ddl_transaction! + + def up + Rails.logger.info("Adding URI to statuses without one...") + Status.where(uri: nil).or(Status.where(uri: '')).find_each do |status| + status.update(uri: ActivityPub::TagManager.instance.uri_for(status)) + end + + Rails.logger.info('Setting root of all conversations...') + safety_assured do + execute('UPDATE conversations SET root = s.uri FROM (SELECT conversation_id, uri FROM statuses WHERE NOT reply) AS s WHERE conversations.id = s.conversation_id') + end + end + + def down + true + end +end diff --git a/db/migrate/20200817003033_add_defaults_to_conversations.rb b/db/migrate/20200817003033_add_defaults_to_conversations.rb new file mode 100644 index 000000000..fc3c0ceee --- /dev/null +++ b/db/migrate/20200817003033_add_defaults_to_conversations.rb @@ -0,0 +1,8 @@ +class AddDefaultsToConversations < ActiveRecord::Migration[5.2] + def change + safety_assured do + change_column :conversations, :account_id, :bigint, default: nil + change_column :conversations, :root, :string, default: nil + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 7d3ec26d7..5d7bd5262 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2020_08_11_024642) do +ActiveRecord::Schema.define(version: 2020_08_17_003653) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -306,6 +306,7 @@ ActiveRecord::Schema.define(version: 2020_08_11_024642) do t.datetime "updated_at", null: false t.bigint "account_id" t.boolean "public", default: false, null: false + t.string "root" t.index ["account_id"], name: "index_conversations_on_account_id" t.index ["uri"], name: "index_conversations_on_uri", unique: true end @@ -818,7 +819,7 @@ ActiveRecord::Schema.define(version: 2020_08_11_024642) do end create_table "status_mutes", force: :cascade do |t| - t.integer "account_id", null: false + t.bigint "account_id", null: false t.bigint "status_id", null: false t.index ["account_id", "status_id"], name: "index_status_mutes_on_account_id_and_status_id", unique: true t.index ["account_id"], name: "index_status_mutes_on_account_id" -- cgit