2022-06-26 02:00:09 -07:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2023-10-15 01:21:46 -07:00
|
|
|
class User < ApplicationRecord
|
2021-12-31 13:19:21 -08:00
|
|
|
include User::Relationship
|
|
|
|
include User::Relationship::Follow
|
2022-04-18 12:24:15 -07:00
|
|
|
include User::Relationship::Block
|
2022-12-27 17:59:51 -08:00
|
|
|
include User::Relationship::Mute
|
2020-04-20 14:03:57 -07:00
|
|
|
include User::AnswerMethods
|
2022-06-26 08:39:52 -07:00
|
|
|
include User::BanMethods
|
2020-04-20 14:03:57 -07:00
|
|
|
include User::InboxMethods
|
2023-02-25 06:46:11 -08:00
|
|
|
include User::NotificationMethods
|
2020-04-20 14:03:57 -07:00
|
|
|
include User::QuestionMethods
|
2022-09-11 13:34:18 -07:00
|
|
|
include User::PushNotificationMethods
|
2022-02-19 08:19:28 -08:00
|
|
|
include User::ReactionMethods
|
2020-04-20 14:03:57 -07:00
|
|
|
include User::RelationshipMethods
|
2023-02-05 10:17:01 -08:00
|
|
|
include User::SharingMethods
|
2020-04-20 14:03:57 -07:00
|
|
|
include User::TimelineMethods
|
2020-10-18 01:39:46 -07:00
|
|
|
include ActiveModel::OneTimePassword
|
2020-04-20 14:03:57 -07:00
|
|
|
|
2014-08-01 03:07:16 -07:00
|
|
|
# Include default devise modules. Others available are:
|
|
|
|
# :confirmable, :lockable, :timeoutable and :omniauthable
|
2015-01-01 09:17:34 -08:00
|
|
|
devise :database_authenticatable, :async, :registerable,
|
2014-08-01 06:27:08 -07:00
|
|
|
:recoverable, :rememberable, :trackable,
|
2023-01-02 02:16:47 -08:00
|
|
|
:validatable, :confirmable, authentication_keys: [:login]
|
2014-11-30 10:43:22 -08:00
|
|
|
|
2020-10-18 01:39:46 -07:00
|
|
|
has_one_time_password
|
2020-10-18 02:39:28 -07:00
|
|
|
enum otp_module: { disabled: 0, enabled: 1 }, _prefix: true
|
2020-10-18 01:39:46 -07:00
|
|
|
attr_accessor :otp_attempt, :otp_validation
|
2023-01-02 02:16:47 -08:00
|
|
|
attr_writer :login
|
2020-10-18 01:39:46 -07:00
|
|
|
|
2020-04-19 13:35:58 -07:00
|
|
|
rolify
|
|
|
|
|
2022-02-04 12:09:43 -08:00
|
|
|
has_many :questions, dependent: :destroy_async
|
|
|
|
has_many :answers, dependent: :destroy_async
|
|
|
|
has_many :comments, dependent: :destroy_async
|
2024-01-27 04:03:45 -08:00
|
|
|
has_many :inbox_entries, dependent: :destroy_async
|
2023-10-25 20:54:48 -07:00
|
|
|
has_many :smiles, class_name: "Reaction", dependent: :destroy_async
|
2022-02-04 12:09:43 -08:00
|
|
|
has_many :notifications, foreign_key: :recipient_id, dependent: :destroy_async
|
|
|
|
has_many :reports, dependent: :destroy_async
|
|
|
|
has_many :lists, dependent: :destroy_async
|
2022-02-04 12:19:43 -08:00
|
|
|
has_many :list_memberships, class_name: "ListMember", dependent: :destroy_async
|
2022-02-04 12:09:43 -08:00
|
|
|
has_many :mute_rules, dependent: :destroy_async
|
2014-11-30 05:43:35 -08:00
|
|
|
|
2022-02-04 12:09:43 -08:00
|
|
|
has_many :subscriptions, dependent: :destroy_async
|
|
|
|
has_many :totp_recovery_codes, dependent: :destroy_async
|
2022-09-11 11:10:21 -07:00
|
|
|
has_many :web_push_subscriptions, dependent: :destroy_async
|
2015-04-20 18:12:11 -07:00
|
|
|
|
2022-02-04 13:08:25 -08:00
|
|
|
has_one :profile, dependent: :destroy
|
|
|
|
has_one :theme, dependent: :destroy
|
2015-08-25 01:26:36 -07:00
|
|
|
|
2022-02-04 12:19:43 -08:00
|
|
|
has_many :bans, class_name: "UserBan", dependent: :destroy_async
|
2023-01-02 02:16:47 -08:00
|
|
|
has_many :banned_users, class_name: "UserBan",
|
|
|
|
foreign_key: "banned_by_id",
|
|
|
|
dependent: :nullify
|
2021-08-14 07:07:12 -07:00
|
|
|
|
2024-01-14 04:14:04 -08:00
|
|
|
has_many :anonymous_blocks, dependent: :destroy_async
|
2024-01-14 04:36:52 -08:00
|
|
|
has_many :passive_anonymous_blocks, class_name: "AnonymousBlock",
|
2024-01-14 04:14:04 -08:00
|
|
|
foreign_key: "target_user_id",
|
2024-01-14 04:36:52 -08:00
|
|
|
dependent: :nullify
|
2024-01-14 04:14:04 -08:00
|
|
|
|
2023-01-20 21:30:33 -08:00
|
|
|
SCREEN_NAME_REGEX = /\A[a-zA-Z0-9_]+\z/
|
2023-01-02 02:16:47 -08:00
|
|
|
WEBSITE_REGEX = /https?:\/\/([A-Za-z.-]+)\/?(?:.*)/i
|
2014-11-30 10:43:22 -08:00
|
|
|
|
2015-01-09 05:12:52 -08:00
|
|
|
before_validation do
|
|
|
|
screen_name.strip!
|
|
|
|
end
|
|
|
|
|
2022-01-11 09:37:07 -08:00
|
|
|
validates :email, fake_email: true, typoed_email: true
|
2023-02-10 11:48:15 -08:00
|
|
|
validates :sharing_custom_url, allow_blank: true, valid_url: true
|
2023-01-20 21:16:14 -08:00
|
|
|
validates :screen_name,
|
|
|
|
presence: true,
|
2023-01-21 04:14:15 -08:00
|
|
|
format: { with: SCREEN_NAME_REGEX, message: I18n.t("activerecord.validation.user.screen_name.format") },
|
2023-01-20 21:16:14 -08:00
|
|
|
length: { minimum: 1, maximum: 16 },
|
|
|
|
uniqueness: { case_sensitive: false },
|
|
|
|
screen_name: true
|
2014-11-11 11:20:00 -08:00
|
|
|
|
2020-05-01 15:43:46 -07:00
|
|
|
mount_uploader :profile_picture, ProfilePictureUploader, mount_on: :profile_picture_file_name
|
2020-05-17 11:58:27 -07:00
|
|
|
process_in_background :profile_picture
|
2020-05-01 15:43:46 -07:00
|
|
|
mount_uploader :profile_header, ProfileHeaderUploader, mount_on: :profile_header_file_name
|
2020-05-17 11:58:27 -07:00
|
|
|
process_in_background :profile_header
|
2014-12-29 01:18:12 -08:00
|
|
|
|
2020-05-02 09:45:11 -07:00
|
|
|
# when a user has been deleted, all reports relating to the user become invalid
|
2015-04-29 17:04:43 -07:00
|
|
|
before_destroy do
|
2023-01-02 02:16:47 -08:00
|
|
|
Report.where(target_id: id, type: "Reports::User").find_each do |r|
|
2015-04-29 17:04:43 -07:00
|
|
|
unless r.nil?
|
|
|
|
r.deleted = true
|
|
|
|
r.save
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-03-26 01:31:49 -07:00
|
|
|
after_destroy do
|
|
|
|
Retrospring::Metrics::USERS_DESTROYED.increment
|
|
|
|
end
|
|
|
|
|
2021-12-19 14:51:06 -08:00
|
|
|
after_create do
|
|
|
|
Profile.create(user_id: id) if Profile.where(user_id: id).count.zero?
|
2023-03-26 01:31:49 -07:00
|
|
|
|
|
|
|
Retrospring::Metrics::USERS_CREATED.increment
|
2021-12-19 14:51:06 -08:00
|
|
|
end
|
|
|
|
|
2022-07-23 03:06:05 -07:00
|
|
|
# use the screen name as parameter for url helpers
|
|
|
|
def to_param = screen_name
|
|
|
|
|
2023-01-02 02:16:47 -08:00
|
|
|
def login = @login || screen_name || email
|
2014-11-30 10:43:22 -08:00
|
|
|
|
2014-08-01 06:27:08 -07:00
|
|
|
def self.find_first_by_auth_conditions(warden_conditions)
|
2015-04-21 15:33:55 -07:00
|
|
|
conditions = warden_conditions.dup
|
2023-01-02 02:16:47 -08:00
|
|
|
if (login = conditions.delete(:login))
|
|
|
|
where(conditions).where(["lower(screen_name) = :value OR lower(email) = :value", { value: login.downcase }]).first
|
2014-08-01 06:27:08 -07:00
|
|
|
else
|
|
|
|
where(conditions).first
|
|
|
|
end
|
|
|
|
end
|
2014-11-30 06:13:17 -08:00
|
|
|
|
2020-05-25 09:04:54 -07:00
|
|
|
# @param list [List]
|
|
|
|
# @return [Boolean] true if +self+ is a member of +list+
|
2023-01-02 02:16:47 -08:00
|
|
|
def member_of?(list) = list_memberships.pluck(:list_id).include? list.id
|
2015-01-13 22:07:40 -08:00
|
|
|
|
2015-01-03 09:09:56 -08:00
|
|
|
# answers a question
|
|
|
|
# @param question [Question] the question to answer
|
|
|
|
# @param content [String] the answer content
|
|
|
|
def answer(question, content)
|
2022-06-13 10:31:37 -07:00
|
|
|
raise Errors::AnsweringOtherBlockedSelf if question.user&.blocking?(self)
|
2022-06-11 14:14:01 -07:00
|
|
|
raise Errors::AnsweringSelfBlockedOther if self.blocking?(question.user)
|
|
|
|
|
2023-02-13 13:24:52 -08:00
|
|
|
Retrospring::Metrics::QUESTIONS_ANSWERED.increment
|
|
|
|
|
2023-12-18 09:28:19 -08:00
|
|
|
Answer.create!(content:, user: self, question:, has_reacted: false, is_subscribed: true)
|
2015-01-03 09:09:56 -08:00
|
|
|
end
|
|
|
|
|
2015-01-03 09:40:56 -08:00
|
|
|
# has the user answered +question+ yet?
|
|
|
|
# @param question [Question]
|
2023-01-02 02:16:47 -08:00
|
|
|
def answered?(question) = question.answers.pluck(:user_id).include? id
|
2015-01-03 09:40:56 -08:00
|
|
|
|
2014-12-05 05:11:08 -08:00
|
|
|
def comment(answer, content)
|
2022-06-09 10:44:58 -07:00
|
|
|
raise Errors::CommentingSelfBlockedOther if self.blocking?(answer.user)
|
|
|
|
raise Errors::CommentingOtherBlockedSelf if answer.user.blocking?(self)
|
|
|
|
|
2023-02-13 13:24:52 -08:00
|
|
|
Retrospring::Metrics::COMMENTS_CREATED.increment
|
|
|
|
|
2023-01-02 02:16:47 -08:00
|
|
|
Comment.create!(user: self, answer:, content:)
|
2014-12-05 05:11:08 -08:00
|
|
|
end
|
2014-12-28 10:32:08 -08:00
|
|
|
|
|
|
|
# @return [Boolean] is the user a moderator?
|
2023-01-02 02:16:47 -08:00
|
|
|
def mod? = has_cached_role?(:moderator) || has_cached_role?(:administrator)
|
2022-08-21 06:02:49 -07:00
|
|
|
|
2023-01-02 02:16:47 -08:00
|
|
|
def admin? = has_cached_role?(:administrator)
|
2014-12-28 10:32:08 -08:00
|
|
|
|
2015-01-03 09:09:56 -08:00
|
|
|
# region stuff used for reporting/moderation
|
2024-01-23 11:42:16 -08:00
|
|
|
def report(object, reason = nil)
|
2024-01-23 11:47:38 -08:00
|
|
|
target_user = if object.instance_of?(::User)
|
2024-01-23 11:42:16 -08:00
|
|
|
object
|
|
|
|
elsif object.respond_to? :user
|
|
|
|
object.user
|
|
|
|
end
|
|
|
|
|
2024-01-23 11:32:48 -08:00
|
|
|
existing = Report.find_by(type: "Reports::#{object.class}", target_id: object.id, user_id: id, target_user_id: target_user&.id, deleted: false)
|
2015-04-21 19:59:10 -07:00
|
|
|
if existing.nil?
|
2024-01-23 11:32:48 -08:00
|
|
|
Report.create(type: "Reports::#{object.class}", target_id: object.id, user_id: id, target_user_id: target_user&.id, reason:)
|
2023-01-02 02:16:47 -08:00
|
|
|
elsif !reason.nil? && reason.length.positive?
|
|
|
|
existing.append_reason(reason)
|
2015-04-21 19:59:10 -07:00
|
|
|
end
|
2014-12-28 10:32:08 -08:00
|
|
|
end
|
2015-01-03 09:09:56 -08:00
|
|
|
# endregion
|
2014-12-29 05:51:52 -08:00
|
|
|
|
2016-01-05 11:54:13 -08:00
|
|
|
def can_export?
|
2023-01-02 02:16:47 -08:00
|
|
|
return (Time.zone.now > export_created_at.in(1.week)) && !export_processing unless export_created_at.nil?
|
2020-04-19 13:35:58 -07:00
|
|
|
|
2023-01-02 02:16:47 -08:00
|
|
|
!export_processing
|
2022-11-06 05:59:56 -08:00
|
|
|
end
|
|
|
|
|
2023-01-02 02:16:47 -08:00
|
|
|
def inbox_locked? = privacy_lock_inbox
|
2014-08-01 03:07:16 -07:00
|
|
|
end
|