app/models/user.rb
# frozen_string_literal: true
# == Schema Information
#
# Table name: users
#
# id :bigint(8) not null, primary key
# 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
# confirmation_token :string
# confirmed_at :datetime
# confirmation_sent_at :datetime
# unconfirmed_email :string
# failed_attempts :integer default(0), not null
# unlock_token :string
# locked_at :datetime
# created_at :datetime not null
# updated_at :datetime not null
# username :string
# events_count :integer default(0), not null
# avatar_thumb_url :string default(""), not null
# avatar_url :string default(""), not null
# external :boolean default(FALSE)
#
class User < ApplicationRecord
include BlocksJsonSerialization
rolify
OMNIAUTH_PROVIDERS = %i[google_oauth2 facebook].freeze
# TODO: move to Boyutluseyler::Regex module
# TODO: https://github.com/presidentbeef/brakeman/wiki/How-to-Report-a-Brakeman-Issue#false-positivesfalse-negatives
# * Output: /\A[a-zA-ZğüşıöçĞÜŞİÖÇ0-9]+(?:[._-][a-zA-ZğüşıöçĞÜŞİÖÇ0-9]+)*\z/
# * Test: https://rubular.com/r/NVt4RvIc6c4ZEL
USERNAME_REGEX =
/
\A # start of string
[a-zA-ZğüşıöçĞÜŞİÖÇ0-9]+ # one or more ASCII letters digits with TR characters support
(?:[._-][a-zA-ZğüşıöçĞÜŞİÖÇ0-9]+)* # 0+ sequences of:
# [._-] a . or _ or -
# [a-zA-ZğüşıöçĞÜŞİÖÇ0-9]+ one or more ASCII letters digits
\z # end of string
/x.freeze
IMG_EXTS = %w[png jpg jpeg gif].freeze
# TODO: move to Boyutluseyler::Regex module
# * Output: /.(png|jpg|jpeg|gif)\z/i
# * Test: https://rubular.com/r/zmGjZaI8J8QMFN
# * No escape characters
# * No variables
# * . Any single character
# * a|b a or b
# * \z End of string
# * i Case insensitive
IMG_EXTS_REGEX = /.(#{IMG_EXTS.join("|")})\z/i.freeze
# validatable adds validations for email and password
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable,
:confirmable, :lockable, :trackable,
:omniauthable, omniauth_providers: OMNIAUTH_PROVIDERS
has_many :designs
has_many :events, class_name: 'Ahoy::Event', dependent: :destroy
has_many :identities, dependent: :destroy, autosave: true
has_many :users_roles, dependent: :destroy
has_many :roles, through: :users_roles
has_one :user_avatar, dependent: :destroy
# Virtual attribute for authenticating by either username or email
attr_accessor :login
# Add an accessor so you can have a field to validate that is seperate from
# password, password_confirmation or password_digest...
attr_accessor :current_password
validates :username, presence: true, uniqueness: { case_sensitive: false },
length: { in: 3..30 }, format: { with: USERNAME_REGEX }
validates :password, confirmation: true
validates :avatar_url, presence: true, format: { with: IMG_EXTS_REGEX }, on: :update
validate :avatar_filename_is_blank, on: :update
class << self
# Devise method overridden to allow sign in with email or username
def find_for_database_authentication(warden_conditions)
conditions = warden_conditions.dup
login = conditions.delete(:login)
where(conditions).find_by('lower(username) = :value OR lower(email) =
:value', value: login.downcase(:turkic).strip)
end
def find_by_username(username)
find_by('lower(username) = :username', username: username.downcase(:turkic))
end
def clean_username(username)
username = username.dup
# Get the email username by removing everything after an `@` sign.
username.gsub!(/@.*\z/, '')
# Remove everything that's not in the list of allowed characters.
username.gsub!(/[^a-zA-ZğüşıöçĞÜŞİÖÇ0-9_\-\.]/, '')
# Remove trailing violations ('.', '_', '-)
username.gsub!(/(\.|\_|\-)*\z/, '')
# Remove leading violations ('.', '_', '-)
username.gsub!(/\A(\.|\_|\-)*/, '')
# Users with the great usernames of "." or ".." would end up with a blank username.
# Work around that by setting their username to "blank", followed by a counter.
username = 'blank' if username.blank?
uniquify = Uniquify.new
uniquify.string(username) { |s| find_by_username(s) } # rubocop:disable Rails/DynamicFindBy
end
end
def recently_sent_password_reset?
reset_password_sent_at.present? && reset_password_sent_at >= 1.minute.ago
end
def skip_confirmation=(bool)
skip_confirmation! if bool
end
private
def avatar_filename_is_blank
# user input: '.png'
filename = Boyutluseyler::PathHelper.info(avatar_url, :filename)
errors.add(:avatar_url, :blank) if filename.blank?
end
end