about summary refs log tree commit diff
path: root/app/models
diff options
context:
space:
mode:
authorFire Demon <firedemon@creature.cafe>2020-07-25 23:19:56 -0500
committerFire Demon <firedemon@creature.cafe>2020-08-30 05:45:15 -0500
commit70763ef0ea6cb6b997ac144d8b4d855b16fb838c (patch)
tree22188b8531085b35652f5459fc04e84384d1a231 /app/models
parent3c6057034eea75f1992c9d2f2b2c2e401858bdc7 (diff)
[Feature, Privacy] Add base support for per-domain post privacy
Diffstat (limited to 'app/models')
-rw-r--r--app/models/account.rb23
-rw-r--r--app/models/account_domain_permission.rb61
-rw-r--r--app/models/concerns/account_associations.rb3
-rw-r--r--app/models/status.rb21
-rw-r--r--app/models/status_domain_permission.rb61
5 files changed, 168 insertions, 1 deletions
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