From 70763ef0ea6cb6b997ac144d8b4d855b16fb838c Mon Sep 17 00:00:00 2001 From: Fire Demon Date: Sat, 25 Jul 2020 23:19:56 -0500 Subject: [Feature, Privacy] Add base support for per-domain post privacy --- app/models/account.rb | 23 +++++++++++ app/models/account_domain_permission.rb | 61 +++++++++++++++++++++++++++++ app/models/concerns/account_associations.rb | 3 ++ app/models/status.rb | 21 +++++++++- app/models/status_domain_permission.rb | 61 +++++++++++++++++++++++++++++ 5 files changed, 168 insertions(+), 1 deletion(-) create mode 100644 app/models/account_domain_permission.rb create mode 100644 app/models/status_domain_permission.rb (limited to 'app/models') diff --git a/app/models/account.rb b/app/models/account.rb index 8b384f212..301dc6c45 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -360,6 +360,29 @@ class Account < ApplicationRecord shared_inbox_url.presence || inbox_url end + def max_visibility_for_domain(domain) + return 'public' if domain.blank? + + domain_permissions.find_by(domain: domain)&.visibility || 'public' + end + + def visibility_for_domain(domain) + v = visibility.to_s + + case max_visibility_for_domain(domain) + when 'public' + v + when 'unlisted' + v == 'public' ? 'unlisted' : v + when 'private' + %w(public unlisted).include?(v) ? 'private' : v + when 'direct' + 'direct' + else + v != 'direct' ? 'limited' : 'direct' + end + end + class Field < ActiveModelSerializers::Model attributes :name, :value, :verified_at, :account, :errors diff --git a/app/models/account_domain_permission.rb b/app/models/account_domain_permission.rb new file mode 100644 index 000000000..ffa9cbbec --- /dev/null +++ b/app/models/account_domain_permission.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true +# == Schema Information +# +# Table name: account_domain_permissions +# +# id :bigint(8) not null, primary key +# account_id :bigint(8) not null +# domain :string default(""), not null +# visibility :integer default("public"), not null +# + +class AccountDomainPermission < ApplicationRecord + include Paginable + + belongs_to :account, inverse_of: :domain_permissions + enum visibility: [:public, :unlisted, :private, :direct, :limited], _suffix: :visibility + + class << self + def create_by_domains(permissions_list) + Array(permissions_list).map(&method(:normalize)).map do |permissions| + where(**permissions).first_or_create + end + end + + def create_by_domains!(permissions_list) + Array(permissions_list).map(&method(:normalize)).map do |permissions| + where(**permissions).first_or_create! + end + end + + def create_or_update(domain_permissions) + domain_permissions = normalize(domain_permissions) + permissions = find_by(domain: domain_permissions[:domain]) + if permissions.present? + permissions.update(**domain_permissions) + else + create(**domain_permissions) + end + permissions + end + + def create_or_update!(domain_permissions) + domain_permissions = normalize(domain_permissions) + permissions = find_by(domain: domain_permissions[:domain]) + if permissions.present? + permissions.update!(**domain_permissions) + else + create!(**domain_permissions) + end + permissions + end + + private + + def normalize(hash) + hash.symbolize_keys! + hash[:domain] = hash[:domain].strip.downcase + hash.compact + end + end +end diff --git a/app/models/concerns/account_associations.rb b/app/models/concerns/account_associations.rb index db7396582..10eaa4874 100644 --- a/app/models/concerns/account_associations.rb +++ b/app/models/concerns/account_associations.rb @@ -63,5 +63,8 @@ module AccountAssociations # Threads has_many :threads, class_name: 'Conversation', inverse_of: :account, dependent: :nullify + + # Domain permissions + has_many :domain_permissions, class_name: 'AccountDomainPermission', inverse_of: :account, dependent: :destroy end end diff --git a/app/models/status.rb b/app/models/status.rb index e8ea2f70a..4806f81f4 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -74,6 +74,7 @@ class Status < ApplicationRecord has_many :inlined_attachments, class_name: 'InlineMediaAttachment', inverse_of: :status, dependent: :destroy has_many :mutes, class_name: 'StatusMute', inverse_of: :status, dependent: :destroy belongs_to :conversation_mute, primary_key: 'conversation_id', foreign_key: 'conversation_id', inverse_of: :conversation, dependent: :destroy, optional: true + has_many :domain_permissions, class_name: 'StatusDomainPermission', inverse_of: :status, dependent: :destroy has_and_belongs_to_many :tags has_and_belongs_to_many :preview_cards @@ -154,6 +155,7 @@ class Status < ApplicationRecord thread: { account: :account_stat } delegate :domain, to: :account, prefix: true + delegate :max_visibility_for_domain, to: :account REAL_TIME_WINDOW = 6.hours @@ -280,6 +282,23 @@ class Status < ApplicationRecord update_status_stat!(key => [public_send(key) - 1, 0].max) end + def visibility_for_domain(domain) + v = domain_permissions.find_by(domain: domain)&.visibility || visibility.to_s + + case max_visibility_for_domain(domain) + when 'public' + v + when 'unlisted' + v == 'public' ? 'unlisted' : v + when 'private' + %w(public unlisted).include?(v) ? 'private' : v + when 'direct' + 'direct' + else + v != 'direct' ? 'limited' : 'direct' + end + end + after_create_commit :increment_counter_caches after_destroy_commit :decrement_counter_caches @@ -544,7 +563,7 @@ class Status < ApplicationRecord if account.domain.nil? && !attribute_changed?(:local_only) self.local_only = true if marked_local_only? end - self.local_only = true if thread&.local_only? && self.local_only.nil? + self.local_only = true if thread&.local_only? && local_only.nil? self.local_only = reblog.local_only if reblog? end diff --git a/app/models/status_domain_permission.rb b/app/models/status_domain_permission.rb new file mode 100644 index 000000000..f9e5dc55e --- /dev/null +++ b/app/models/status_domain_permission.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true +# == Schema Information +# +# Table name: status_domain_permissions +# +# id :bigint(8) not null, primary key +# status_id :bigint(8) not null +# domain :string default(""), not null +# visibility :integer default("public"), not null +# + +class StatusDomainPermission < ApplicationRecord + include Paginable + + belongs_to :status, inverse_of: :domain_permissions + enum visibility: [:public, :unlisted, :private, :direct, :limited], _suffix: :visibility + + class << self + def create_by_domains(permissions_list) + Array(permissions_list).map(&method(:normalize)).map do |permissions| + where(**permissions).first_or_create + end + end + + def create_by_domains!(permissions_list) + Array(permissions_list).map(&method(:normalize)).map do |permissions| + where(**permissions).first_or_create! + end + end + + def create_or_update(domain_permissions) + domain_permissions = normalize(domain_permissions) + permissions = find_by(domain: domain_permissions[:domain]) + if permissions.present? + permissions.update(**domain_permissions) + else + create(**domain_permissions) + end + permissions + end + + def create_or_update!(domain_permissions) + domain_permissions = normalize(domain_permissions) + permissions = find_by(domain: domain_permissions[:domain]) + if permissions.present? + permissions.update!(**domain_permissions) + else + create!(**domain_permissions) + end + permissions + end + + private + + def normalize(hash) + hash.symbolize_keys! + hash[:domain] = hash[:domain].strip.downcase + hash.compact + end + end +end -- cgit