Newer
Older
dmpopidor / app / models / user.rb
class User < ActiveRecord::Base

  ##
  # Devise
  #   Include default devise modules. Others available are:
  #   :token_authenticatable, :confirmable,
  #   :lockable, :timeoutable and :omniauthable
  devise :invitable, :database_authenticatable, :registerable, :recoverable,
         :rememberable, :trackable, :validatable, :omniauthable,
         :omniauth_providers => [:shibboleth, :orcid]


  ##
  # User Notification Preferences
  serialize :prefs, Hash

  ##
  # Associations
  has_and_belongs_to_many :perms, join_table: :users_perms
  belongs_to :language
  belongs_to :org
  has_one  :pref
  has_many :answers
  has_many :notes
  has_many :exported_plans
  has_many :roles, dependent: :destroy
  has_many :plans, through: :roles do
    def filter(query)
      return self unless query.present?
      t = self.arel_table
      q = "%#{query}%"
      conditions = t[:title].matches(q)
      columns = %i(
        grant_number identifier description principal_investigator data_contact
      )
      columns = ['grant_number', 'identifier', 'description', 'principal_investigator', 'data_contact']
      columns.each {|col| conditions = conditions.or(t[col].matches(q)) }
      self.where(conditions)
    end
  end

  has_many :user_identifiers
  has_many :identifier_schemes, through: :user_identifiers

  validates :email, email: true, allow_nil: true, uniqueness: {message: _("must be unique")}

  ##
  # Scopes
  default_scope { includes(:org, :perms, :plans) }



  # EVALUATE CLASS AND INSTANCE METHODS BELOW
  #
  # What do they do? do they do it efficiently, and do we need them?

  # Determines the locale set for the user or the organisation he/she belongs
  # @return String or nil
  def get_locale
    if !self.language.nil?
      return self.language.abbreviation
    elsif !self.org.nil?
      return self.org.get_locale
    else
      return nil
    end
  end


  ##
  # gives either the name of the user, or the email if name unspecified
  #
  # @param user_email [Boolean] defaults to true, allows the use of email if there is no firstname or surname
  # @return [String] the email or the firstname and surname of the user
  def name(use_email = true)
    if (firstname.blank? && surname.blank?) || use_email then
      return email
    else
      name = "#{firstname} #{surname}"
      return name.strip
    end
  end

  ##
  # Returns the user's identifier for the specified scheme name
  #
  # @param the identifier scheme name (e.g. ORCID)
  # @return [UserIdentifier] the user's identifier for that scheme
  def identifier_for(scheme)
    user_identifiers.where(identifier_scheme: scheme).first
  end

# TODO: Check the logic here. Its deleting the permissions if the user does not have permission
#       to change orgs and either the incoming or existing org is nil.
#       We should also NOT be auto-saving here!!!
  ##
  # sets a new organisation id for the user
  # if the user has any perms such as org_admin or admin, those are removed
  # if the user had an api_token, that is removed
  #
  # @param new_organisation_id [Integer] the id for an organisation
  # @return [String] the empty string as a causality of setting api_token
  def org_id=(new_org_id)
    unless self.can_change_org? || new_org_id.nil? || self.org.nil? || (new_org_id.to_s == self.org.id.to_s)
      # rip all permissions from the user
      self.perms.delete_all
    end
    # set the user's new organisation
    super(new_org_id)
    self.save!
    # rip api permissions from the user
    self.remove_token!
  end

  ##
  # sets a new organisation for the user
  #
  # @param new_organisation [Organisation] the new organisation for the user
  def organisation=(new_org)
    org_id = new_org.id unless new_org.nil?
  end

  ##
  # checks if the user is a super admin
  # if the user has any privelege which requires them to see the super admin page
  # then they are a super admin
  #
  # @return [Boolean] true if the user is an admin
  def can_super_admin?
    return self.can_add_orgs? || self.can_grant_api_to_orgs? || self.can_change_org?
  end

  ##
  # checks if the user is an organisation admin
  # if the user has any privlege which requires them to see the org-admin pages
  # then they are an org admin
  #
  # @return [Boolean] true if the user is an organisation admin
  def can_org_admin?
    return self.can_grant_permissions? || self.can_modify_guidance? ||
           self.can_modify_templates? || self.can_modify_org_details?
  end

  ##
  # checks if the user can add new organisations
  #
  # @return [Boolean] true if the user can add new organisations
  def can_add_orgs?
    perms.include? Perm.add_orgs
  end

  ##
  # checks if the user can change their organisation affiliations
  #
  # @return [Boolean] true if the user can change their organisation affiliations
  def can_change_org?
    perms.include? Perm.change_affiliation
  end

  ##
  # checks if the user can grant their permissions to others
  #
  # @return [Boolean] true if the user can grant their permissions to others
  def can_grant_permissions?
    perms.include? Perm.grant_permissions
  end

  ##
  # checks if the user can modify organisation templates
  #
  # @return [Boolean] true if the user can modify organisation templates
  def can_modify_templates?
    self.perms.include? Perm.modify_templates
  end

  ##
  # checks if the user can modify organisation guidance
  #
  # @return [Boolean] true if the user can modify organistion guidance
  def can_modify_guidance?
    perms.include? Perm.modify_guidance
  end

  ##
  # checks if the user can use the api
  #
  # @return [Boolean] true if the user can use the api
  def can_use_api?
    perms.include? Perm.use_api
  end

  ##
  # checks if the user can modify their org's details
  #
  # @return [Boolean] true if the user can modify the org's details
  def can_modify_org_details?
    perms.include? Perm.change_org_details
  end


  ##
  # checks if the user can grant the api to organisations
  #
  # @return [Boolean] true if the user can grant api permissions to organisations
  def can_grant_api_to_orgs?
    perms.include? Perm.grant_api
  end

  ##
  # removes the api_token from the user
  # modifies the user model
  def remove_token!
    unless api_token.blank?
      self.api_token = ""
      self.save!
    end
  end

  ##
  # generates a new token for the user unless the user already has a token.
  # modifies the user's model.
  def keep_or_generate_token!
    if api_token.nil? || api_token.empty?
      self.api_token = loop do
        random_token = SecureRandom.urlsafe_base64(nil, false)
        break random_token unless User.exists?(api_token: random_token)
      end
      self.save!
      # send an email to the user to notify them of their new api token
      #UserMailer.api_token_granted_notification(self)
    end
  end

  ##
  # Load the user based on the scheme and id provided by the Omniauth call
  # --------------------------------------------------------------
  def self.from_omniauth(auth)
    scheme = IdentifierScheme.find_by(name: auth.provider.downcase)

    if scheme.nil?
      throw Exception.new('Unknown OAuth provider: ' + auth.provider)
    else
      joins(:user_identifiers).where('user_identifiers.identifier': auth.uid,
                   'user_identifiers.identifier_scheme_id': scheme.id).first
    end
  end

  ##
  # Return the user's preferences for a given base key
  #
  # @return [JSON] with symbols as keys
  def get_preferences(key)
    if self.pref.present? && self.pref.settings[key.to_s].present?
      return self.pref.settings[key.to_s].deep_symbolize_keys
    else
      return Pref.default_settings[key]
    end
  end

  ##
  # Override devise_invitable email title
  # --------------------------------------------------------------
  def deliver_invitation(options = {})
    super(options.merge(subject: _('A Data Management Plan in %{application_name} has been shared with you') % {application_name: Rails.configuration.branding[:application][:name]}))
  end

end