app/models/user.rb
# == Schema Information
#
# Table name: users
#
# id :integer not null, primary key
# login :string
# identity_url :string
# name :string
# admin :boolean default(FALSE)
# bio :text
# signature :text
# role :string default("user")
# home_phone :string
# work_phone :string
# cell_phone :string
# company :string
# street :string
# city :string
# state :string
# zip :string
# title :string
# twitter :string
# linkedin :string
# thumbnail :string
# medium_image :string
# large_image :string
# language :string default("en")
# assigned_ticket_count :integer default(0)
# topics_count :integer default(0)
# active :boolean default(TRUE)
# created_at :datetime not null
# updated_at :datetime not null
# email :string default(""), not null
# encrypted_password :string default(""), not null
# reset_password_token :string
# reset_password_sent_at :datetime
# remember_created_at :datetime
# sign_in_count :integer default(0), not null
# current_sign_in_at :datetime
# last_sign_in_at :datetime
# current_sign_in_ip :inet
# last_sign_in_ip :inet
# provider :string
# uid :string
# invitation_token :string
# invitation_created_at :datetime
# invitation_sent_at :datetime
# invitation_accepted_at :datetime
# invitation_limit :integer
# invited_by_id :integer
# invited_by_type :string
# invitations_count :integer default(0)
# invitation_message :text
# time_zone :string default("UTC")
# profile_image :string
# notify_on_private :boolean default(FALSE)
# notify_on_public :boolean default(FALSE)
# notify_on_reply :boolean default(FALSE)
# account_number :string
# priority :string default("normal")
#
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable
devise :invitable, :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:omniauthable, :omniauth_providers => Devise.omniauth_providers
INVALID_NAME_CHARACTERS = /\A('|")|\d|('|")\z/
# Add preferences to user model
include RailsSettings::Extend
TEMP_EMAIL_PREFIX = 'change@me'
attr_accessor :opt_in
validates :name, presence: true
validates :email, presence: true
include Gravtastic
mount_uploader :profile_image, ProfileImageUploader
include PgSearch::Model
pg_search_scope :user_search,
against: [:name, :login, :email, :company, :account_number, :home_phone, :work_phone, :cell_phone]
paginates_per 15
# Relationships
has_many :topics, dependent: :destroy
has_many :posts, dependent: :destroy
has_many :votes, dependent: :destroy
has_many :docs
has_many :backups, dependent: :delete_all
has_many :api_keys, dependent: :destroy
has_attachment :avatar, accept: [:jpg, :png, :gif]
is_gravtastic
after_invitation_accepted :set_role_on_invitation_accept
after_create :enable_notifications_for_admin
before_save :reject_invalid_characters_from_name
acts_as_taggable_on :teams
ROLES = %w[admin agent editor user]
# TODO: Will want to refactor this using .or when upgrading to Rails 5
scope :admins, -> { where('admin = ? OR role = ?',true,'admin').order('name asc') }
scope :agents, -> { where('admin = ? OR role = ? OR role = ?',true,'admin','agent').order('name asc') }
scope :customers, -> { where('admin = ? and role = ?',false,'user').where.not(role: ['agent','admin','editor']).where.not(id: 2).order('name asc') }
scope :team, -> { where('admin = ? OR role = ? OR role = ? OR role = ?',true,'admin','agent','editor').order('name asc') }
scope :active, -> { where(active: true)}
scope :by_role, -> (role) { where(role: role) }
scope :active_first, -> { order('updated_at desc') }
scope :alpha, -> { order('name asc') }
scope :available, -> { where(status: 'available') }
def set_role_on_invitation_accept
self.role = self.role.presence || "agent"
self.active = true
self.save
end
def enable_notifications_for_admin
if self.role == "admin"
self.notify_on_private = true
self.notify_on_public = true
self.notify_on_reply = true
end
end
# Permanently destroy a user along with all owned records
# change doc ownership
def permanently_destroy
return if self.is_admin?
return if self.id == 2 #prevent the system user from being destroyed
DeleteUserJob.perform_later(self.id)
end
# Removes or anonymizes associated records
def scrub
return if self.is_admin?
return if self.id == 2 #prevent the system user from anonymized
# unassign from any topics assigned to
self.unassign_all
# "forget user"
self.anonymize
end
# anonymizes a users attributes
def anonymize
return if self.is_admin?
# anonymize own attributes
self.update!(
login: 'anon',
name: 'Anonymous User',
role: 'user',
signature: nil,
home_phone: nil,
work_phone: nil,
cell_phone: nil,
company: nil,
street: nil,
city: nil,
state: nil,
zip: nil,
title: nil,
twitter: nil,
linkedin: nil,
bio: nil,
thumbnail: nil,
medium_image: nil,
large_image: nil,
profile_image: nil,
current_sign_in_ip: nil,
last_sign_in_ip: nil,
encrypted_password: SecureRandom.hex(24),
account_number: nil,
active: false,
email: 'change@me-' + SecureRandom.hex(5) + '.anonymous'
)
# anonymize cached attributes (topics)
self.topics.update_all(user_name: 'Anonymized User')
# rebuild index
end
# Unassign from all tickets
def unassign_all
Topic.where(assigned_user_id: self.id).update_all(assigned_user_id: nil)
end
# Can this user be deleted? Protects admins/system user from accidental delete
def can_scrub_and_delete?
return false if self.id == 2 || self.is_admin?
true
end
# Is this user editable by the current logged in agent?
def can_be_edited? current_user
return true if current_user.is_admin?
!self.is_agent?
end
def self.notifiable_on_public
agents.where(notify_on_public: true).reorder('id asc')
end
def self.notifiable_on_private
agents.where(notify_on_private: true).reorder('id asc')
end
def self.notifiable_on_reply
agents.where(notify_on_reply: true).reorder('id asc')
end
def active_assigned_count
Topic.where(assigned_user_id: self.id).active.count
end
def is_restricted?
self.team_list.count > 0 && !self.is_admin?
end
def self.create_password
Devise.friendly_token
end
def thumbnail_url
self.thumbnail.blank? ? self.gravatar_url(size: 60) : self.thumbnail
end
def image_url
self.medium_image || self.gravatar_url(size: 60)
end
def self.find_for_oauth(auth)
user = find_by(email: auth.info.email)
if user
user.tap do |u|
u.provider = auth.provider
u.uid = auth.uid
u.save!
end
else
# NOTE: this stopped working with the test, not sure why. Replaced with
# find_or_create_by and everything passed again:
# where(provider: auth.provider, uid: auth.uid).first_or_create do |u|
# it turns out that is not part of the public rails api, despite being
# used in the wild. https://github.com/rails/rails/issues/23495
find_or_create_by(provider: auth.provider, uid: auth.uid) do |u|
u.email = auth.info.email.present? ? auth.info.email : u.temp_email(auth)
u.name = auth.info.name.present? ? auth.info.name : "Name Missing"
u.role = 'user'
u.thumbnail = auth.info.image
u.password = Devise.friendly_token[0,20]
end
end
end
def temp_email(auth)
"#{TEMP_EMAIL_PREFIX}-#{auth.uid}-#{auth.provider}.com"
end
def to_param
"#{id}-#{name.parameterize}"
end
def signup_guest
enc = Devise.token_generator.generate(User, :reset_password_token)
self.reset_password_token = enc
self.reset_password_sent_at = Time.now.utc
self.login = self.email.split("@")[0]
self.password = User.create_password
self.save
end
# evaluates to true if they are a priority (high/vip) user
def priority?
self.priority == 'high' || self.priority == 'vip'
end
# NOTE: Could have user AR Enumerables for this, but the field was already in the database as a string
# and changing it could be painful for upgrading installed users. These are three
# Utility methods for checking the role of an admin:
def is_admin?
self.role == 'admin'
end
def is_agent?
%w( agent admin ).include?(self.role)
end
def is_editor?
%w( editor agent admin ).include?(self.role)
end
def self.bulk_invite(emails, message, role)
#below line merge comma saperated emails as well as emails saperated by new lines
emails = emails.each_line.reject { |l| l =~ /^\s+$/ }.map { |l| l.strip.split(', ') }.flatten
emails.each do |email|
is_valid_email = email.match('^.+@.+$')
if is_valid_email
User.invite!({email: email}) do |user|
user.invitation_message = message
user.name = "Invited User: #{email}"
user.role = role
user.active = false
end
end
end
end
#when using deliver_later attr_accessor :message becomes nil on mailer view
def send_devise_notification(notification, *args)
devise_mailer.send(notification, self, *args).deliver_later
end
# check if user is active or not
def active_for_authentication?
super && self.active?
end
# message to the user that is not allowed to login
def inactive_message
"You are not allowed to sign in!"
end
def self.register email, user_name
# this method is very similar to email_processor#create_user
# actually it was copyied from there.
# it should create an issue to properly refactor and
# preserve the DRY principle.
# create user
usr = User.new
token, enc = Devise.token_generator.generate(User, :reset_password_token)
usr.reset_password_token = enc
usr.reset_password_sent_at = Time.now.utc
usr.email = email
usr.name = user_name
usr.password = User.create_password
if usr.save
UserMailer.new_user(usr.id, token).deliver_later
end
usr
end
private
def reject_invalid_characters_from_name
self.name = name.gsub(INVALID_NAME_CHARACTERS, '') if !!name.match(INVALID_NAME_CHARACTERS)
end
end