diff options
-rw-r--r-- | app/lib/activitypub/activity/create.rb | 3 | ||||
-rw-r--r-- | app/lib/feed_manager.rb | 4 | ||||
-rw-r--r-- | app/models/concerns/account_associations.rb | 3 | ||||
-rw-r--r-- | app/models/conversation.rb | 3 | ||||
-rw-r--r-- | app/models/status.rb | 23 | ||||
-rw-r--r-- | app/policies/status_policy.rb | 52 | ||||
-rw-r--r-- | db/migrate/20200721202723_add_account_id_to_conversations.rb | 9 | ||||
-rw-r--r-- | db/migrate/20200721212401_backfill_account_id_on_conversations.rb | 15 | ||||
-rw-r--r-- | db/migrate/20200721221427_add_public_to_conversations.rb | 7 | ||||
-rw-r--r-- | db/migrate/20200721221659_backfill_conversation_visibility.rb | 15 | ||||
-rw-r--r-- | db/schema.rb | 6 |
11 files changed, 119 insertions, 21 deletions
diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb index d2bbd26d5..fbb4624e3 100644 --- a/app/lib/activitypub/activity/create.rb +++ b/app/lib/activitypub/activity/create.rb @@ -383,7 +383,8 @@ class ActivityPub::Activity::Create < ActivityPub::Activity return Conversation.find_by(id: OStatus::TagManager.instance.unique_tag_to_local_id(uri, 'Conversation')) if OStatus::TagManager.instance.local_id?(uri) begin - Conversation.find_or_create_by!(uri: uri) + conversation = Conversation.find_by(uri: uri) + Conversation.create!(uri: uri, account: @account, public: %i(public unlisted).include?(visibility_from_audience)) if conversation.nil? rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotUnique retry end diff --git a/app/lib/feed_manager.rb b/app/lib/feed_manager.rb index 92c125c0f..49db68119 100644 --- a/app/lib/feed_manager.rb +++ b/app/lib/feed_manager.rb @@ -56,6 +56,7 @@ class FeedManager should_filter &&= status.account_id == list.account_id should_filter &&= !list.show_all_replies? should_filter &&= !(list.show_list_replies? && ListAccount.where(list_id: list.id, account_id: status.in_reply_to_account_id).exists?) + should_filter &&= !(list.show_list_replies? && status.conversation&.account_id.present? && ListAccount.where(list_id: list.id, account_id: status.conversation.account_id).exists?) return false if should_filter end @@ -248,11 +249,13 @@ class FeedManager check_for_blocks = crutches[:active_mentions][status.id] || [] check_for_blocks.concat([status.account_id]) + check_for_blocks.concat([status.conversation.account_id]) unless status.conversation&.account_id.nil? check_for_blocks.concat([[status.in_reply_to_account_id]]) if status.reply? if status.reblog? check_for_blocks.concat([status.reblog.account_id]) check_for_blocks.concat(crutches[:active_mentions][status.reblog_of_id] || []) + check_for_blocks.concat([status.reblog.conversation.account_id]) unless status.reblog.conversation&.account_id.nil? check_for_blocks.concat([[status.reblog.in_reply_to_account_id]]) if status.reblog.reply? end @@ -260,6 +263,7 @@ class FeedManager if status.reply? && !status.in_reply_to_account_id.nil? # Filter out if it's a reply should_filter = !crutches[:following][status.in_reply_to_account_id] # and I'm not following the person it's a reply to + should_filter &&= !crutches[:following][status.conversation&.account_id] # and I'm not following the thread owner should_filter &&= receiver_id != status.in_reply_to_account_id # and it's not a reply to me should_filter &&= status.account_id != status.in_reply_to_account_id # and it's not a self-reply diff --git a/app/models/concerns/account_associations.rb b/app/models/concerns/account_associations.rb index cca3a17fa..db7396582 100644 --- a/app/models/concerns/account_associations.rb +++ b/app/models/concerns/account_associations.rb @@ -60,5 +60,8 @@ module AccountAssociations # Hashtags has_and_belongs_to_many :tags has_many :featured_tags, -> { includes(:tag) }, dependent: :destroy, inverse_of: :account + + # Threads + has_many :threads, class_name: 'Conversation', inverse_of: :account, dependent: :nullify end end diff --git a/app/models/conversation.rb b/app/models/conversation.rb index bbe3ada31..d1674fe4e 100644 --- a/app/models/conversation.rb +++ b/app/models/conversation.rb @@ -7,6 +7,8 @@ # uri :string # created_at :datetime not null # updated_at :datetime not null +# account_id :bigint(8) +# public :boolean default(FALSE), not null # class Conversation < ApplicationRecord @@ -14,6 +16,7 @@ class Conversation < ApplicationRecord has_many :statuses has_many :mutes, class_name: 'ConversationMute', inverse_of: :conversation, dependent: :destroy + belongs_to :account, inverse_of: :threads def local? uri.nil? diff --git a/app/models/status.rb b/app/models/status.rb index 120a69715..657fcaca0 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -290,8 +290,7 @@ class Status < ApplicationRecord before_validation :prepare_contents, if: :local? before_validation :set_reblog - before_validation :set_conversation - before_validation :set_visibility + before_validation :set_conversation_perms before_validation :set_local after_create :set_poll_id @@ -537,27 +536,25 @@ class Status < ApplicationRecord update_column(:poll_id, poll.id) unless poll.nil? end - def set_visibility - self.visibility = reblog.visibility if reblog? && visibility.nil? - self.visibility = (account.locked? ? :private : :public) if visibility.nil? - self.visibility = thread.visibility unless thread.nil? || %w(public unlisted).include?(thread.visibility) || ['direct', 'limited', thread.visibility].include?(visibility.to_s) - self.sensitive = false if sensitive.nil? - end - def set_locality self.local_only = marked_local_only? if account.domain.nil? && !attribute_changed?(:local_only) end - def set_conversation + def set_conversation_perms self.thread = thread.reblog if thread&.reblog? - self.reply = !(in_reply_to_id.nil? && thread.nil?) unless reply + self.visibility = reblog.visibility if reblog? && visibility.nil? + self.visibility = (account.locked? ? :private : :public) if visibility.nil? + self.visibility = thread.visibility unless thread.nil? || %w(public unlisted).include?(thread.visibility) || ['direct', 'limited', thread.visibility].include?(visibility.to_s) + self.sensitive = false if sensitive.nil? if reply? && !thread.nil? self.in_reply_to_account_id = carried_over_reply_to_account_id self.conversation_id = thread.conversation_id if conversation_id.nil? elsif conversation_id.nil? - self.conversation = Conversation.new + self.conversation = reply? ? Conversation.new(account_id: nil, public: false) : Conversation.new(account_id: account_id, public: %w(public unlisted).include?(visibility.to_s)) + elsif !reply? && account_id != conversation.account_id + conversation.update!(account_id: account_id, public: %w(public unlisted).include?(visibility.to_s)) end end @@ -616,4 +613,4 @@ class Status < ApplicationRecord end end end -# rubocop:enable Metrics/ClassLength \ No newline at end of file +# rubocop:enable Metrics/ClassLength diff --git a/app/policies/status_policy.rb b/app/policies/status_policy.rb index 22e985b03..bec58c39f 100644 --- a/app/policies/status_policy.rb +++ b/app/policies/status_policy.rb @@ -18,9 +18,9 @@ class StatusPolicy < ApplicationPolicy if requires_mention? owned? || mention_exists? elsif private? - owned? || (following_author? && following_parent_author?) || mention_exists? + owned? || following_owners? || mention_exists? else - current_account.nil? || !(author_blocking? || parent_author_blocking? || author_blocking_domain? || parent_author_blocking_domain?) + current_account.nil? || !blocked_by_owners? end end @@ -53,7 +53,7 @@ class StatusPolicy < ApplicationPolicy end def private? - record.private_visibility? + record.private_visibility? || !public_conversation? end def mention_exists? @@ -78,6 +78,12 @@ class StatusPolicy < ApplicationPolicy parent_author.domain_blocking?(current_account.domain) end + def conversation_author_blocking_domain? + return false if current_account.nil? || current_account.domain.nil? || conversation_owner.nil? + + conversation_owner.domain_blocking?(current_account.domain) + end + def blocking_author? return false if current_account.nil? @@ -96,6 +102,19 @@ class StatusPolicy < ApplicationPolicy @preloaded_relations[:blocked_by] ? @preloaded_relations[:blocked_by][parent_author.id] : parent_author.blocking?(current_account) end + def conversation_author_blocking? + return public_conversation? if conversation_owner.nil? + + @preloaded_relations[:blocked_by] ? @preloaded_relations[:blocked_by][conversation_owner.id] : conversation_owner.blocking?(current_account) + end + + def blocked_by_owners? + return (author_blocking? || author_blocking_domain?) if conversation_owner&.id == author.id && parent_author&.id == author.id + return true if conversation_author_blocking? || parent_author_blocking? || author_blocking? + + conversation_author_blocking_domain? || parent_author_blocking_domain? || author_blocking_domain? + end + def following_author? return false if current_account.nil? @@ -109,14 +128,31 @@ class StatusPolicy < ApplicationPolicy @preloaded_relations[:following] ? @preloaded_relations[:following][parent_author.id] : current_account.following?(parent_author) end + def following_conversation_owner? + return false if current_account.nil? + return public_conversation? if conversation_owner.nil? + + @preloaded_relations[:following] ? @preloaded_relations[:following][conversation_owner.id] : current_account.following?(conversation_owner) + end + + def following_owners? + return following_author? if conversation_owner&.id == author.id && parent_author&.id == author.id + + following_conversation_owner? && following_parent_author? && following_author? + end + def author - record.account + @author ||= record.account end def parent_author - record.in_reply_to_account + @parent_author ||= record.in_reply_to_account end - + + def conversation_owner + @conversation_owner ||= record.conversation&.account + end + def local_only? record.local_only? end @@ -124,4 +160,8 @@ class StatusPolicy < ApplicationPolicy def published? record.published? end + + def public_conversation? + @public_conversation ||= (record.conversation&.public? || false) + end end diff --git a/db/migrate/20200721202723_add_account_id_to_conversations.rb b/db/migrate/20200721202723_add_account_id_to_conversations.rb new file mode 100644 index 000000000..afddf4823 --- /dev/null +++ b/db/migrate/20200721202723_add_account_id_to_conversations.rb @@ -0,0 +1,9 @@ +class AddAccountIdToConversations < ActiveRecord::Migration[5.2] + disable_ddl_transaction! + + def change + safety_assured do + add_reference :conversations, :account, foreign_key: true, index: {algorithm: :concurrently} + end + end +end diff --git a/db/migrate/20200721212401_backfill_account_id_on_conversations.rb b/db/migrate/20200721212401_backfill_account_id_on_conversations.rb new file mode 100644 index 000000000..595fd8e52 --- /dev/null +++ b/db/migrate/20200721212401_backfill_account_id_on_conversations.rb @@ -0,0 +1,15 @@ +class BackfillAccountIdOnConversations < ActiveRecord::Migration[5.2] + disable_ddl_transaction! + + def up + Rails.logger.info('Backfilling owners of conversation threads...') + safety_assured do + Conversation.left_outer_joins(:statuses).where(statuses: { id: nil }).in_batches.destroy_all + execute('UPDATE conversations SET account_id = s.account_id FROM (SELECT account_id, conversation_id 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/20200721221427_add_public_to_conversations.rb b/db/migrate/20200721221427_add_public_to_conversations.rb new file mode 100644 index 000000000..392bd7418 --- /dev/null +++ b/db/migrate/20200721221427_add_public_to_conversations.rb @@ -0,0 +1,7 @@ +class AddPublicToConversations < ActiveRecord::Migration[5.2] + def change + safety_assured do + add_column :conversations, :public, :boolean, default: false, null: false + end + end +end diff --git a/db/migrate/20200721221659_backfill_conversation_visibility.rb b/db/migrate/20200721221659_backfill_conversation_visibility.rb new file mode 100644 index 000000000..93394b825 --- /dev/null +++ b/db/migrate/20200721221659_backfill_conversation_visibility.rb @@ -0,0 +1,15 @@ +class BackfillConversationVisibility < ActiveRecord::Migration[5.2] + disable_ddl_transaction! + + def up + Rails.logger.info('Backfilling thread visibility...') + + safety_assured do + execute('UPDATE conversations SET public = true FROM (SELECT account_id, conversation_id FROM statuses WHERE NOT reply AND visibility IN (0, 1)) AS s WHERE conversations.id = s.conversation_id') + end + end + + def down + true + end +end diff --git a/db/schema.rb b/db/schema.rb index 674ff8ab6..901c13c76 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_07_21_195456) do +ActiveRecord::Schema.define(version: 2020_07_21_221659) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -286,6 +286,9 @@ ActiveRecord::Schema.define(version: 2020_07_21_195456) do t.string "uri" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.bigint "account_id" + t.boolean "public", default: false, null: false + t.index ["account_id"], name: "index_conversations_on_account_id" t.index ["uri"], name: "index_conversations_on_uri", unique: true end @@ -964,6 +967,7 @@ ActiveRecord::Schema.define(version: 2020_07_21_195456) do add_foreign_key "bookmarks", "statuses", on_delete: :cascade add_foreign_key "conversation_mutes", "accounts", name: "fk_225b4212bb", on_delete: :cascade add_foreign_key "conversation_mutes", "conversations", on_delete: :cascade + add_foreign_key "conversations", "accounts" add_foreign_key "custom_filters", "accounts", on_delete: :cascade add_foreign_key "devices", "accounts", on_delete: :cascade add_foreign_key "devices", "oauth_access_tokens", column: "access_token_id", on_delete: :cascade |