fablabbcn/fablabs

View on GitHub
app/models/user.rb

Summary

Maintainability
B
4 hrs
Test Coverage
class User < ActiveRecord::Base

  has_many :access_grants, class_name: "Doorkeeper::AccessGrant",
                           foreign_key: :resource_owner_id,
                           dependent: :delete_all # or :destroy if you need callbacks

  has_many :access_tokens, class_name: "Doorkeeper::AccessToken",
                           foreign_key: :resource_owner_id,
                           dependent: :delete_all # or :destroy if you need callbacks

  has_many :oauth_applications, class_name: 'Doorkeeper::Application', as: :owner

  include Tokenable
  include Authority::UserAbilities
  include Authority::Abilities
  self.authorizer_name = 'UserAuthorizer'

  before_create :generate_fab10_coupon_code

  extend DragonflyValidations
  dragonfly_accessor :avatar do
    default 'public/default-user-avatar.png'
  end
  dragonfly_validations(:avatar)

  include Workflow
  include WorkflowActiverecord
  include VerifyWorkflow

  rolify
  has_secure_password

  has_many :created_organizations, class_name: 'Organization', foreign_key: 'creator_id'
  has_many :created_events, class_name: 'Event', foreign_key: 'creator_id'
  has_many :created_labs, class_name: 'Lab', foreign_key: 'creator_id'
  has_many :created_projects, class_name: 'Project', foreign_key: 'owner_id'
  has_many :comments, foreign_key: 'author_id'
  has_many :discussions, foreign_key: 'creator_id'
  has_many :recoveries
  has_many :role_applications
  has_many :employees
  has_many :jobs
  has_one  :coupon

  has_many :academics

  has_many :links, as: :linkable
  accepts_nested_attributes_for :links, reject_if: lambda{ |l| l[:url].blank? }, allow_destroy: true

  has_many :created_activities, foreign_key: 'creator_id', class_name: 'Activity'
  has_many :activities, foreign_key: 'actor_id'

  has_many :contributions, foreign_key: 'contributor_id'
  has_many :projects, through: :contributions

  has_many :favourites
  has_many :projects, :through => :favourites

  has_many :grades
  has_many :projects, :through => :grades

  has_many :approval_workflow_logs

  validates_acceptance_of :agree_policy_terms, :accept => true, on: :create

  # validates_format_of :email, :with => /\A(.+)@(.+)\z/
  validates :email, format: { with: URI::MailTo::EMAIL_REGEXP, message: "only allows valid emails" }

  validates_format_of :email_fallback, :with => /\A(.+)@(.+)\z/, allow_blank: true

  validates :username, format: { :with => /\A[a-zA-Z0-9]+([\.-])?[a-zA-Z0-9]+\z/ }, length: { minimum: 4, maximum: 50 }
  
  validates :first_name, :last_name, :email, :username, presence: true
  validates_uniqueness_of :email, :username, case_sensitive: false
  validates_uniqueness_of :email_fallback, allow_blank: true
  validates :password, presence: true, length: { minimum: 6 }, if: lambda{ !password.nil? }, on: :update

  scope :verified, -> { where(workflow_state: :verified) }
  scope :unverified, -> { where(workflow_state: :unverified) }

  extend FriendlyId
  friendly_id :slug_candidates, use: [:slugged, :finders]
  def slug_candidates
    [:username]
  end

  before_create { generate_token(:email_validation_hash) }
  before_create :downcase_email

  after_save :discourse_sync_if_needed, if: -> { ENV['DISCOURSE_ENABLED'] }

  def avatar_url
    if avatar_uid.present?
      avatar.thumb('150x150#').url
    else
      default_url = "https://www.fablabs.io/default-user-avatar.png"
      gravatar_id = Digest::MD5.hexdigest(email.downcase)
      "https://gravatar.com/avatar/#{gravatar_id}.png?s=150&d=#{CGI.escape(default_url)}"
    end
  end

  def studied_at? lab
    academics.where(lab: lab).exists?
  end

  def employed_by? lab
    Employee.with_approved_state.where(lab: lab, user: self).exists?
  end

  def applied_to? lab
    Employee.where(lab: lab, user: self).exists?
  end

  def full_name
    "#{first_name} #{last_name}"
  end

  def to_s
    full_name
  end

  def default_locale
    locale || I18n.default_locale
  end

  def superadmin?
    has_role? :superadmin
  end

  def admin_labs
    self.roles.where(name: "admin", resource_type: "Lab")
  end

  def is_unique_referee?
    return true if not Lab.where("referee_id IN (?)",  self.admin_labs.map{ |u| u.resource_id }).empty?
  end

  def is_referee?
    return true if not RefereeApprovalProcess.where("referee_lab_id IN (?)", self.admin_labs.map{ |u| u.resource_id }).empty?
  end

  def is_creator? lab
    return true if lab.creator_id == self.id
  end

  def favourited? project_id
    return true if self.favourites.where(project_id: project_id).first
  end

  def graded? project_id
    return true if self.grades.where(project_id: project_id).first
  end

  def favourite project_id
    return self.favourites.where(project_id: project_id).first
  end

  def grade project_id
    return self.grades.where(project_id: project_id).first
  end

  def passed_spam_period?
    return true if superadmin?

    created_at < 2.months.ago
  end

  def unique_referee_labs
    Lab.where("referee_id IN (?) AND workflow_state IN (?)", self.admin_labs.map{ |u| u.resource_id }, ['unverified', 'more_info_needed', 'more_info_added'])
  end

  def pending_referee_labs
    labs = RefereeApprovalProcess.where(referee_lab_id: admin_labs.map(&:resource_id)).map(&:referred_lab).uniq
    labs.select{ |lab| lab if ['unverified', 'need_more_info', 'more_info_added'].include?(lab&.workflow_state) }
  end

  def referee_labs
    RefereeApprovalProcess.where(referee_lab_id: admin_labs.map(&:resource_id)).map(&:referred_lab).uniq
  end

  def referees_count
    unique_referee_labs.count + pending_referee_labs.count
  end

  def recovery_key
    recoveries.last.key if recoveries.any?
  end

  def unverify
    generate_token(:email_validation_hash)
  end

  def email_string
    "\"#{self}\" <#{self.email}>"
  end

  def self.admin_emails
    User.with_role(:superadmin).map(&:email)
  end

  # def coupon_code
  #   SecureRandom.urlsafe_base64[0..10].gsub(/[^0-9a-zA-Z]/i, '')
  # end

  def generate_fab10_coupon_code
    if self.fab10_coupon_code.blank?
      self.fab10_coupon_code = loop do
        random_token = SecureRandom.urlsafe_base64[0..10].gsub(/[^0-9a-zA-Z]/i, '')
        break random_token unless User.exists?(fab10_coupon_code: random_token)
      end
    end

    if employees.with_approved_state.any?
      self.fab10_cost = 35000
    end
  end

  def role_names
    roles.map(&:name).uniq.join(', ')
  end

  def async_discourse_sync
    DiscourseUserSyncWorker.perform_async(self.id)
  end

  def discourse_profile_url
    return '' if username.blank?

    "#{ENV['DISCOURSE_ENDPOINT']}/u/#{username}/summary"
  end

  private

  def discourse_sync_if_needed
    if (changes.keys & ["first_name", "last_name", "username", "email"]).present?
      async_discourse_sync
    end
  end

  def downcase_email
    self.email.downcase!
  end

  def self.ransackable_attributes(auth_object = nil)
    publicCols = ['id', 'first_name', 'last_name', 'username', 'country_code'] 
      
    if auth_object == :superadmin
      publicCols + ['email', 'workflow_state']
    else
      publicCols
    end
  end

  def self.ransackable_associations(auth_object = nil)
    if auth_object == :superadmin
      %w[roles]
    else
      %w[]
    end
  end

end