about summary refs log tree commit diff
path: root/app/models
diff options
context:
space:
mode:
Diffstat (limited to 'app/models')
-rw-r--r--app/models/account.rb27
-rw-r--r--app/models/feed.rb27
-rw-r--r--app/models/follow.rb28
-rw-r--r--app/models/follow_suggestion.rb50
-rw-r--r--app/models/media_attachment.rb2
-rw-r--r--app/models/status.rb15
-rw-r--r--app/models/subscription.rb29
-rw-r--r--app/models/user.rb1
8 files changed, 54 insertions, 125 deletions
diff --git a/app/models/account.rb b/app/models/account.rb
index 16d654195..0f3d0dda2 100644
--- a/app/models/account.rb
+++ b/app/models/account.rb
@@ -13,12 +13,12 @@ class Account < ApplicationRecord
   validates :username, presence: true, uniqueness: { scope: :domain, case_sensitive: true }, unless: 'local?'
 
   # Avatar upload
-  has_attached_file :avatar, styles: { large: '300x300#', medium: '96x96#', small: '48x48#' }
+  has_attached_file :avatar, styles: { large: '300x300#' }, convert_options: { all: '-strip' }
   validates_attachment_content_type :avatar, content_type: IMAGE_MIME_TYPES
   validates_attachment_size :avatar, less_than: 2.megabytes
 
   # Header upload
-  has_attached_file :header, styles: { medium: '700x335#' }
+  has_attached_file :header, styles: { medium: '700x335#' }, convert_options: { all: '-strip' }
   validates_attachment_content_type :header, content_type: IMAGE_MIME_TYPES
   validates_attachment_size :header, less_than: 2.megabytes
 
@@ -44,8 +44,12 @@ class Account < ApplicationRecord
   has_many :block_relationships, class_name: 'Block', foreign_key: 'account_id', dependent: :destroy
   has_many :blocking, -> { order('blocks.id desc') }, through: :block_relationships, source: :target_account
 
+  # Media
   has_many :media_attachments, dependent: :destroy
 
+  # PuSH subscriptions
+  has_many :subscriptions, dependent: :destroy
+
   pg_search_scope :search_for, against: { username: 'A', domain: 'B' }, using: { tsearch: { prefix: true } }
 
   scope :remote, -> { where.not(domain: nil) }
@@ -66,12 +70,12 @@ class Account < ApplicationRecord
 
   def unfollow!(other_account)
     follow = active_relationships.find_by(target_account: other_account)
-    follow.destroy unless follow.nil?
+    follow&.destroy
   end
 
   def unblock!(other_account)
     block = block_relationships.find_by(target_account: other_account)
-    block.destroy unless block.nil?
+    block&.destroy
   end
 
   def following?(other_account)
@@ -116,7 +120,11 @@ class Account < ApplicationRecord
   end
 
   def avatar_remote_url=(url)
-    self.avatar = URI.parse(url) unless self[:avatar_remote_url] == url
+    parsed_url = URI.parse(url)
+
+    return if !%w(http https).include?(parsed_url.scheme) || self[:avatar_remote_url] == url
+
+    self.avatar              = parsed_url
     self[:avatar_remote_url] = url
   rescue OpenURI::HTTPError => e
     Rails.logger.debug "Error fetching remote avatar: #{e}"
@@ -130,15 +138,6 @@ class Account < ApplicationRecord
     username
   end
 
-  def common_followers_with(other_account)
-    results  = Neography::Rest.new.execute_query('MATCH (a {account_id: {a_id}})-[:follows]->(b)-[:follows]->(c {account_id: {c_id}}) RETURN b.account_id', a_id: id, c_id: other_account.id)
-    ids      = results['data'].map(&:first)
-    accounts = Account.where(id: ids).with_counters.limit(20).map { |a| [a.id, a] }.to_h
-    ids.map { |id| accounts[id] }.compact
-  rescue Neography::NeographyError, Excon::Error::Socket
-    []
-  end
-
   class << self
     def find_local!(username)
       find_remote!(username, nil)
diff --git a/app/models/feed.rb b/app/models/feed.rb
index 45cb923d1..7b181d529 100644
--- a/app/models/feed.rb
+++ b/app/models/feed.rb
@@ -16,8 +16,8 @@ class Feed
       RegenerationWorker.perform_async(@account.id, @type)
       @statuses = Status.send("as_#{@type}_timeline", @account).paginate_by_max_id(limit, nil, nil)
     else
-      status_map = cache(unhydrated)
-      @statuses = unhydrated.map { |id| status_map[id] }.compact
+      status_map = Status.where(id: unhydrated).map { |s| [s.id, s] }.to_h
+      @statuses  = unhydrated.map { |id| status_map[id] }.compact
     end
 
     @statuses
@@ -25,29 +25,6 @@ class Feed
 
   private
 
-  def cache(ids)
-    raw                    = Status.where(id: ids).to_a
-    uncached_ids           = []
-    cached_keys_with_value = Rails.cache.read_multi(*raw.map(&:cache_key))
-
-    raw.each do |status|
-      uncached_ids << status.id unless cached_keys_with_value.key?(status.cache_key)
-    end
-
-    unless uncached_ids.empty?
-      uncached = Status.where(id: uncached_ids).with_includes.map { |s| [s.id, s] }.to_h
-
-      uncached.values.each do |status|
-        Rails.cache.write(status.cache_key, status)
-      end
-    end
-
-    cached = cached_keys_with_value.values.map { |s| [s.id, s] }.to_h
-    cached.merge!(uncached) unless uncached_ids.empty?
-
-    cached
-  end
-
   def key
     FeedManager.instance.key(@type, @account.id)
   end
diff --git a/app/models/follow.rb b/app/models/follow.rb
index cc5bceb75..f83490caa 100644
--- a/app/models/follow.rb
+++ b/app/models/follow.rb
@@ -27,32 +27,4 @@ class Follow < ApplicationRecord
   def title
     destroyed? ? "#{account.acct} is no longer following #{target_account.acct}" : "#{account.acct} started following #{target_account.acct}"
   end
-
-  after_create  :add_to_graph
-  after_destroy :remove_from_graph
-
-  def sync!
-    add_to_graph
-  end
-
-  private
-
-  def add_to_graph
-    neo = Neography::Rest.new
-
-    a = neo.create_unique_node('account_index', 'Account', account_id.to_s, account_id: account_id)
-    b = neo.create_unique_node('account_index', 'Account', target_account_id.to_s, account_id: target_account_id)
-
-    neo.create_unique_relationship('follow_index', 'Follow', id.to_s, 'follows', a, b)
-  rescue Neography::NeographyError, Excon::Error::Socket => e
-    Rails.logger.error e
-  end
-
-  def remove_from_graph
-    neo = Neography::Rest.new
-    rel = neo.get_relationship_index('follow_index', 'Follow', id.to_s)
-    neo.delete_relationship(rel)
-  rescue Neography::NeographyError, Excon::Error::Socket => e
-    Rails.logger.error e
-  end
 end
diff --git a/app/models/follow_suggestion.rb b/app/models/follow_suggestion.rb
deleted file mode 100644
index 2daa40dcb..000000000
--- a/app/models/follow_suggestion.rb
+++ /dev/null
@@ -1,50 +0,0 @@
-# frozen_string_literal: true
-
-class FollowSuggestion
-  class << self
-    def get(for_account_id, limit = 10)
-      neo = Neography::Rest.new
-
-      query = <<END
-MATCH (a {account_id: {id}})-[:follows]->(b)-[:follows]->(c)
-WHERE a <> c
-AND NOT (a)-[:follows]->(c)
-RETURN DISTINCT c.account_id, count(b), c.nodeRank
-ORDER BY count(b) DESC, c.nodeRank DESC
-LIMIT {limit}
-END
-
-      results = neo.execute_query(query, id: for_account_id, limit: limit)
-
-      if results.empty? || results['data'].empty?
-        results = fallback(for_account_id, limit)
-      elsif results['data'].size < limit
-        results['data'] = (results['data'] + fallback(for_account_id, limit - results['data'].size)['data']).uniq
-      end
-
-      account_ids  = results['data'].map(&:first)
-      blocked_ids  = Block.where(account_id: for_account_id).pluck(:target_account_id)
-      accounts_map = Account.where(id: account_ids - blocked_ids).with_counters.map { |a| [a.id, a] }.to_h
-
-      account_ids.map { |id| accounts_map[id] }.compact
-    rescue Neography::NeographyError, Excon::Error::Socket => e
-      Rails.logger.error e
-      return []
-    end
-
-    private
-
-    def fallback(for_account_id, limit)
-      neo = Neography::Rest.new
-
-      query = <<END
-MATCH (b)
-RETURN b.account_id
-ORDER BY b.nodeRank DESC
-LIMIT {limit}
-END
-
-      neo.execute_query(query, id: for_account_id, limit: limit)
-    end
-  end
-end
diff --git a/app/models/media_attachment.rb b/app/models/media_attachment.rb
index bfbf00d76..f1b9b8112 100644
--- a/app/models/media_attachment.rb
+++ b/app/models/media_attachment.rb
@@ -16,6 +16,8 @@ class MediaAttachment < ApplicationRecord
 
   validates :account, presence: true
 
+  default_scope { order('id asc') }
+
   def local?
     remote_url.blank?
   end
diff --git a/app/models/status.rb b/app/models/status.rb
index 3402929bf..f9dcd97e4 100644
--- a/app/models/status.rb
+++ b/app/models/status.rb
@@ -97,7 +97,10 @@ class Status < ApplicationRecord
     end
 
     def as_public_timeline(account = nil)
-      query = joins('LEFT OUTER JOIN accounts ON statuses.account_id = accounts.id').where('accounts.silenced = FALSE')
+      query = joins('LEFT OUTER JOIN accounts ON statuses.account_id = accounts.id')
+              .where('accounts.silenced = FALSE')
+              .where('statuses.in_reply_to_id IS NULL')
+              .where('statuses.reblog_of_id IS NULL')
       query = filter_timeline(query, account) unless account.nil?
       query
     end
@@ -106,6 +109,8 @@ class Status < ApplicationRecord
       query = tag.statuses
                  .joins('LEFT OUTER JOIN accounts ON statuses.account_id = accounts.id')
                  .where('accounts.silenced = FALSE')
+                 .where('statuses.in_reply_to_id IS NULL')
+                 .where('statuses.reblog_of_id IS NULL')
       query = filter_timeline(query, account) unless account.nil?
       query
     end
@@ -123,13 +128,7 @@ class Status < ApplicationRecord
     def filter_timeline(query, account)
       blocked = Block.where(account: account).pluck(:target_account_id)
       return query if blocked.empty?
-
-      query
-        .joins('LEFT OUTER JOIN statuses AS parents ON statuses.in_reply_to_id = parents.id')
-        .joins('LEFT OUTER JOIN statuses AS reblogs ON statuses.reblog_of_id = reblogs.id')
-        .where('statuses.account_id NOT IN (?)', blocked)
-        .where('(parents.id IS NULL OR parents.account_id NOT IN (?))', blocked)
-        .where('(reblogs.id IS NULL OR reblogs.account_id NOT IN (?))', blocked)
+      query.where('statuses.account_id NOT IN (?)', blocked)
     end
   end
 
diff --git a/app/models/subscription.rb b/app/models/subscription.rb
new file mode 100644
index 000000000..497cabb09
--- /dev/null
+++ b/app/models/subscription.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+class Subscription < ApplicationRecord
+  MIN_EXPIRATION = 3600 * 24 * 7
+  MAX_EXPIRATION = 3600 * 24 * 30
+
+  belongs_to :account
+
+  validates :callback_url, presence: true
+  validates :callback_url, uniqueness: { scope: :account_id }
+
+  scope :active, -> { where(confirmed: true).where('expires_at > ?', Time.now.utc) }
+
+  def lease_seconds=(str)
+    self.expires_at = Time.now.utc + [[MIN_EXPIRATION, str.to_i].max, MAX_EXPIRATION].min.seconds
+  end
+
+  def lease_seconds
+    (expires_at - Time.now.utc).to_i
+  end
+
+  before_validation :set_min_expiration
+
+  private
+
+  def set_min_expiration
+    self.lease_seconds = 0 unless expires_at
+  end
+end
diff --git a/app/models/user.rb b/app/models/user.rb
index 366172e9a..423833d47 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -15,6 +15,7 @@ class User < ApplicationRecord
 
   has_settings do |s|
     s.key :notification_emails, defaults: { follow: false, reblog: false, favourite: false, mention: false }
+    s.key :interactions, defaults: { must_be_follower: false, must_be_following: false }
   end
 
   def send_devise_notification(notification, *args)