1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2024-12-11 22:24:21 +01:00
fab-manager/app/models/user.rb

392 lines
13 KiB
Ruby

class User < ActiveRecord::Base
include NotifyWith::NotificationReceiver
include NotifyWith::NotificationAttachedObject
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:confirmable, :async
rolify
# enable OmniAuth authentication only if needed
devise :omniauthable, :omniauth_providers => [AuthProvider.active.strategy_name.to_sym] unless AuthProvider.active.providable_type == DatabaseProvider.name
extend FriendlyId
friendly_id :username, use: :slugged
has_one :profile, dependent: :destroy
accepts_nested_attributes_for :profile
has_many :my_projects, foreign_key: :author_id, class_name: 'Project', dependent: :destroy
has_many :project_users, dependent: :destroy
has_many :projects, through: :project_users
has_many :reservations, dependent: :destroy
accepts_nested_attributes_for :reservations, allow_destroy: true
# Les formations sont déjà faites
has_many :user_trainings, dependent: :destroy
has_many :trainings, through: :user_trainings
belongs_to :group
has_many :subscriptions, dependent: :destroy
accepts_nested_attributes_for :subscriptions, allow_destroy: true
has_many :users_credits, dependent: :destroy
has_many :credits, through: :users_credits
has_many :training_credits, through: :users_credits, source: :training_credit
has_many :machine_credits, through: :users_credits, source: :machine_credit
has_many :invoices, dependent: :destroy
has_many :user_tags, dependent: :destroy
has_many :tags, through: :user_tags
accepts_nested_attributes_for :tags, allow_destroy: true
has_one :wallet, dependent: :destroy
has_many :exports, dependent: :destroy
# fix for create admin user
before_save do
self.email.downcase! if self.email
end
before_create :assign_default_role
after_create :create_a_wallet
after_commit :create_stripe_customer, on: [:create]
after_commit :notify_admin_when_user_is_created, on: :create
after_update :notify_admin_invoicing_changed, if: :invoicing_disabled_changed?
after_update :notify_group_changed, if: :group_id_changed?
attr_accessor :cgu
delegate :first_name, to: :profile
delegate :last_name, to: :profile
validate :cgu_must_accept, if: :new_record?
validates :username, presence: true, uniqueness: true, length: { maximum: 30 }
scope :active, -> { where(is_active: true) }
scope :without_subscription, -> { includes(:subscriptions).where(subscriptions: { user_id: nil }) }
scope :with_subscription, -> { joins(:subscriptions) }
def to_builder
Jbuilder.new do |json|
json.id id
json.username username
json.email email
json.role roles.first.name
json.group_id group_id
json.name profile.full_name
json.need_completion need_completion?
json.profile do
json.user_avatar do
json.id profile.user_avatar.id
json.attachment_url profile.user_avatar.attachment_url
end if profile.user_avatar
json.first_name profile.first_name
json.last_name profile.last_name
json.gender profile.gender.to_s
json.birthday profile.birthday.iso8601 if profile.birthday
json.interest profile.interest
json.software_mastered profile.software_mastered
json.address profile.address.address if profile.address
json.phone profile.phone
end
json.subscribed_plan do
json.id subscribed_plan.id
json.name subscribed_plan.name
json.base_name subscribed_plan.base_name
json.amount (subscribed_plan.amount / 100.0)
json.interval subscribed_plan.interval
json.interval_count subscribed_plan.interval_count
json.training_credit_nb subscribed_plan.training_credit_nb
json.training_credits subscribed_plan.training_credits do |tc|
json.training_id tc.creditable_id
end
json.machine_credits subscribed_plan.machine_credits do |mc|
json.machine_id mc.creditable_id
json.hours mc.hours
end
end if subscribed_plan
json.subscription do
json.id subscription.id
json.expired_at subscription.expired_at.iso8601
json.canceled_at subscription.canceled_at.iso8601 if subscription.canceled_at
json.stripe subscription.stp_subscription_id.present?
json.plan do
json.id subscription.plan.id
json.base_name subscription.plan.base_name
json.name subscription.plan.name
json.interval subscription.plan.interval
json.interval_count subscription.plan.interval_count
json.amount subscription.plan.amount ? (subscription.plan.amount / 100.0) : 0
end
end if subscription
json.training_credits training_credits do |tc|
json.training_id tc.creditable_id
end
json.machine_credits machine_credits do |mc|
json.machine_id mc.creditable_id
json.hours_used mc.users_credits.find_by_user_id(id).hours_used
end
json.last_sign_in_at last_sign_in_at.iso8601 if last_sign_in_at
end
end
def to_json(options)
to_builder.target!
end
def self.admins
User.with_role(:admin)
end
def is_training_machine?(machine)
return true if is_admin?
trainings.map do |t|
t.machines
end.flatten.uniq.include?(machine)
end
def training_reservation_by_machine(machine)
reservations.where(reservable_type: 'Training', reservable_id: machine.trainings.map(&:id)).first
end
def subscribed_plan
return nil if subscription.nil? or subscription.expired_at < Time.now
subscription.plan
end
def subscription
subscriptions.last
end
def is_admin?
has_role? :admin
end
def is_member?
has_role? :member
end
def all_projects
my_projects.to_a.concat projects
end
def generate_admin_invoice(offer_day = false, offer_day_start_at = nil)
if self.subscription
if offer_day
self.subscription.generate_and_save_offer_day_invoice(offer_day_start_at) unless self.invoicing_disabled?
else
self.subscription.generate_and_save_invoice unless self.invoicing_disabled?
end
end
end
def stripe_customer
Stripe::Customer.retrieve stp_customer_id
end
def soft_destroy
update_attribute(:is_active, false)
uninvolve_from_projects
end
def uninvolve_from_projects
my_projects.destroy_all
project_users.destroy_all
end
def active_for_authentication?
super and self.is_active?
end
def self.from_omniauth(auth)
active_provider = AuthProvider.active
if active_provider.strategy_name != auth.provider
raise SecurityError, 'The identity provider does not match the activated one'
end
where(provider: auth.provider, uid: auth.uid).first_or_create.tap do |user|
# execute this regardless of whether record exists or not (-> User#tap)
# this will init or update the user thanks to the informations retrieved from the SSO
user.profile ||= Profile.new
auth.info.mapping.each do |key, value|
user.set_data_from_sso_mapping(key, value)
end
user.password = Devise.friendly_token[0,20]
end
end
def need_completion?
profile.gender.nil? or profile.first_name.blank? or profile.last_name.blank? or username.blank? or
email.blank? or encrypted_password.blank? or group_id.nil? or profile.birthday.blank? or profile.phone.blank?
end
## Retrieve the requested data in the User and user's Profile tables
## @param sso_mapping {String} must be of form 'user._field_' or 'profile._field_'. Eg. 'user.email'
def get_data_from_sso_mapping(sso_mapping)
parsed = /^(user|profile)\.(.+)$/.match(sso_mapping)
if parsed[1] == 'user'
self[parsed[2].to_sym]
elsif parsed[1] == 'profile'
self.profile[parsed[2].to_sym]
end
end
## Set some data on the current user, according to the sso_key given
## @param sso_mapping {String} must be of form 'user._field_' or 'profile._field_'. Eg. 'user.email'
## @param data {*} the data to put in the given key. Eg. 'user@example.com'
def set_data_from_sso_mapping(sso_mapping, data)
if sso_mapping.to_s.start_with? 'user.'
self[sso_mapping[5..-1].to_sym] = data unless data.nil?
elsif sso_mapping.to_s.start_with? 'profile.'
case sso_mapping.to_s
when 'profile.avatar'
self.profile.user_avatar ||= UserAvatar.new
self.profile.user_avatar.remote_attachment_url = data
when 'profile.address'
self.profile.address ||= Address.new
self.profile.address.address = data
when 'profile.organization_name'
self.profile.organization ||= Organization.new
self.profile.organization.name = data
when 'profile.organization_address'
self.profile.organization ||= Organization.new
self.profile.organization.address ||= Address.new
self.profile.organization.address.address = data
else
self.profile[sso_mapping[8..-1].to_sym] = data unless data.nil?
end
end
end
## used to allow the migration of existing users between authentication providers
def generate_auth_migration_token
update_attributes(:auth_token => Devise.friendly_token)
end
## link the current user to the given provider (omniauth attributes hash)
## and remove the auth_token to mark his account as "migrated"
def link_with_omniauth_provider(auth)
active_provider = AuthProvider.active
if active_provider.strategy_name != auth.provider
raise SecurityError, 'The identity provider does not match the activated one'
end
if User.where(provider: auth.provider, uid: auth.uid).size > 0
raise DuplicateIndexError, "This #{active_provider.name} account is already linked to an existing user"
end
update_attributes(provider: auth.provider, uid: auth.uid, auth_token: nil)
end
## Merge the provided User's SSO details into the current user and drop the provided user to ensure the unity
## @param sso_user {User} the provided user will be DELETED after the merge was successful
def merge_from_sso(sso_user)
# update the attibutes to link the account to the sso account
self.provider = sso_user.provider
self.uid = sso_user.uid
# remove the token
self.auth_token = nil
self.merged_at = DateTime.now
# check that the email duplication was resolved
if sso_user.email.end_with? '-duplicate'
email_addr = sso_user.email.match(/^<([^>]+)>.{20}-duplicate$/)[1]
unless email_addr == self.email
raise DuplicateIndexError, email_addr
end
end
# update the user's profile to set the data managed by the SSO
auth_provider = AuthProvider.from_strategy_name(sso_user.provider)
auth_provider.sso_fields.each do |field|
value = sso_user.get_data_from_sso_mapping(field)
# we do not merge the email field if its end with the special value '-duplicate' as this means
# that the user is currently merging with the account that have the same email than the sso
unless field == 'user.email' and value.end_with? '-duplicate'
self.set_data_from_sso_mapping(field, value)
end
end
# run the account transfert in an SQL transaction to ensure data integrity
User.transaction do
# remove the temporary account
sso_user.destroy
# finally, save the new details
self.save!
end
end
protected
def confirmation_required?
false
end
private
def assign_default_role
add_role(:member) if self.roles.blank?
end
def cached_has_role?(role)
roles = Rails.cache.fetch(roles_for: { object_id: self.object_id }, expires_in: 1.day, race_condition_ttl: 2.seconds) { self.roles.map(&:name) }
roles.include?(role.to_s)
end
def cgu_must_accept
errors.add(:cgu, I18n.t('activerecord.errors.messages.empty')) if cgu == '0'
end
def create_stripe_customer
StripeWorker.perform_async(:create_stripe_customer, id)
end
def create_a_wallet
self.create_wallet
end
def notify_admin_when_user_is_created
if need_completion? and not provider.nil?
NotificationCenter.call type: 'notify_admin_when_user_is_imported',
receiver: User.admins,
attached_object: self
else
NotificationCenter.call type: 'notify_admin_when_user_is_created',
receiver: User.admins,
attached_object: self
end
end
def notify_group_changed
if changes[:group_id].first != nil
ex_group = Group.find(changes[:group_id].first)
meta_data = { ex_group_name: ex_group.name }
User.admins.each do |admin|
notification = Notification.new(meta_data: meta_data)
notification.send_notification(type: :notify_admin_user_group_changed, attached_object: self).to(admin).deliver_later
end
NotificationCenter.call type: :notify_user_user_group_changed,
receiver: self,
attached_object: self
end
end
def notify_admin_invoicing_changed
NotificationCenter.call type: 'notify_admin_invoicing_changed',
receiver: User.admins,
attached_object: self
end
end