member.rb 4.8 KB
Newer Older
V
Valery Sizov 已提交
1 2 3 4 5 6 7 8
# == 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
S
Stan Hu 已提交
9
#  user_id            :integer
V
Valery Sizov 已提交
10 11 12 13
#  notification_level :integer          not null
#  type               :string(255)
#  created_at         :datetime
#  updated_at         :datetime
14
#  created_by_id      :integer
S
Stan Hu 已提交
15 16
#  invite_email       :string(255)
#  invite_token       :string(255)
17
#  invite_accepted_at :datetime
V
Valery Sizov 已提交
18 19
#

D
Dmitriy Zaporozhets 已提交
20
class Member < ActiveRecord::Base
21
  include Sortable
D
Dmitriy Zaporozhets 已提交
22 23 24
  include Notifiable
  include Gitlab::Access

25 26
  attr_accessor :raw_invite_token

27
  belongs_to :created_by, class_name: "User"
D
Dmitriy Zaporozhets 已提交
28 29 30
  belongs_to :user
  belongs_to :source, polymorphic: true

D
Douwe Maan 已提交
31
  validates :user, presence: true, unless: :invite?
D
Dmitriy Zaporozhets 已提交
32
  validates :source, presence: true
33
  validates :user_id, uniqueness: { scope: [:source_type, :source_id],
D
Douwe Maan 已提交
34 35
                                    message: "already exists in source",
                                    allow_nil: true }
36
  validates :access_level, inclusion: { in: Gitlab::Access.all_values }, presence: true
37
  validates :invite_email,  presence: { if: :invite? },
38 39 40 41 42 43 44 45 46
            email: {
              strict_mode: true,
              allow_nil: true
            },
            uniqueness: {
              scope: [:source_type,
                      :source_id],
              allow_nil: true
            }
D
Dmitriy Zaporozhets 已提交
47

D
Douwe Maan 已提交
48 49
  scope :invite, -> { where(user_id: nil) }
  scope :non_invite, -> { where("user_id IS NOT NULL") }
50 51 52 53 54 55
  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) }

D
Douwe Maan 已提交
56 57 58 59 60 61
  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?

62
  delegate :name, :username, :email, to: :user, prefix: true
D
Douwe Maan 已提交
63

64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
  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)
83

84 85 86 87 88 89 90
      # `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
91

92
      if can_update_member?(current_user, member)
93 94
        member.created_by ||= current_user
        member.access_level = access_level
95

96 97
        member.save
      end
98
    end
99 100 101

    private

102
    def can_update_member?(current_user, member)
103
      !current_user || current_user.can?(:update_group_member, member) ||
104
        current_user.can?(:update_project_member, member)
105
    end
D
Douwe Maan 已提交
106 107
  end

D
Douwe Maan 已提交
108 109 110 111 112
  def invite?
    self.invite_token.present?
  end

  def accept_invite!(new_user)
D
Douwe Maan 已提交
113
    return false unless invite?
114

D
Douwe Maan 已提交
115 116 117 118 119 120 121 122 123 124 125 126
    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

D
Douwe Maan 已提交
127 128 129 130 131 132 133 134 135 136
  def decline_invite!
    return false unless invite?

    destroyed = self.destroy

    after_decline_invite if destroyed

    destroyed
  end

D
Douwe Maan 已提交
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
  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

D
Douwe Maan 已提交
177 178 179 180
  def after_decline_invite
    # override in subclass
  end

D
Douwe Maan 已提交
181 182 183 184 185 186 187
  def system_hook_service
    SystemHooksService.new
  end

  def notification_service
    NotificationService.new
  end
D
Dmitriy Zaporozhets 已提交
188
end