member.rb 4.44 KB
Newer Older
Valery Sizov's avatar
Valery Sizov committed
1 2 3 4 5 6 7 8 9 10 11 12 13
# == Schema Information
#
# Table name: members
#
#  id                 :integer          not null, primary key
#  access_level       :integer          not null
#  source_id          :integer          not null
#  source_type        :string(255)      not null
#  user_id            :integer          not null
#  notification_level :integer          not null
#  type               :string(255)
#  created_at         :datetime
#  updated_at         :datetime
14 15 16 17
#  created_by_id      :integer
#  invite_email       :string
#  invite_token       :string
#  invite_accepted_at :datetime
Valery Sizov's avatar
Valery Sizov committed
18 19
#

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
20
class Member < ActiveRecord::Base
21
  include Sortable
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
22 23 24
  include Notifiable
  include Gitlab::Access

25
  belongs_to :created_by, class_name: "User"
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
26 27 28
  belongs_to :user
  belongs_to :source, polymorphic: true

Douwe Maan's avatar
Douwe Maan committed
29
  validates :user, presence: true, unless: :invite?
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
30
  validates :source, presence: true
Douwe Maan's avatar
Douwe Maan committed
31 32 33
  validates :user_id, uniqueness: { scope: [:source_type, :source_id], 
                                    message: "already exists in source",
                                    allow_nil: true }
34
  validates :access_level, inclusion: { in: Gitlab::Access.all_values }, presence: true
Douwe Maan's avatar
Douwe Maan committed
35 36 37
  validates :invite_email,  presence: { if: :invite? }, 
                            email: { strict_mode: true, allow_nil: true }, 
                            uniqueness: { scope: [:source_type, :source_id], allow_nil: true }
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
38

Douwe Maan's avatar
Douwe Maan committed
39 40
  scope :invite, -> { where(user_id: nil) }
  scope :non_invite, -> { where("user_id IS NOT NULL") }
41 42 43 44 45 46
  scope :guests, -> { where(access_level: GUEST) }
  scope :reporters, -> { where(access_level: REPORTER) }
  scope :developers, -> { where(access_level: DEVELOPER) }
  scope :masters,  -> { where(access_level: MASTER) }
  scope :owners,  -> { where(access_level: OWNER) }

Douwe Maan's avatar
Douwe Maan committed
47 48 49 50 51 52
  before_validation :generate_invite_token, on: :create, if: -> (member) { member.invite_email.present? }
  after_create :send_invite, if: :invite?
  after_create :post_create_hook, unless: :invite?
  after_update :post_update_hook, unless: :invite?
  after_destroy :post_destroy_hook, unless: :invite?

53
  delegate :name, :username, :email, to: :user, prefix: true
Douwe Maan's avatar
Douwe Maan committed
54

55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
  class << self
    def find_by_invite_token(invite_token)
      invite_token = Devise.token_generator.digest(self, :invite_token, invite_token)
      find_by(invite_token: invite_token)
    end

    # This method is used to find users that have been entered into the "Add members" field.
    # These can be the User objects directly, their IDs, their emails, or new emails to be invited.
    def user_for_id(user_id)
      return user_id if user_id.is_a?(User)

      user = User.find_by(id: user_id)
      user ||= User.find_by(email: user_id)
      user ||= user_id
      user
    end

    def add_user(members, user_id, access_level, current_user = nil)
      user = user_for_id(user_id)
      
      # `user` can be either a User object or an email to be invited
      if user.is_a?(User)
        member = members.find_or_initialize_by(user_id: user.id)
      else
        member = members.build
        member.invite_email = user
      end

      member.created_by ||= current_user
      member.access_level = access_level

      member.save
    end
Douwe Maan's avatar
Douwe Maan committed
88 89
  end

Douwe Maan's avatar
Douwe Maan committed
90 91 92 93 94
  def invite?
    self.invite_token.present?
  end

  def accept_invite!(new_user)
Douwe Maan's avatar
Douwe Maan committed
95 96
    return false unless invite?
    
Douwe Maan's avatar
Douwe Maan committed
97 98 99 100 101 102 103 104 105 106 107 108
    self.invite_token = nil
    self.invite_accepted_at = Time.now.utc

    self.user = new_user

    saved = self.save

    after_accept_invite if saved

    saved
  end

Douwe Maan's avatar
Douwe Maan committed
109 110 111 112 113 114 115 116 117 118
  def decline_invite!
    return false unless invite?

    destroyed = self.destroy

    after_decline_invite if destroyed

    destroyed
  end

Douwe Maan's avatar
Douwe Maan committed
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
  def generate_invite_token
    raw, enc = Devise.token_generator.generate(self.class, :invite_token)
    @raw_invite_token = raw
    self.invite_token = enc
  end

  def generate_invite_token!
    generate_invite_token && save(validate: false)
  end

  def resend_invite
    return unless invite?

    generate_invite_token! unless @raw_invite_token

    send_invite
  end

  private

  def send_invite
    # override in subclass
  end

  def post_create_hook
    system_hook_service.execute_hooks_for(self, :create)
  end

  def post_update_hook
    # override in subclass
  end

  def post_destroy_hook
    system_hook_service.execute_hooks_for(self, :destroy)
  end

  def after_accept_invite
    post_create_hook
  end

Douwe Maan's avatar
Douwe Maan committed
159 160 161 162
  def after_decline_invite
    # override in subclass
  end

Douwe Maan's avatar
Douwe Maan committed
163 164 165 166 167 168 169
  def system_hook_service
    SystemHooksService.new
  end

  def notification_service
    NotificationService.new
  end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
170
end