Commiting

This commit is contained in:
mgabdev
2020-11-15 12:48:32 -06:00
parent 62515bbaee
commit fb612f60c8
1011 changed files with 3507 additions and 49604 deletions

View File

@@ -35,7 +35,7 @@
# outbox_url :string default(""), not null
# shared_inbox_url :string default(""), not null
# followers_url :string default(""), not null
# protocol :integer default("ostatus"), not null
# protocol :integer default(0), not null
# memorial :boolean default(FALSE), not null
# moved_to_account_id :bigint(8)
# featured_collection_url :string
@@ -67,8 +67,6 @@ class Account < ApplicationRecord
include AccountCounters
include DomainNormalizable
enum protocol: [:ostatus, :activitypub]
validates :username, presence: true
# Remote user validations
@@ -85,7 +83,6 @@ class Account < ApplicationRecord
scope :remote, -> { where.not(domain: nil) }
scope :local, -> { where(domain: nil) }
scope :expiring, ->(time) { remote.where.not(subscription_expires_at: nil).where('subscription_expires_at < ?', time) }
scope :partitioned, -> { order(Arel.sql('row_number() over (partition by domain)')) }
scope :silenced, -> { where.not(silenced_at: nil) }
scope :suspended, -> { where.not(suspended_at: nil) }
@@ -110,13 +107,11 @@ class Account < ApplicationRecord
:current_sign_in_at,
:confirmed?,
:approved?,
:pending?,
:admin?,
:moderator?,
:staff?,
:locale,
:hides_network?,
:shows_application?,
to: :user,
prefix: true,
allow_nil: true
@@ -153,23 +148,6 @@ class Account < ApplicationRecord
Follow.where(target_account_id: id).count
end
def to_webfinger_s
"acct:#{local_username_and_domain}"
end
def subscribed?
subscription_expires_at.present?
end
def possibly_stale?
last_webfingered_at.nil? || last_webfingered_at <= 1.day.ago
end
def refresh!
return if local?
ResolveAccountService.new.call(acct)
end
def silenced?
silenced_at.present?
end
@@ -247,10 +225,6 @@ class Account < ApplicationRecord
end
end
def also_known_as
self[:also_known_as] || []
end
def fields
(self[:fields] || []).map { |f| Field.new(self, f) }
end
@@ -307,17 +281,11 @@ class Account < ApplicationRecord
(['RSA'] + [modulus, exponent].map { |n| Base64.urlsafe_encode64(n) }).join('.')
end
def subscription(webhook_url)
@subscription ||= OStatus2::Subscription.new(remote_url, secret: secret, webhook: webhook_url, hub: hub_url)
end
def save_with_optional_media!
save!
rescue ActiveRecord::RecordInvalid
self.avatar = nil
self.header = nil
self[:avatar_remote_url] = ''
self[:header_remote_url] = ''
save!
end
@@ -400,11 +368,6 @@ class Account < ApplicationRecord
reorder(nil).pluck(Arel.sql('distinct accounts.domain'))
end
def inboxes
urls = reorder(nil).where(protocol: :activitypub).pluck(Arel.sql("distinct coalesce(nullif(accounts.shared_inbox_url, ''), accounts.inbox_url)"))
DeliveryFailureTracker.filter(urls)
end
def search_for(terms, limit = 10, offset = 0, options = {})
textsearch, query = generate_query_for_search(terms)
@onlyVerified = options[:onlyVerified] || false

View File

@@ -1,32 +0,0 @@
# frozen_string_literal: true
# == Schema Information
#
# Table name: account_domain_blocks
#
# id :bigint(8) not null, primary key
# domain :string
# created_at :datetime not null
# updated_at :datetime not null
# account_id :bigint(8)
#
class AccountDomainBlock < ApplicationRecord
include Paginable
include DomainNormalizable
belongs_to :account
validates :domain, presence: true, uniqueness: { scope: :account_id }
after_commit :remove_blocking_cache
after_commit :remove_relationship_cache
private
def remove_blocking_cache
Rails.cache.delete("exclude_domains_for:#{account_id}")
end
def remove_relationship_cache
Rails.cache.delete_matched("relationship:#{account_id}:*")
end
end

View File

@@ -1,46 +0,0 @@
# frozen_string_literal: true
# == Schema Information
#
# Table name: account_identity_proofs
#
# id :bigint(8) not null, primary key
# account_id :bigint(8)
# provider :string default(""), not null
# provider_username :string default(""), not null
# token :text default(""), not null
# verified :boolean default(FALSE), not null
# live :boolean default(FALSE), not null
# created_at :datetime not null
# updated_at :datetime not null
#
class AccountIdentityProof < ApplicationRecord
belongs_to :account
validates :provider, inclusion: { in: ProofProvider::SUPPORTED_PROVIDERS }
validates :provider_username, format: { with: /\A[a-z0-9_]+\z/i }, length: { minimum: 2, maximum: 30 }
validates :provider_username, uniqueness: { scope: [:account_id, :provider] }
validates :token, format: { with: /\A[a-f0-9]+\z/ }, length: { maximum: 66 }
validate :validate_with_provider, if: :token_changed?
scope :active, -> { where(verified: true, live: true) }
after_commit :queue_worker, if: :saved_change_to_token?
delegate :refresh!, :on_success_path, :badge, to: :provider_instance
def provider_instance
@provider_instance ||= ProofProvider.find(provider, self)
end
private
def queue_worker
provider_instance.worker_class.perform_async(id)
end
def validate_with_provider
provider_instance.validate!
end
end

View File

@@ -1,27 +0,0 @@
# frozen_string_literal: true
# == Schema Information
#
# Table name: account_pins
#
# id :bigint(8) not null, primary key
# account_id :bigint(8)
# target_account_id :bigint(8)
# created_at :datetime not null
# updated_at :datetime not null
#
class AccountPin < ApplicationRecord
include Paginable
include RelationshipCacheable
belongs_to :account
belongs_to :target_account, class_name: 'Account'
validate :validate_follow_relationship
private
def validate_follow_relationship
errors.add(:base, I18n.t('accounts.pin_errors.following')) unless account.following?(target_account)
end
end

View File

@@ -25,7 +25,6 @@ class Block < ApplicationRecord
end
after_commit :remove_blocking_cache
before_validation :set_uri, only: :create
private
@@ -33,8 +32,4 @@ class Block < ApplicationRecord
Rails.cache.delete("exclude_account_ids_for:#{account_id}")
Rails.cache.delete("exclude_account_ids_for:#{target_account_id}")
end
def set_uri
self.uri = ActivityPub::TagManager.instance.generate_uri_for(self) if uri.nil?
end
end

View File

@@ -7,16 +7,11 @@ module AccountAssociations
# Local users
has_one :user, inverse_of: :account, dependent: :destroy
# Identity proofs
has_many :identity_proofs, class_name: 'AccountIdentityProof', dependent: :destroy, inverse_of: :account
# Timelines
has_many :stream_entries, inverse_of: :account, dependent: :destroy
has_many :statuses, inverse_of: :account, dependent: :destroy
has_many :favourites, inverse_of: :account, dependent: :destroy
has_many :mentions, inverse_of: :account, dependent: :destroy
has_many :notifications, inverse_of: :account, dependent: :destroy
has_many :conversations, class_name: 'AccountConversation', dependent: :destroy, inverse_of: :account
has_many :scheduled_statuses, inverse_of: :account, dependent: :destroy
# Pinned statuses
@@ -27,10 +22,6 @@ module AccountAssociations
has_many :status_pins, inverse_of: :account, dependent: :destroy
has_many :pinned_statuses, -> { reorder('status_pins.created_at DESC') }, through: :status_pins, class_name: 'Status', source: :status
# Endorsements
has_many :account_pins, inverse_of: :account, dependent: :destroy
has_many :endorsed_accounts, through: :account_pins, class_name: 'Account', source: :target_account
# Media
has_many :media_attachments, dependent: :destroy
has_many :polls, dependent: :destroy
@@ -63,7 +54,6 @@ module AccountAssociations
# Hashtags
has_and_belongs_to_many :tags
has_many :featured_tags, -> { includes(:tag) }, dependent: :destroy, inverse_of: :account
# Billing
has_many :transactions, class_name: 'Transaction', dependent: :destroy, inverse_of: :account

View File

@@ -8,10 +8,6 @@ module AccountFinderConcern
find_local(username) || raise(ActiveRecord::RecordNotFound)
end
def find_remote!(username, domain)
find_remote(username, domain) || raise(ActiveRecord::RecordNotFound)
end
def find_acct!(acct)
find_acct(acct) || raise(ActiveRecord::RecordNotFound)
end
@@ -21,25 +17,24 @@ module AccountFinderConcern
end
def find_local(username)
find_remote(username, nil)
end
def find_remote(username, domain)
AccountFinder.new(username, domain).account
find_now(username)
end
def find_acct(acct)
username, domain = acct.split("@")
find_remote(username, domain)
find_now(username)
end
def find_now(username)
AccountFinder.new(username).account
end
end
class AccountFinder
attr_reader :username, :domain
attr_reader :username
def initialize(username, domain)
def initialize(username)
@username = username
@domain = domain
end
def account
@@ -65,11 +60,7 @@ module AccountFinderConcern
end
def matching_domain
if domain.nil?
Account.where(domain: nil)
else
Account.where(Account.arel_table[:domain].lower.eq domain.to_s.downcase)
end
Account.where(domain: nil)
end
end
end

View File

@@ -40,20 +40,6 @@ module AccountInteractions
end
end
def endorsed_map(target_account_ids, account_id)
follow_mapping(AccountPin.where(account_id: account_id, target_account_id: target_account_ids), :target_account_id)
end
def domain_blocking_map(target_account_ids, account_id)
accounts_map = Account.where(id: target_account_ids).select('id, domain').each_with_object({}) { |a, h| h[a.id] = a.domain }
blocked_domains = domain_blocking_map_by_domain(accounts_map.values.compact, account_id)
accounts_map.reduce({}) { |h, (id, domain)| h.merge(id => blocked_domains[domain]) }
end
def domain_blocking_map_by_domain(target_domains, account_id)
follow_mapping(AccountDomainBlock.where(account_id: account_id, domain: target_domains), :domain)
end
private
def follow_mapping(query, field)
@@ -85,8 +71,6 @@ module AccountInteractions
has_many :muting, -> { order('mutes.id desc') }, through: :mute_relationships, source: :target_account
has_many :muted_by_relationships, class_name: 'Mute', foreign_key: :target_account_id, dependent: :destroy
has_many :muted_by, -> { order('mutes.id desc') }, through: :muted_by_relationships, source: :account
has_many :conversation_mutes, dependent: :destroy
has_many :domain_blocks, class_name: 'AccountDomainBlock', dependent: :destroy
end
def follow!(other_account, reblogs: nil, uri: nil)
@@ -120,14 +104,6 @@ module AccountInteractions
mute
end
def mute_conversation!(conversation)
conversation_mutes.find_or_create_by!(conversation: conversation)
end
def block_domain!(other_domain)
domain_blocks.find_or_create_by!(domain: other_domain)
end
def unfollow!(other_account)
follow = active_relationships.find_by(target_account: other_account)
follow&.destroy
@@ -143,16 +119,6 @@ module AccountInteractions
mute&.destroy
end
def unmute_conversation!(conversation)
mute = conversation_mutes.find_by(conversation: conversation)
mute&.destroy!
end
def unblock_domain!(other_domain)
block = domain_blocks.find_by(domain: other_domain)
block&.destroy
end
def following?(other_account)
active_relationships.where(target_account: other_account).exists?
end
@@ -161,18 +127,10 @@ module AccountInteractions
block_relationships.where(target_account: other_account).exists?
end
def domain_blocking?(other_domain)
domain_blocks.where(domain: other_domain).exists?
end
def muting?(other_account)
mute_relationships.where(target_account: other_account).exists?
end
def muting_conversation?(conversation)
conversation_mutes.where(conversation: conversation).exists?
end
def muting_notifications?(other_account)
mute_relationships.where(target_account: other_account, hide_notifications: true).exists?
end
@@ -201,10 +159,6 @@ module AccountInteractions
status_pins.where(status: status).exists?
end
def endorsed?(account)
account_pins.where(target_account: account).exists?
end
def followers_for_local_distribution
followers.local
.joins(:user)

View File

@@ -44,9 +44,9 @@ module Omniauthable
# verified email. If no verified email was provided or the user already
# exists, we assign a temporary email and ask the user to verify it on
# the next step via Auth::ConfirmationsController.finish_signup
# : TODO : remove, no sign up from app. only website
user = User.new(user_params_from_auth(auth))
user.account.avatar_remote_url = auth.info.image if auth.info.image =~ /\A#{URI.regexp(%w(http https))}\z/
user.skip_confirmation!
user.save!
user

View File

@@ -122,7 +122,6 @@ module StatusThreadingConcern
blocked_by: Account.blocked_by_map(account_ids, account.id),
muting: Account.muting_map(account_ids, account.id),
following: Account.following_map(account_ids, account.id),
domain_blocking_by_domain: Account.domain_blocking_map_by_domain(domains, account.id),
}
end

View File

@@ -1,43 +0,0 @@
# frozen_string_literal: true
module Streamable
extend ActiveSupport::Concern
included do
has_one :stream_entry, as: :activity
after_create do
account.stream_entries.create!(activity: self, hidden: hidden?) if needs_stream_entry?
end
end
def title
super
end
def content
title
end
def target
super
end
def object_type
:activity
end
def thread
super
end
def hidden?
false
end
private
def needs_stream_entry?
account.local?
end
end

View File

@@ -1,14 +0,0 @@
# frozen_string_literal: true
# == Schema Information
#
# Table name: conversation_mutes
#
# id :bigint(8) not null, primary key
# conversation_id :bigint(8) not null
# account_id :bigint(8) not null
#
class ConversationMute < ApplicationRecord
belongs_to :account
belongs_to :conversation
end

View File

@@ -12,7 +12,6 @@
# image_updated_at :datetime
# created_at :datetime not null
# updated_at :datetime not null
# disabled :boolean default(FALSE), not null
# uri :string
# image_remote_url :string
# visible_in_picker :boolean default(TRUE), not null

View File

@@ -1,42 +0,0 @@
# frozen_string_literal: true
# == Schema Information
#
# Table name: domain_blocks
#
# id :bigint(8) not null, primary key
# domain :string default(""), not null
# created_at :datetime not null
# updated_at :datetime not null
# severity :integer default("silence")
# reject_media :boolean default(FALSE), not null
# reject_reports :boolean default(FALSE), not null
#
class DomainBlock < ApplicationRecord
include DomainNormalizable
enum severity: [:silence, :suspend, :noop]
validates :domain, presence: true, uniqueness: true
has_many :accounts, foreign_key: :domain, primary_key: :domain
delegate :count, to: :accounts, prefix: true
scope :matches_domain, ->(value) { where(arel_table[:domain].matches("%#{value}%")) }
def self.blocked?(domain)
where(domain: domain, severity: :suspend).exists?
end
def stricter_than?(other_block)
return true if suspend?
return false if other_block.suspend? && (silence? || noop?)
return false if other_block.silence? && noop?
(reject_media || !other_block.reject_media) && (reject_reports || !other_block.reject_reports)
end
def affected_accounts_count
scope = suspend? ? accounts.where(suspended_at: created_at) : accounts.where(silenced_at: created_at)
scope.count
end
end

View File

@@ -39,14 +39,6 @@ class Export
end
end
def to_blocked_domains_csv
CSV.generate do |csv|
account.domain_blocks.pluck(:domain).each do |domain|
csv << [domain]
end
end
end
def total_storage
account.media_attachments.sum(:file_file_size)
end
@@ -75,10 +67,6 @@ class Export
account.muting.count
end
def total_domain_blocks
account.domain_blocks.count
end
private
def to_csv(accounts)

View File

@@ -1,47 +0,0 @@
# frozen_string_literal: true
# == Schema Information
#
# Table name: featured_tags
#
# id :bigint(8) not null, primary key
# account_id :bigint(8)
# tag_id :bigint(8)
# statuses_count :bigint(8) default(0), not null
# last_status_at :datetime
# created_at :datetime not null
# updated_at :datetime not null
#
class FeaturedTag < ApplicationRecord
belongs_to :account, inverse_of: :featured_tags, required: true
belongs_to :tag, inverse_of: :featured_tags, required: true
delegate :name, to: :tag, allow_nil: true
validates_associated :tag, on: :create
validates :name, presence: true, on: :create
validate :validate_featured_tags_limit, on: :create
def name=(str)
self.tag = Tag.find_or_initialize_by(name: str.strip.delete('#').mb_chars.downcase.to_s)
end
def increment(timestamp)
update(statuses_count: statuses_count + 1, last_status_at: timestamp)
end
def decrement(deleted_status_id)
update(statuses_count: [0, statuses_count - 1].max, last_status_at: account.statuses.where(visibility: %i(public unlisted)).tagged_with(tag).where.not(id: deleted_status_id).select(:created_at).first&.created_at)
end
def reset_data
self.statuses_count = account.statuses.where(visibility: %i(public unlisted)).tagged_with(tag).count
self.last_status_at = account.statuses.where(visibility: %i(public unlisted)).tagged_with(tag).select(:created_at).first&.created_at
end
private
def validate_featured_tags_limit
errors.add(:base, I18n.t('featured_tags.errors.limit')) if account.featured_tags.count >= 10
end
end

View File

@@ -31,25 +31,15 @@ class Follow < ApplicationRecord
end
def revoke_request!
FollowRequest.create!(account: account, target_account: target_account, show_reblogs: show_reblogs, uri: uri)
FollowRequest.create!(account: account, target_account: target_account)
destroy!
end
before_validation :set_uri, only: :create
after_create :increment_cache_counters
after_destroy :remove_endorsements
after_destroy :decrement_cache_counters
private
def set_uri
self.uri = ActivityPub::TagManager.instance.generate_uri_for(self) if uri.nil?
end
def remove_endorsements
AccountPin.where(target_account_id: target_account_id, account_id: account_id).delete_all
end
def increment_cache_counters
account&.increment_count!(:following_count)
target_account&.increment_count!(:followers_count)

View File

@@ -25,7 +25,7 @@ class FollowRequest < ApplicationRecord
validates_with FollowLimitValidator, on: :create
def authorize!
account.follow!(target_account, reblogs: show_reblogs, uri: uri)
account.follow!(target_account)
MergeWorker.perform_async(target_account.id, account.id) if account.local?
destroy!
end
@@ -33,14 +33,6 @@ class FollowRequest < ApplicationRecord
alias reject! destroy!
def local?
false # Force uri_for to use uri attribute
end
before_validation :set_uri, only: :create
private
def set_uri
self.uri = ActivityPub::TagManager.instance.generate_uri_for(self) if uri.nil?
false
end
end

View File

@@ -12,8 +12,6 @@ class Form::AccountBatch
unfollow!
when 'remove_from_followers'
remove_from_followers!
when 'block_domains'
block_domains!
when 'approve'
approve!
when 'reject'
@@ -35,12 +33,6 @@ class Form::AccountBatch
end
end
def block_domains!
AfterAccountDomainBlockWorker.push_bulk(account_domains) do |domain|
[current_account.id, domain]
end
end
def account_domains
accounts.pluck(Arel.sql('distinct domain')).compact
end

View File

@@ -9,47 +9,25 @@ class Form::AdminSettings
site_title
site_short_description
site_description
site_extended_description
site_terms
registrations_mode
closed_registrations_message
open_deletion
timeline_preview
show_staff_badge
bootstrap_timeline_accounts
theme
min_invite_role
activity_api_enabled
peers_api_enabled
show_known_fediverse_at_about_page
preview_sensitive_media
profile_directory
thumbnail
hero
mascot
).freeze
BOOLEAN_KEYS = %i(
open_deletion
timeline_preview
show_staff_badge
activity_api_enabled
peers_api_enabled
show_known_fediverse_at_about_page
preview_sensitive_media
profile_directory
).freeze
UPLOAD_KEYS = %i(
thumbnail
hero
mascot
).freeze
attr_accessor(*KEYS)
validates :site_short_description, :site_description, html: { wrap_with: :p }
validates :site_extended_description, :site_terms, :closed_registrations_message, html: true
validates :registrations_mode, inclusion: { in: %w(open approved none) }
validates :min_invite_role, inclusion: { in: %w(disabled user moderator admin) }
validates :site_contact_email, :site_contact_username, presence: true

View File

@@ -20,6 +20,6 @@ class Form::Migration
private
def set_account
self.account = (ResolveAccountService.new.call(acct) if account.nil? && acct.present?)
self.account = nil
end
end

View File

@@ -56,6 +56,7 @@ class Group < ApplicationRecord
record.errors.add(:base, I18n.t('groups.errors.limit')) if Group.where(account_id: value).count >= PER_ACCOUNT_LIMIT
end
before_save :set_slug
before_save :set_password
before_destroy :clean_feed_manager
after_create :add_owner_to_accounts
@@ -80,6 +81,20 @@ class Group < ApplicationRecord
password
end
end
def set_slug
puts "tilly-5: " + slug.to_s
puts "tilly-6: " + self.member_count.to_s
if !slug.nil? && self.member_count > 50 && self.slug.nil?
self.slug = slug.downcase.strip.gsub(' ', '-').gsub(/[^\w-]/, '')
elsif !slug.nil? && self.member_count < 50 && self.slug.nil?
self.slug = nil
else
self.slug = self.slug
end
end
def add_owner_to_accounts
group_accounts << GroupAccount.new(account: account, role: :admin, write_permissions: true)
end

View File

@@ -13,6 +13,7 @@
class GroupAccount < ApplicationRecord
self.ignored_columns = ["unread_count"]
# : todo : enum: 1,2,3,4,5...
enum role: {
admin: "admin",
moderator: "moderator"

View File

@@ -1,42 +0,0 @@
# frozen_string_literal: true
# == Schema Information
#
# Table name: imports
#
# id :bigint(8) not null, primary key
# type :integer not null
# approved :boolean default(FALSE), not null
# created_at :datetime not null
# updated_at :datetime not null
# data_file_name :string
# data_content_type :string
# data_file_size :integer
# data_updated_at :datetime
# account_id :bigint(8) not null
# overwrite :boolean default(FALSE), not null
#
class Import < ApplicationRecord
FILE_TYPES = %w(text/plain text/csv).freeze
MODES = %i(merge overwrite).freeze
self.inheritance_column = false
belongs_to :account
enum type: [:following, :blocking, :muting, :domain_blocking]
validates :type, presence: true
has_attached_file :data
validates_attachment_content_type :data, content_type: FILE_TYPES
validates_attachment_presence :data
def mode
overwrite? ? :overwrite : :merge
end
def mode=(str)
self.overwrite = str.to_sym == :overwrite
end
end

View File

@@ -1,39 +0,0 @@
# frozen_string_literal: true
# == Schema Information
#
# Table name: invites
#
# id :bigint(8) not null, primary key
# user_id :bigint(8) not null
# code :string default(""), not null
# expires_at :datetime
# max_uses :integer
# uses :integer default(0), not null
# created_at :datetime not null
# updated_at :datetime not null
# autofollow :boolean default(FALSE), not null
#
class Invite < ApplicationRecord
include Expireable
belongs_to :user
has_many :users, inverse_of: :invite
scope :available, -> { where(expires_at: nil).or(where('expires_at >= ?', Time.now.utc)) }
before_validation :set_code
def valid_for_use?
(max_uses.nil? || uses < max_uses) && !expired?
end
private
def set_code
loop do
self.code = ([*('a'..'z'), *('A'..'Z'), *('0'..'9')] - %w(0 1 I l O)).sample(8).join
break if Invite.find_by(code: code).nil?
end
end
end

View File

@@ -1,32 +0,0 @@
# frozen_string_literal: true
class InviteFilter
attr_reader :params
def initialize(params)
@params = params
end
def results
scope = Invite.order(created_at: :desc)
params.each do |key, value|
scope.merge!(scope_for(key, value)) if value.present?
end
scope
end
private
def scope_for(key, _value)
case key.to_s
when 'available'
Invite.available
when 'expired'
Invite.expired
else
raise "Unknown filter: #{key}"
end
end
end

View File

@@ -16,6 +16,7 @@ class Notification < ApplicationRecord
include Paginable
include Cacheable
# : todo : enums, 1,2,3,4,5,...
TYPE_CLASS_MAP = {
mention: 'Mention',
reblog: 'Status',

View File

@@ -46,10 +46,6 @@ class Poll < ApplicationRecord
options.map.with_index { |title, key| Option.new(self, key.to_s, title, show_totals_now? ? cached_tallies[key] : nil) }
end
def possibly_stale?
remote? && last_fetched_before_expiration? && time_passed_since_last_fetch?
end
def voted?(account)
account.id == account_id || votes.where(account: account).exists?
end
@@ -94,15 +90,7 @@ class Poll < ApplicationRecord
Rails.cache.delete("statuses/#{status_id}")
end
def last_fetched_before_expiration?
last_fetched_at.nil? || expires_at.nil? || last_fetched_at < expires_at
end
def time_passed_since_last_fetch?
last_fetched_at.nil? || last_fetched_at < 1.minute.ago
end
def show_totals_now?
expired? || !hide_totals?
expired?
end
end

View File

@@ -1,80 +0,0 @@
# frozen_string_literal: true
# == Schema Information
#
# Table name: relays
#
# id :bigint(8) not null, primary key
# inbox_url :string default(""), not null
# follow_activity_id :string
# created_at :datetime not null
# updated_at :datetime not null
# state :integer default("idle"), not null
#
class Relay < ApplicationRecord
PRESET_RELAY = 'https://relay.gab.com/inbox'
validates :inbox_url, presence: true, uniqueness: true, url: true, if: :will_save_change_to_inbox_url?
enum state: [:idle, :pending, :accepted, :rejected]
scope :enabled, -> { accepted }
before_destroy :ensure_disabled
alias enabled? accepted?
def enable!
activity_id = ActivityPub::TagManager.instance.generate_uri_for(nil)
payload = Oj.dump(follow_activity(activity_id))
update!(state: :pending, follow_activity_id: activity_id)
DeliveryFailureTracker.new(inbox_url).track_success!
ActivityPub::DeliveryWorker.perform_async(payload, some_local_account.id, inbox_url)
end
def disable!
activity_id = ActivityPub::TagManager.instance.generate_uri_for(nil)
payload = Oj.dump(unfollow_activity(activity_id))
update!(state: :idle, follow_activity_id: nil)
DeliveryFailureTracker.new(inbox_url).track_success!
ActivityPub::DeliveryWorker.perform_async(payload, some_local_account.id, inbox_url)
end
private
def follow_activity(activity_id)
{
'@context': ActivityPub::TagManager::CONTEXT,
id: activity_id,
type: 'Follow',
actor: ActivityPub::TagManager.instance.uri_for(some_local_account),
object: ActivityPub::TagManager::COLLECTIONS[:public],
}
end
def unfollow_activity(activity_id)
{
'@context': ActivityPub::TagManager::CONTEXT,
id: activity_id,
type: 'Undo',
actor: ActivityPub::TagManager.instance.uri_for(some_local_account),
object: {
id: follow_activity_id,
type: 'Follow',
actor: ActivityPub::TagManager.instance.uri_for(some_local_account),
object: ActivityPub::TagManager::COLLECTIONS[:public],
},
}
end
def some_local_account
@some_local_account ||= Account.representative
end
def ensure_disabled
return unless enabled?
disable!
end
end

View File

@@ -1,56 +0,0 @@
# frozen_string_literal: true
class RemoteFollow
include ActiveModel::Validations
attr_accessor :acct, :addressable_template
validates :acct, presence: true
def initialize(attrs = nil)
@acct = attrs[:acct].gsub(/\A@/, '').strip if !attrs.nil? && !attrs[:acct].nil?
end
def valid?
return false unless super
populate_template
errors.empty?
end
def subscribe_address_for(account)
addressable_template.expand(uri: account.local_username_and_domain).to_s
end
def interact_address_for(status)
addressable_template.expand(uri: ActivityPub::TagManager.instance.uri_for(status)).to_s
end
private
def populate_template
if acct.blank? || redirect_url_link.nil? || redirect_url_link.template.nil?
missing_resource_error
else
@addressable_template = Addressable::Template.new(redirect_uri_template)
end
end
def redirect_uri_template
redirect_url_link.template
end
def redirect_url_link
acct_resource&.link('http://ostatus.org/schema/1.0/subscribe')
end
def acct_resource
@_acct_resource ||= Goldfinger.finger("acct:#{acct}")
rescue Goldfinger::Error, HTTP::ConnectionError
nil
end
def missing_resource_error
errors.add(:acct, I18n.t('remote_follow.missing_resource'))
end
end

View File

@@ -1,57 +0,0 @@
# frozen_string_literal: true
class RemoteProfile
include ActiveModel::Model
attr_reader :document
def initialize(body)
@document = Nokogiri::XML.parse(body, nil, 'utf-8')
end
def root
@root ||= document.at_xpath('/atom:feed|/atom:entry', atom: OStatus::TagManager::XMLNS)
end
def author
@author ||= root.at_xpath('./atom:author|./dfrn:owner', atom: OStatus::TagManager::XMLNS, dfrn: OStatus::TagManager::DFRN_XMLNS)
end
def hub_link
@hub_link ||= link_href_from_xml(root, 'hub')
end
def display_name
@display_name ||= author.at_xpath('./poco:displayName', poco: OStatus::TagManager::POCO_XMLNS)&.content
end
def note
@note ||= author.at_xpath('./atom:summary|./poco:note', atom: OStatus::TagManager::XMLNS, poco: OStatus::TagManager::POCO_XMLNS)&.content
end
def scope
@scope ||= author.at_xpath('./mastodon:scope', mastodon: OStatus::TagManager::MTDN_XMLNS)&.content
end
def avatar
@avatar ||= link_href_from_xml(author, 'avatar')
end
def header
@header ||= link_href_from_xml(author, 'header')
end
def emojis
@emojis ||= author.xpath('./xmlns:link[@rel="emoji"]', xmlns: OStatus::TagManager::XMLNS)
end
def locked?
scope == 'private'
end
private
def link_href_from_xml(xml, type)
xml.at_xpath(%(./atom:link[@rel="#{type}"]/@href), atom: OStatus::TagManager::XMLNS)&.content
end
end

View File

@@ -33,8 +33,6 @@ class Report < ApplicationRecord
false # Force uri_for to use uri attribute
end
before_validation :set_uri, only: :create
def object_type
:flag
end
@@ -97,7 +95,4 @@ class Report < ApplicationRecord
Admin::ActionLog.from("(#{sql}) AS admin_action_logs")
end
def set_uri
self.uri = ActivityPub::TagManager.instance.generate_uri_for(self) if uri.nil? && account.local?
end
end

View File

@@ -12,6 +12,7 @@
#
class Shortcut < ApplicationRecord
# : todo : enum 1,2, etc.
# enum shortcut_type: {
# account: 'account',
# group: 'group'

View File

@@ -34,7 +34,6 @@ class Status < ApplicationRecord
before_destroy :unlink_from_conversations
include Paginable
include Streamable
include Cacheable
include StatusThreadingConcern
@@ -48,7 +47,6 @@ class Status < ApplicationRecord
:public,
:unlisted,
:private,
:direct,
:limited,
:private_group,
], _suffix: :visibility
@@ -79,16 +77,14 @@ class Status < ApplicationRecord
has_and_belongs_to_many :preview_cards
has_one :notification, as: :activity, dependent: :destroy
has_one :stream_entry, as: :activity, inverse_of: :status
has_one :status_stat, inverse_of: :status
has_one :poll, inverse_of: :status, dependent: :destroy
validates :uri, uniqueness: true, presence: true, unless: :local?
validates :text, presence: true, unless: -> { with_media? || reblog? }
validates_with StatusLengthValidator
validates_with DisallowedHashtagsValidator
validates :reblog, uniqueness: { scope: :account }, if: :reblog?
validates :visibility, exclusion: { in: %w(direct limited) }, if: :reblog?
validates :visibility, exclusion: { in: %w(limited) }, if: :reblog?
accepts_nested_attributes_for :poll
@@ -127,13 +123,11 @@ class Status < ApplicationRecord
:status_stat,
:tags,
:preview_cards,
:stream_entry,
:preloadable_poll,
account: :account_stat,
active_mentions: { account: :account_stat },
reblog: [
:application,
:stream_entry,
:tags,
:preview_cards,
:media_attachments,
@@ -147,8 +141,6 @@ class Status < ApplicationRecord
delegate :domain, to: :account, prefix: true
REAL_TIME_WINDOW = 6.hours
def searchable_by(preloaded = nil)
ids = [account_id]
@@ -181,10 +173,6 @@ class Status < ApplicationRecord
!quote_of_id.nil?
end
def within_realtime_window?
created_at >= REAL_TIME_WINDOW.ago
end
def verb
if destroyed?
:delete
@@ -222,7 +210,7 @@ class Status < ApplicationRecord
end
def hidden?
private_visibility? || private_group_visibility? || direct_visibility? || limited_visibility?
private_visibility? || private_group_visibility? || limited_visibility?
end
def distributable?
@@ -294,7 +282,7 @@ class Status < ApplicationRecord
class << self
def selectable_visibilities
visibilities.keys - %w(direct limited private_group)
visibilities.keys - %w(limited private_group)
end
def in_chosen_languages(account)
@@ -315,57 +303,11 @@ class Status < ApplicationRecord
where(group: groupIds, reply: false)
end
def as_direct_timeline(account, limit = 20, max_id = nil, since_id = nil, cache_ids = false)
# direct timeline is mix of direct message from_me and to_me.
# 2 queries are executed with pagination.
# constant expression using arel_table is required for partial index
# _from_me part does not require any timeline filters
query_from_me = where(account_id: account.id)
.where(Status.arel_table[:visibility].eq(3))
.limit(limit)
.order('statuses.id DESC')
# _to_me part requires mute and block filter.
# FIXME: may we check mutes.hide_notifications?
query_to_me = Status
.joins(:mentions)
.merge(Mention.where(account_id: account.id))
.where(Status.arel_table[:visibility].eq(3))
.limit(limit)
.order('mentions.status_id DESC')
.not_excluded_by_account(account)
if max_id.present?
query_from_me = query_from_me.where('statuses.id < ?', max_id)
query_to_me = query_to_me.where('mentions.status_id < ?', max_id)
end
if since_id.present?
query_from_me = query_from_me.where('statuses.id > ?', since_id)
query_to_me = query_to_me.where('mentions.status_id > ?', since_id)
end
if cache_ids
# returns array of cache_ids object that have id and updated_at
(query_from_me.cache_ids.to_a + query_to_me.cache_ids.to_a).uniq(&:id).sort_by(&:id).reverse.take(limit)
else
# returns ActiveRecord.Relation
items = (query_from_me.select(:id).to_a + query_to_me.select(:id).to_a).uniq(&:id).sort_by(&:id).reverse.take(limit)
Status.where(id: items.map(&:id))
end
end
def as_pro_timeline(account = nil)
query = timeline_scope.without_replies.popular_accounts.where('statuses.updated_at > ?', 2.hours.ago)
apply_timeline_filters(query, account)
end
def as_public_timeline(account = nil)
query = timeline_scope.without_replies.where('statuses.updated_at > ?', 15.minutes.ago)
apply_timeline_filters(query, account)
end
def as_tag_timeline(tag, account = nil)
query = timeline_scope.tagged_with(tag).without_replies
@@ -388,10 +330,6 @@ class Status < ApplicationRecord
select('reblog_of_id').where(reblog_of_id: status_ids).where(account_id: account_id).reorder(nil).each_with_object({}) { |s, h| h[s.reblog_of_id] = true }
end
def mutes_map(conversation_ids, account_id)
ConversationMute.select('conversation_id').where(conversation_id: conversation_ids).where(account_id: account_id).each_with_object({}) { |m, h| h[m.conversation_id] = true }
end
def pins_map(status_ids, account_id)
StatusPin.select('status_id').where(status_id: status_ids).where(account_id: account_id).each_with_object({}) { |p, h| h[p.status_id] = true }
end
@@ -490,7 +428,7 @@ class Status < ApplicationRecord
end
def store_uri
update_column(:uri, ActivityPub::TagManager.instance.uri_for(self)) if uri.nil?
update_column(:uri, "/#{self.account.username}/posts/#{self.id}") if uri.nil?
end
def prepare_contents
@@ -556,15 +494,13 @@ class Status < ApplicationRecord
end
def increment_counter_caches
return if direct_visibility?
account&.increment_count!(:statuses_count)
reblog&.increment_count!(:reblogs_count) if reblog? && (public_visibility? || unlisted_visibility?)
thread&.increment_count!(:replies_count) if in_reply_to_id.present? && (public_visibility? || unlisted_visibility?)
end
def decrement_counter_caches
return if direct_visibility? || marked_for_mass_destruction?
return if marked_for_mass_destruction?
account&.decrement_count!(:statuses_count)
reblog&.decrement_count!(:reblogs_count) if reblog? && (public_visibility? || unlisted_visibility?)
@@ -572,14 +508,14 @@ class Status < ApplicationRecord
end
def unlink_from_conversations
return unless direct_visibility?
# return unless direct_visibility?
mentioned_accounts = mentions.includes(:account).map(&:account)
inbox_owners = mentioned_accounts.select(&:local?) + (account.local? ? [account] : [])
# mentioned_accounts = mentions.includes(:account).map(&:account)
# inbox_owners = mentioned_accounts.select(&:local?) + (account.local? ? [account] : [])
inbox_owners.each do |inbox_owner|
AccountConversation.remove_status(inbox_owner, self)
end
# inbox_owners.each do |inbox_owner|
# AccountConversation.remove_status(inbox_owner, self)
# end
end
end

View File

@@ -1,59 +0,0 @@
# frozen_string_literal: true
# == Schema Information
#
# Table name: stream_entries
#
# id :bigint(8) not null, primary key
# activity_id :bigint(8)
# activity_type :string
# created_at :datetime not null
# updated_at :datetime not null
# hidden :boolean default(FALSE), not null
# account_id :bigint(8)
#
class StreamEntry < ApplicationRecord
include Paginable
belongs_to :account, inverse_of: :stream_entries
belongs_to :activity, polymorphic: true
belongs_to :status, foreign_type: 'Status', foreign_key: 'activity_id', inverse_of: :stream_entry
validates :account, :activity, presence: true
STATUS_INCLUDES = [:account, :stream_entry, :conversation, :media_attachments, :tags, mentions: :account, reblog: [:stream_entry, :account, :conversation, :media_attachments, :tags, mentions: :account], thread: [:stream_entry, :account]].freeze
default_scope { where(activity_type: 'Status') }
scope :recent, -> { reorder(id: :desc) }
scope :with_includes, -> { includes(:account, status: STATUS_INCLUDES) }
delegate :target, :title, :content, :thread,
to: :status,
allow_nil: true
def object_type
orphaned? || targeted? ? :activity : status.object_type
end
def verb
orphaned? ? :delete : status.verb
end
def targeted?
[:follow, :request_friend, :authorize, :reject, :unfollow, :block, :unblock, :share, :favorite].include? verb
end
def threaded?
(verb == :favorite || object_type == :comment) && !thread.nil?
end
def mentions
orphaned? ? [] : status.active_mentions.map(&:account)
end
private
def orphaned?
status.nil?
end
end

View File

@@ -1,62 +0,0 @@
# frozen_string_literal: true
# == Schema Information
#
# Table name: subscriptions
#
# id :bigint(8) not null, primary key
# callback_url :string default(""), not null
# secret :string
# expires_at :datetime
# confirmed :boolean default(FALSE), not null
# created_at :datetime not null
# updated_at :datetime not null
# last_successful_delivery_at :datetime
# domain :string
# account_id :bigint(8) not null
#
class Subscription < ApplicationRecord
MIN_EXPIRATION = 1.day.to_i
MAX_EXPIRATION = 30.days.to_i
belongs_to :account
validates :callback_url, presence: true
validates :callback_url, uniqueness: { scope: :account_id }
scope :confirmed, -> { where(confirmed: true) }
scope :future_expiration, -> { where(arel_table[:expires_at].gt(Time.now.utc)) }
scope :expired, -> { where(arel_table[:expires_at].lt(Time.now.utc)) }
scope :active, -> { confirmed.future_expiration }
def lease_seconds=(value)
self.expires_at = future_expiration(value)
end
def lease_seconds
(expires_at - Time.now.utc).to_i
end
def expired?
Time.now.utc > expires_at
end
before_validation :set_min_expiration
private
def future_expiration(value)
Time.now.utc + future_offset(value).seconds
end
def future_offset(seconds)
[
[MIN_EXPIRATION, seconds.to_i].max,
MAX_EXPIRATION,
].min
end
def set_min_expiration
self.lease_seconds = 0 unless expires_at
end
end

View File

@@ -14,7 +14,6 @@ class Tag < ApplicationRecord
has_and_belongs_to_many :accounts
has_and_belongs_to_many :sample_accounts, -> { searchable.discoverable.popular.limit(3) }, class_name: 'Account'
has_many :featured_tags, dependent: :destroy, inverse_of: :tag
has_one :account_tag_stat, dependent: :destroy
HASHTAG_NAME_RE = '[[:word:]_]*[[:alpha:]_·][[:word:]_]*'

View File

@@ -1,65 +0,0 @@
# frozen_string_literal: true
class TrendingTags
KEY = 'trending_tags'
EXPIRE_HISTORY_AFTER = 7.days.seconds
EXPIRE_TRENDS_AFTER = 1.day.seconds
THRESHOLD = 5
class << self
include Redisable
def record_use!(tag, account, at_time = Time.now.utc)
return if disallowed_hashtags.include?(tag.name) || account.silenced? || account.bot?
increment_historical_use!(tag.id, at_time)
increment_unique_use!(tag.id, account.id, at_time)
increment_vote!(tag.id, at_time)
end
def get(limit)
key = "#{KEY}:#{Time.now.utc.beginning_of_day.to_i}"
tag_ids = redis.zrevrange(key, 0, limit - 1).map(&:to_i)
tags = Tag.where(id: tag_ids).to_a.each_with_object({}) { |tag, h| h[tag.id] = tag }
tag_ids.map { |tag_id| tags[tag_id] }.compact
end
private
def increment_historical_use!(tag_id, at_time)
key = "activity:tags:#{tag_id}:#{at_time.beginning_of_day.to_i}"
redis.incrby(key, 1)
redis.expire(key, EXPIRE_HISTORY_AFTER)
end
def increment_unique_use!(tag_id, account_id, at_time)
key = "activity:tags:#{tag_id}:#{at_time.beginning_of_day.to_i}:accounts"
redis.pfadd(key, account_id)
redis.expire(key, EXPIRE_HISTORY_AFTER)
end
def increment_vote!(tag_id, at_time)
key = "#{KEY}:#{at_time.beginning_of_day.to_i}"
expected = redis.pfcount("activity:tags:#{tag_id}:#{(at_time - 1.day).beginning_of_day.to_i}:accounts").to_f
expected = 1.0 if expected.zero?
observed = redis.pfcount("activity:tags:#{tag_id}:#{at_time.beginning_of_day.to_i}:accounts").to_f
if expected > observed || observed < THRESHOLD
redis.zrem(key, tag_id.to_s)
else
score = ((observed - expected)**2) / expected
redis.zadd(key, score, tag_id.to_s)
end
redis.expire(key, EXPIRE_TRENDS_AFTER)
end
def disallowed_hashtags
return @disallowed_hashtags if defined?(@disallowed_hashtags)
@disallowed_hashtags = Setting.disallowed_hashtags.nil? ? [] : Setting.disallowed_hashtags
@disallowed_hashtags = @disallowed_hashtags.split(' ') if @disallowed_hashtags.is_a? String
@disallowed_hashtags = @disallowed_hashtags.map(&:downcase)
end
end
end

View File

@@ -33,7 +33,6 @@
# account_id :bigint(8) not null
# disabled :boolean default(FALSE), not null
# moderator :boolean default(FALSE), not null
# invite_id :bigint(8)
# remember_token :string
# chosen_languages :string is an Array
# created_by_application_id :bigint(8)
@@ -43,6 +42,8 @@
#
class User < ApplicationRecord
self.ignored_columns = ["{:column=>:invite}_id"]
include Settings::Extend
include UserRoles
@@ -69,24 +70,18 @@ class User < ApplicationRecord
include LdapAuthenticable
belongs_to :account, inverse_of: :user
belongs_to :invite, counter_cache: :uses, optional: true
belongs_to :created_by_application, class_name: 'Doorkeeper::Application', optional: true
accepts_nested_attributes_for :account
has_many :applications, class_name: 'Doorkeeper::Application', as: :owner
has_many :backups, inverse_of: :user
has_one :invite_request, class_name: 'UserInviteRequest', inverse_of: :user, dependent: :destroy
accepts_nested_attributes_for :invite_request, reject_if: ->(attributes) { attributes['text'].blank? }
validates :locale, inclusion: I18n.available_locales.map(&:to_s), if: :locale?
validates :unique_email, uniqueness: true, on: :create
validates_with BlacklistedEmailValidator, on: :create
validates_with EmailMxValidator, if: :validate_email_dns?
validates :agreement, acceptance: { allow_nil: false, accept: [true, 'true', '1'] }, on: :create
scope :recent, -> { order(id: :desc) }
scope :pending, -> { where(approved: false) }
scope :approved, -> { where(approved: true) }
scope :confirmed, -> { where.not(confirmed_at: nil) }
scope :enabled, -> { where(disabled: false) }
@@ -96,7 +91,6 @@ class User < ApplicationRecord
scope :emailable, -> { confirmed.enabled.joins(:account).merge(Account.searchable) }
before_validation :sanitize_languages
before_validation :set_unique_email
before_create :set_approved
after_create :prepare_new_user!
@@ -109,24 +103,15 @@ class User < ApplicationRecord
delegate :auto_play_gif, :default_sensitive, :unfollow_modal, :boost_modal, :delete_modal,
:noindex, :theme, :display_media, :hide_network,
:expand_spoilers, :default_language, :aggregate_reblogs, :show_application,
:expand_spoilers, :default_language, :aggregate_reblogs,
:group_in_home_feed, to: :settings, prefix: :setting, allow_nil: false
attr_reader :invite_code
attr_writer :external
def confirmed?
confirmed_at.present?
end
def invited?
invite_id.present?
end
def valid_invitation?
invite_id.present? && invite.valid_for_use?
end
def disable!
update!(disabled: true,
last_sign_in_at: current_sign_in_at,
@@ -138,8 +123,8 @@ class User < ApplicationRecord
end
def confirm
new_user = !confirmed?
self.approved = true if open_registrations?
new_user = !confirmed?
self.approved = true
super
@@ -149,8 +134,8 @@ class User < ApplicationRecord
end
def confirm!
new_user = !confirmed?
self.approved = true if open_registrations?
new_user = !confirmed?
self.approved = true
skip_confirmation!
save!
@@ -158,18 +143,10 @@ class User < ApplicationRecord
# prepare_new_user! if new_user && approved?
end
def pending?
!approved?
end
def active_for_authentication?
super && approved?
end
def inactive_message
!approved? ? :pending : super
end
def approve!
return if approved?
@@ -200,10 +177,6 @@ class User < ApplicationRecord
settings.notification_emails['report']
end
def allows_pending_account_emails?
settings.notification_emails['pending_account']
end
def hides_network?
@hides_network ||= settings.hide_network
end
@@ -212,10 +185,6 @@ class User < ApplicationRecord
@aggregates_reblogs ||= settings.aggregate_reblogs
end
def shows_application?
@shows_application ||= settings.show_application
end
def allows_group_in_home_feed?
settings.group_in_home_feed
end
@@ -249,11 +218,6 @@ class User < ApplicationRecord
session.web_push_subscription.nil? ? nil : session.web_push_subscription
end
def invite_code=(code)
self.invite = Invite.find_by(code: code) if code.present?
@invite_code = code
end
def challenge
#
end
@@ -294,11 +258,7 @@ class User < ApplicationRecord
private
def set_approved
self.approved = open_registrations? || valid_invitation? || external?
end
def open_registrations?
Setting.registrations_mode == 'open'
self.approved = true
end
def external?
@@ -345,12 +305,4 @@ class User < ApplicationRecord
end
end
def set_unique_email
user, domain = self.email.split('@')
user = user.split('+').first
if ((domain == 'gmail.com') || (domain == 'hotmail.com'))
user = user.gsub('.', '')
end
self.unique_email = "#{user}@#{domain}"
end
end

View File

@@ -1,17 +0,0 @@
# frozen_string_literal: true
# == Schema Information
#
# Table name: user_invite_requests
#
# id :bigint(8) not null, primary key
# user_id :bigint(8)
# text :text
# created_at :datetime not null
# updated_at :datetime not null
#
class UserInviteRequest < ApplicationRecord
belongs_to :user, inverse_of: :invite_request
validates :text, presence: true, length: { maximum: 420 }
end