class User < ActiveRecord::Base include Verification devise :database_authenticatable, :registerable, :confirmable, :recoverable, :rememberable, :trackable, :validatable, :omniauthable, :async, :password_expirable, :secure_validatable, authentication_keys: [:login] acts_as_voter acts_as_paranoid column: :hidden_at include ActsAsParanoidAliases include Graphqlable has_one :administrator has_one :moderator has_one :valuator has_one :manager has_one :poll_officer, class_name: "Poll::Officer" has_one :organization has_one :lock has_many :flags has_many :identities, dependent: :destroy has_many :debates, -> { with_hidden }, foreign_key: :author_id has_many :proposals, -> { with_hidden }, foreign_key: :author_id has_many :budget_investments, -> { with_hidden }, foreign_key: :author_id, class_name: 'Budget::Investment' has_many :comments, -> { with_hidden } has_many :spending_proposals, foreign_key: :author_id has_many :failed_census_calls has_many :notifications has_many :direct_messages_sent, class_name: 'DirectMessage', foreign_key: :sender_id has_many :direct_messages_received, class_name: 'DirectMessage', foreign_key: :receiver_id has_many :legislation_answers, class_name: 'Legislation::Answer', dependent: :destroy, inverse_of: :user has_many :follows belongs_to :geozone validates :username, presence: true, if: :username_required? validates :username, uniqueness: { scope: :registering_with_oauth }, if: :username_required? validates :document_number, uniqueness: { scope: :document_type }, allow_nil: true validate :validate_username_length validates :official_level, inclusion: {in: 0..5} validates :terms_of_service, acceptance: { allow_nil: false }, on: :create validates_associated :organization, message: false accepts_nested_attributes_for :organization, update_only: true attr_accessor :skip_password_validation attr_accessor :use_redeemable_code attr_accessor :login scope :administrators, -> { joins(:administrator) } scope :moderators, -> { joins(:moderator) } scope :organizations, -> { joins(:organization) } scope :officials, -> { where("official_level > 0") } scope :newsletter, -> { where(newsletter: true) } scope :for_render, -> { includes(:organization) } scope :by_document, ->(document_type, document_number) do where(document_type: document_type, document_number: document_number) end scope :email_digest, -> { where(email_digest: true) } scope :active, -> { where(erased_at: nil) } scope :erased, -> { where.not(erased_at: nil) } scope :public_for_api, -> { all } scope :by_comments, ->(query, topics_ids) { joins(:comments).where(query, topics_ids).uniq } scope :by_authors, ->(author_ids) { where("users.id IN (?)", author_ids) } scope :by_username_email_or_document_number, ->(search_string) do string = "%#{search_string}%" where("username ILIKE ? OR email ILIKE ? OR document_number ILIKE ?", string, string, string) end before_validation :clean_document_number # Get the existing user by email if the provider gives us a verified email. def self.first_or_initialize_for_oauth(auth) oauth_email = auth.info.email oauth_email_confirmed = oauth_email.present? && (auth.info.verified || auth.info.verified_email) oauth_user = User.find_by(email: oauth_email) if oauth_email_confirmed oauth_user || User.new( username: auth.info.name || auth.uid, email: oauth_email, oauth_email: oauth_email, password: Devise.friendly_token[0, 20], terms_of_service: '1', confirmed_at: oauth_email_confirmed ? DateTime.current : nil ) end def name organization? ? organization.name : username end def debate_votes(debates) voted = votes.for_debates(Array(debates).map(&:id)) voted.each_with_object({}) { |v, h| h[v.votable_id] = v.value } end def proposal_votes(proposals) voted = votes.for_proposals(Array(proposals).map(&:id)) voted.each_with_object({}) { |v, h| h[v.votable_id] = v.value } end def legislation_proposal_votes(proposals) voted = votes.for_legislation_proposals(proposals) voted.each_with_object({}) { |v, h| h[v.votable_id] = v.value } end def spending_proposal_votes(spending_proposals) voted = votes.for_spending_proposals(spending_proposals) voted.each_with_object({}) { |v, h| h[v.votable_id] = v.value } end def budget_investment_votes(budget_investments) voted = votes.for_budget_investments(budget_investments) voted.each_with_object({}) { |v, h| h[v.votable_id] = v.value } end def comment_flags(comments) comment_flags = flags.for_comments(comments) comment_flags.each_with_object({}){ |f, h| h[f.flaggable_id] = true } end def voted_in_group?(group) votes.for_budget_investments(Budget::Investment.where(group: group)).exists? end def administrator? administrator.present? end def moderator? moderator.present? end def valuator? valuator.present? end def manager? manager.present? end def poll_officer? poll_officer.present? end def organization? organization.present? end def verified_organization? organization && organization.verified? end def official? official_level && official_level > 0 end def add_official_position!(position, level) return if position.blank? || level.blank? update official_position: position, official_level: level.to_i end def remove_official_position! update official_position: nil, official_level: 0 end def has_official_email? domain = Setting['email_domain_for_officials'] email.present? && ((email.end_with? "@#{domain}") || (email.end_with? ".#{domain}")) end def display_official_position_badge? return true if official_level > 1 official_position_badge? && official_level == 1 end def block debates_ids = Debate.where(author_id: id).pluck(:id) comments_ids = Comment.where(user_id: id).pluck(:id) proposal_ids = Proposal.where(author_id: id).pluck(:id) investment_ids = Budget::Investment.where(author_id: id).pluck(:id) proposal_notification_ids = ProposalNotification.where(author_id: id).pluck(:id) hide Debate.hide_all debates_ids Comment.hide_all comments_ids Proposal.hide_all proposal_ids Budget::Investment.hide_all investment_ids ProposalNotification.hide_all proposal_notification_ids end def erase(erase_reason = nil) update( erased_at: Time.current, erase_reason: erase_reason, username: nil, email: nil, unconfirmed_email: nil, phone_number: nil, encrypted_password: "", confirmation_token: nil, reset_password_token: nil, email_verification_token: nil, confirmed_phone: nil, unconfirmed_phone: nil ) identities.destroy_all end def erased? erased_at.present? end def take_votes_if_erased_document(document_number, document_type) erased_user = User.erased.where(document_number: document_number) .where(document_type: document_type).first if erased_user.present? take_votes_from(erased_user) erased_user.update(document_number: nil, document_type: nil) end end def take_votes_from(other_user) return if other_user.blank? Poll::Voter.where(user_id: other_user.id).update_all(user_id: id) Budget::Ballot.where(user_id: other_user.id).update_all(user_id: id) Vote.where("voter_id = ? AND voter_type = ?", other_user.id, "User").update_all(voter_id: id) data_log = "id: #{other_user.id} - #{Time.current.strftime('%Y-%m-%d %H:%M:%S')}" update(former_users_data_log: "#{former_users_data_log} | #{data_log}") end def locked? Lock.find_or_create_by(user: self).locked? end def self.search(term) term.present? ? where("email = ? OR username ILIKE ?", term, "%#{term}%") : none end def self.username_max_length @@username_max_length ||= columns.find { |c| c.name == 'username' }.limit || 60 end def self.minimum_required_age (Setting['min_age_to_participate'] || 16).to_i end def show_welcome_screen? verification = Setting["feature.user.skip_verification"].present? ? true : unverified? sign_in_count == 1 && verification && !organization && !administrator? end def password_required? return false if skip_password_validation super end def username_required? !organization? && !erased? end def email_required? !erased? && unverified? end def locale self[:locale] ||= I18n.default_locale.to_s end def confirmation_required? super && !registering_with_oauth end def send_oauth_confirmation_instructions if oauth_email != email update(confirmed_at: nil) send_confirmation_instructions end update(oauth_email: nil) if oauth_email.present? end def name_and_email "#{name} (#{email})" end def age Age.in_years(date_of_birth) end def save_requiring_finish_signup begin self.registering_with_oauth = true save(validate: false) # Devise puts unique constraints for the email the db, so we must detect & handle that rescue ActiveRecord::RecordNotUnique self.email = nil save(validate: false) end true end def ability @ability ||= Ability.new(self) end delegate :can?, :cannot?, to: :ability def public_proposals public_activity? ? proposals : User.none end def public_debates public_activity? ? debates : User.none end def public_comments public_activity? ? comments : User.none end # overwritting of Devise method to allow login using email OR username def self.find_for_database_authentication(warden_conditions) conditions = warden_conditions.dup login = conditions.delete(:login) where(conditions.to_hash).where(["lower(email) = ?", login.downcase]).first || where(conditions.to_hash).where(["username = ?", login]).first end def interests followables = follows.map(&:followable) followables.compact.map { |followable| followable.tags.map(&:name) }.flatten.compact.uniq end private def clean_document_number return unless document_number.present? self.document_number = document_number.gsub(/[^a-z0-9]+/i, "").upcase end def validate_username_length validator = ActiveModel::Validations::LengthValidator.new( attributes: :username, maximum: User.username_max_length) validator.validate(self) end end