class User < ApplicationRecord include User::AnswerMethods include User::InboxMethods include User::QuestionMethods include User::RelationshipMethods include User::TimelineMethods # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable and :omniauthable devise :database_authenticatable, :async, :registerable, :recoverable, :rememberable, :trackable, :validatable, :confirmable, :authentication_keys => [:login] rolify # attr_accessor :login has_many :questions, dependent: :destroy has_many :answers, dependent: :destroy has_many :comments, dependent: :destroy has_many :inboxes, dependent: :destroy 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 has_many :smiles, dependent: :destroy has_many :comment_smiles, dependent: :destroy has_many :services, dependent: :destroy has_many :notifications, foreign_key: :recipient_id, dependent: :destroy has_many :reports, dependent: :destroy has_many :moderation_comments, dependent: :destroy has_many :moderation_votes, dependent: :destroy has_many :groups, dependent: :destroy has_many :group_memberships, class_name: "GroupMember", foreign_key: 'user_id', dependent: :destroy has_many :subscriptions, dependent: :destroy has_one :theme, dependent: :destroy SCREEN_NAME_REGEX = /\A[a-zA-Z0-9_]{1,16}\z/ WEBSITE_REGEX = /https?:\/\/([A-Za-z.\-]+)\/?(?:.*)/i before_validation do screen_name.strip! end validates :email, fake_email: true validates :screen_name, presence: true, format: { with: SCREEN_NAME_REGEX }, uniqueness: { case_sensitive: false }, screen_name: true validates :display_name, length: { maximum: 50 } validates :bio, length: { maximum: 200 } # validates :website, format: { with: WEBSITE_REGEX } has_attached_file :profile_picture, styles: { large: "500x500#", medium: "256x256#", small: "80x80#" }, default_url: "/images/:style/no_avatar.png", use_timestamp: false, processors: [:cropper] validates_attachment_content_type :profile_picture, :content_type => /\Aimage\/(png|jpe?g|gif)\Z/ 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] validates_attachment_content_type :profile_header, :content_type => /\Aimage\/(png|jpe?g)\Z/ process_in_background :profile_picture process_in_background :profile_header before_save do self.website = if website.match %r{\Ahttps?://} website else "http://#{website}" end unless website.blank? end # when a user deleted himself, all reports relating to the user are invalid before_destroy do rep = Report.where(target_id: self.id, type: 'Reports::User') rep.each do |r| unless r.nil? r.deleted = true r.save end end end def login=(login) @login = login end def login @login || self.screen_name || self.email end def self.find_first_by_auth_conditions(warden_conditions) conditions = warden_conditions.dup 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 # follows an user. def follow(target_user) active_relationships.create(target: target_user) end # unfollows an user def unfollow(target_user) active_relationships.find_by(target: target_user).destroy end # @return [Boolean] true if +self+ is following +target_user+ def following?(target_user) friends.include? target_user end # @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 # 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 # has the user answered +question+ yet? # @param question [Question] def answered?(question) question.answers.pluck(:user_id).include? self.id end # smiles an answer # @param answer [Answer] the answer to smile def smile(answer) Smile.create!(user: self, answer: answer) end # unsmile an answer # @param answer [Answer] the answer to unsmile def unsmile(answer) Smile.find_by(user: self, answer: answer).destroy end # 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 def smiled?(answer) answer.smiles.pluck(:user_id).include? self.id end def smiled_comment?(comment) comment.smiles.pluck(:user_id).include? self.id end def display_website website.match(/https?:\/\/([A-Za-z.\-0-9]+)\/?(?:.*)/i)[1] rescue NoMethodError website end def comment(answer, content) Comment.create!(user: self, answer: answer, content: content) end # @return [Boolean] is the user a moderator? def mod? has_role?(:moderator) || has_role?(:administrator) end # region stuff used for reporting/moderation def report(object, reason = nil) existing = Report.find_by(type: "Reports::#{object.class}", target_id: object.id, user_id: self.id, deleted: false) 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 end # @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 # @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 def report_comment(report, content) ModerationComment.create!(user: self, report: report, content: content) end # endregion def cropping? !crop_x.blank? && !crop_y.blank? && !crop_w.blank? && !crop_h.blank? end # forwards fill def banned? self.permanently_banned? or ((not self.banned_until.nil?) and self.banned_until >= DateTime.current) 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 def can_export? unless self.export_created_at.nil? return (Time.now > self.export_created_at.in(1.week)) && !self.export_processing end !self.export_processing end # %w[admin moderator].each do |m| # define_method(m) { raise "not allowed: #{m}" } # define_method(m+??) { raise "not allowed: #{m}?"} # define_method(m+?=) { |*a| raise "not allowed: #{m}="} # end end