2020-04-18 15:59:18 -07:00
|
|
|
class User < ApplicationRecord
|
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,
|
2015-07-21 08:55:28 -07:00
|
|
|
:validatable, :confirmable, :authentication_keys => [:login]
|
2014-11-30 10:43:22 -08:00
|
|
|
|
2014-08-01 06:27:08 -07:00
|
|
|
# attr_accessor :login
|
2014-11-30 10:43:22 -08:00
|
|
|
|
2014-10-27 22:36:38 -07:00
|
|
|
has_many :questions, dependent: :destroy
|
|
|
|
has_many :answers, dependent: :destroy
|
|
|
|
has_many :comments, dependent: :destroy
|
2014-11-10 14:45:36 -08:00
|
|
|
has_many :inboxes, dependent: :destroy
|
2014-11-30 05:43:35 -08:00
|
|
|
has_many :active_relationships, class_name: 'Relationship',
|
|
|
|
foreign_key: 'source_id',
|
|
|
|
dependent: :destroy
|
|
|
|
has_many :passive_relationships, class_name: 'Relationship',
|
|
|
|
foreign_key: 'target_id',
|
|
|
|
dependent: :destroy
|
|
|
|
has_many :friends, through: :active_relationships, source: :target
|
|
|
|
has_many :followers, through: :passive_relationships, source: :source
|
2014-12-27 15:34:56 -08:00
|
|
|
has_many :smiles, dependent: :destroy
|
2015-05-03 18:39:41 -07:00
|
|
|
has_many :comment_smiles, dependent: :destroy
|
2014-12-27 15:34:56 -08:00
|
|
|
has_many :services, dependent: :destroy
|
|
|
|
has_many :notifications, foreign_key: :recipient_id, dependent: :destroy
|
2014-12-27 05:35:09 -08:00
|
|
|
has_many :reports, dependent: :destroy
|
2014-12-28 12:47:51 -08:00
|
|
|
has_many :moderation_comments, dependent: :destroy
|
|
|
|
has_many :moderation_votes, dependent: :destroy
|
2015-01-08 04:16:13 -08:00
|
|
|
has_many :groups, dependent: :destroy
|
2015-01-11 21:41:44 -08:00
|
|
|
has_many :group_memberships, class_name: "GroupMember", foreign_key: 'user_id', dependent: :destroy
|
2014-11-30 05:43:35 -08:00
|
|
|
|
2015-04-20 18:12:11 -07:00
|
|
|
has_many :subscriptions, dependent: :destroy
|
|
|
|
|
2015-08-25 01:26:36 -07:00
|
|
|
has_one :theme, dependent: :destroy
|
|
|
|
|
2014-08-01 06:27:08 -07:00
|
|
|
SCREEN_NAME_REGEX = /\A[a-zA-Z0-9_]{1,16}\z/
|
2014-12-01 11:47:10 -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
|
|
|
|
|
2015-07-21 13:44:14 -07:00
|
|
|
validates :email, fake_email: true
|
2015-01-09 05:12:52 -08:00
|
|
|
validates :screen_name, presence: true, format: { with: SCREEN_NAME_REGEX }, uniqueness: { case_sensitive: false }, screen_name: true
|
2014-11-11 11:20:00 -08:00
|
|
|
|
2014-12-12 09:52:56 -08:00
|
|
|
validates :display_name, length: { maximum: 50 }
|
2014-12-16 03:48:40 -08:00
|
|
|
validates :bio, length: { maximum: 200 }
|
2014-12-12 09:52:56 -08:00
|
|
|
|
2014-12-01 14:17:16 -08:00
|
|
|
# validates :website, format: { with: WEBSITE_REGEX }
|
2014-11-11 11:20:00 -08:00
|
|
|
|
2014-12-29 05:51:52 -08:00
|
|
|
has_attached_file :profile_picture, styles: { large: "500x500#", medium: "256x256#", small: "80x80#" },
|
|
|
|
default_url: "/images/:style/no_avatar.png", use_timestamp: false,
|
|
|
|
processors: [:cropper]
|
2015-05-09 21:38:06 -07:00
|
|
|
validates_attachment_content_type :profile_picture, :content_type => /\Aimage\/(png|jpe?g|gif)\Z/
|
2015-05-08 08:44:16 -07:00
|
|
|
|
|
|
|
has_attached_file :profile_header, styles: { web: "1500x350#", mobile: "450x105#", retina: "900x210#" },
|
|
|
|
default_url: '/images/header/:style/no_header.jpg', use_timestamp: false,
|
|
|
|
processors: [:cropper]
|
2015-05-09 21:38:06 -07:00
|
|
|
validates_attachment_content_type :profile_header, :content_type => /\Aimage\/(png|jpe?g)\Z/
|
2015-05-08 08:44:16 -07:00
|
|
|
|
2014-12-29 02:58:01 -08:00
|
|
|
process_in_background :profile_picture
|
2015-05-08 08:44:16 -07:00
|
|
|
process_in_background :profile_header
|
2014-12-29 01:18:12 -08:00
|
|
|
|
2014-12-26 16:22:13 -08:00
|
|
|
before_save do
|
2014-12-26 16:45:24 -08:00
|
|
|
self.display_name = 'WRYYYYYYYY' if display_name == 'Dio Brando'
|
2014-12-26 16:22:13 -08:00
|
|
|
self.website = if website.match %r{\Ahttps?://}
|
|
|
|
website
|
|
|
|
else
|
|
|
|
"http://#{website}"
|
|
|
|
end unless website.blank?
|
|
|
|
end
|
|
|
|
|
2015-04-29 17:04:43 -07:00
|
|
|
# when a user deleted himself, all reports relating to the user are invalid
|
|
|
|
before_destroy do
|
2015-04-29 17:22:24 -07:00
|
|
|
rep = Report.where(target_id: self.id, type: 'Reports::User')
|
2015-04-29 17:04:43 -07:00
|
|
|
rep.each do |r|
|
|
|
|
unless r.nil?
|
|
|
|
r.deleted = true
|
|
|
|
r.save
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-08-01 06:27:08 -07:00
|
|
|
def login=(login)
|
|
|
|
@login = login
|
|
|
|
end
|
|
|
|
|
|
|
|
def login
|
|
|
|
@login || self.screen_name || self.email
|
|
|
|
end
|
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
|
2014-08-01 06:27:08 -07:00
|
|
|
if login = conditions.delete(:login)
|
|
|
|
where(conditions).where(["lower(screen_name) = :value OR lower(email) = :value", { :value => login.downcase }]).first
|
|
|
|
else
|
|
|
|
where(conditions).first
|
|
|
|
end
|
|
|
|
end
|
2014-11-30 06:13:17 -08:00
|
|
|
|
2014-11-30 06:39:13 -08:00
|
|
|
# @return [Array] the users' timeline
|
|
|
|
def timeline
|
|
|
|
Answer.where("user_id in (?) OR user_id = ?", friend_ids, id).order(:created_at).reverse_order
|
|
|
|
end
|
|
|
|
|
2014-11-30 06:13:17 -08:00
|
|
|
# follows an user.
|
|
|
|
def follow(target_user)
|
2014-12-28 12:40:33 -08:00
|
|
|
active_relationships.create(target: target_user)
|
2014-11-30 06:13:17 -08:00
|
|
|
end
|
|
|
|
|
2014-11-30 06:39:13 -08:00
|
|
|
# unfollows an user
|
2014-11-30 06:13:17 -08:00
|
|
|
def unfollow(target_user)
|
2014-12-28 12:40:33 -08:00
|
|
|
active_relationships.find_by(target: target_user).destroy
|
2014-11-30 06:13:17 -08:00
|
|
|
end
|
|
|
|
|
2014-11-30 11:31:22 -08:00
|
|
|
# @return [Boolean] true if +self+ is following +target_user+
|
2014-11-30 06:13:17 -08:00
|
|
|
def following?(target_user)
|
|
|
|
friends.include? target_user
|
|
|
|
end
|
2014-11-30 10:43:22 -08:00
|
|
|
|
2015-01-13 22:07:40 -08:00
|
|
|
# @param group [Group]
|
|
|
|
# @return [Boolean] true if +self+ is a member of +group+
|
|
|
|
def member_of?(group)
|
|
|
|
group_memberships.pluck(:group_id).include? group.id
|
|
|
|
end
|
|
|
|
|
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)
|
|
|
|
Answer.create!(content: content,
|
|
|
|
user: self,
|
|
|
|
question: question)
|
|
|
|
end
|
|
|
|
|
2015-01-03 09:40:56 -08:00
|
|
|
# has the user answered +question+ yet?
|
|
|
|
# @param question [Question]
|
|
|
|
def answered?(question)
|
2015-01-13 22:50:27 -08:00
|
|
|
question.answers.pluck(:user_id).include? self.id
|
2015-01-03 09:40:56 -08:00
|
|
|
end
|
|
|
|
|
2014-11-30 10:43:22 -08:00
|
|
|
# smiles an answer
|
|
|
|
# @param answer [Answer] the answer to smile
|
|
|
|
def smile(answer)
|
2014-12-28 14:26:16 -08:00
|
|
|
Smile.create!(user: self, answer: answer)
|
2014-11-30 10:43:22 -08:00
|
|
|
end
|
|
|
|
|
|
|
|
# unsmile an answer
|
|
|
|
# @param answer [Answer] the answer to unsmile
|
|
|
|
def unsmile(answer)
|
2014-12-28 12:46:57 -08:00
|
|
|
Smile.find_by(user: self, answer: answer).destroy
|
2014-11-30 10:43:22 -08:00
|
|
|
end
|
2014-11-30 11:31:22 -08:00
|
|
|
|
2015-05-03 18:39:41 -07:00
|
|
|
# smiles a comment
|
|
|
|
# @param comment [Comment] the comment to smile
|
|
|
|
def smile_comment(comment)
|
|
|
|
CommentSmile.create!(user: self, comment: comment)
|
|
|
|
end
|
|
|
|
|
|
|
|
# unsmile an comment
|
|
|
|
# @param comment [Comment] the comment to unsmile
|
|
|
|
def unsmile_comment(comment)
|
|
|
|
CommentSmile.find_by(user: self, comment: comment).destroy
|
|
|
|
end
|
|
|
|
|
2014-11-30 11:31:22 -08:00
|
|
|
def smiled?(answer)
|
2015-01-13 22:50:27 -08:00
|
|
|
answer.smiles.pluck(:user_id).include? self.id
|
2014-11-30 11:31:22 -08:00
|
|
|
end
|
2014-12-01 11:47:10 -08:00
|
|
|
|
2015-05-03 18:39:41 -07:00
|
|
|
def smiled_comment?(comment)
|
|
|
|
comment.smiles.pluck(:user_id).include? self.id
|
|
|
|
end
|
|
|
|
|
2014-12-01 11:47:10 -08:00
|
|
|
def display_website
|
2015-01-05 01:16:00 -08:00
|
|
|
website.match(/https?:\/\/([A-Za-z.\-0-9]+)\/?(?:.*)/i)[1]
|
2014-12-01 11:47:10 -08:00
|
|
|
rescue NoMethodError
|
|
|
|
website
|
|
|
|
end
|
2014-12-05 05:11:08 -08:00
|
|
|
|
|
|
|
def comment(answer, content)
|
2014-12-28 12:58:11 -08:00
|
|
|
Comment.create!(user: self, answer: answer, content: 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?
|
|
|
|
def mod?
|
|
|
|
self.moderator? || self.admin?
|
|
|
|
end
|
|
|
|
|
2015-01-03 09:09:56 -08:00
|
|
|
# region stuff used for reporting/moderation
|
2015-04-21 19:59:10 -07:00
|
|
|
def report(object, reason = nil)
|
2015-04-29 17:22:24 -07:00
|
|
|
existing = Report.find_by(type: "Reports::#{object.class}", target_id: object.id, user_id: self.id, deleted: false)
|
2015-04-21 19:59:10 -07:00
|
|
|
if existing.nil?
|
|
|
|
Report.create(type: "Reports::#{object.class}", target_id: object.id, user_id: self.id, reason: reason)
|
|
|
|
elsif not reason.nil? and reason.length > 0
|
|
|
|
if existing.reason.nil?
|
|
|
|
existing.update(reason: reason)
|
|
|
|
else
|
|
|
|
existing.update(reason: [existing.reason || "", reason].join("\n"))
|
|
|
|
end
|
|
|
|
else
|
|
|
|
existing
|
|
|
|
end
|
2014-12-28 10:32:08 -08:00
|
|
|
end
|
2014-12-28 14:26:16 -08:00
|
|
|
|
|
|
|
# @param upvote [Boolean]
|
|
|
|
def report_vote(report, upvote = false)
|
|
|
|
return unless mod?
|
|
|
|
ModerationVote.create!(user: self, report: report, upvote: upvote)
|
|
|
|
end
|
|
|
|
|
|
|
|
def report_unvote(report)
|
|
|
|
return unless mod?
|
|
|
|
ModerationVote.find_by(user: self, report: report).destroy
|
|
|
|
end
|
|
|
|
|
|
|
|
def report_voted?(report)
|
|
|
|
return false unless mod?
|
|
|
|
report.moderation_votes.each { |s| return true if s.user_id == self.id }
|
|
|
|
false
|
|
|
|
end
|
2014-12-28 14:57:07 -08:00
|
|
|
|
|
|
|
# @param upvote [Boolean]
|
|
|
|
def report_x_voted?(report, upvote)
|
|
|
|
return false unless mod?
|
|
|
|
report.moderation_votes.where(upvote: upvote).each { |s| return true if s.user_id == self.id }
|
|
|
|
false
|
|
|
|
end
|
2014-12-28 15:50:14 -08:00
|
|
|
|
|
|
|
def report_comment(report, content)
|
|
|
|
ModerationComment.create!(user: self, report: report, content: content)
|
|
|
|
end
|
2015-01-03 09:09:56 -08:00
|
|
|
# endregion
|
2014-12-29 05:51:52 -08:00
|
|
|
|
|
|
|
def cropping?
|
|
|
|
!crop_x.blank? && !crop_y.blank? && !crop_w.blank? && !crop_h.blank?
|
|
|
|
end
|
2015-04-22 17:56:29 -07:00
|
|
|
|
|
|
|
# forwards fill
|
|
|
|
def banned?
|
2015-04-22 18:18:17 -07:00
|
|
|
self.permanently_banned? or ((not self.banned_until.nil?) and self.banned_until >= DateTime.current)
|
2015-04-22 17:56:29 -07:00
|
|
|
end
|
|
|
|
|
|
|
|
def unban
|
|
|
|
self.update(permanently_banned: false, ban_reason: nil, banned_until: nil)
|
|
|
|
end
|
|
|
|
|
|
|
|
def ban(buntil=nil, reason=nil)
|
|
|
|
if buntil == nil
|
|
|
|
self.update(permanently_banned: true, ban_reason: reason)
|
|
|
|
else
|
|
|
|
self.update(permanently_banned: false, banned_until: buntil, ban_reason: reason)
|
|
|
|
end
|
|
|
|
end
|
2016-01-05 11:54:13 -08:00
|
|
|
|
|
|
|
def can_export?
|
2016-01-05 12:50:21 -08:00
|
|
|
unless self.export_created_at.nil?
|
|
|
|
return (Time.now > self.export_created_at.in(1.week)) && !self.export_processing
|
|
|
|
end
|
|
|
|
!self.export_processing
|
2016-01-05 11:54:13 -08:00
|
|
|
end
|
2014-08-01 03:07:16 -07:00
|
|
|
end
|