member.rb 5.5 KB
Newer Older
D
Dmitriy Zaporozhets 已提交
1
class Member < ActiveRecord::Base
2
  include Sortable
3
  include Importable
D
Dmitriy Zaporozhets 已提交
4 5
  include Gitlab::Access

6 7
  attr_accessor :raw_invite_token

8
  belongs_to :created_by, class_name: "User"
D
Dmitriy Zaporozhets 已提交
9 10 11
  belongs_to :user
  belongs_to :source, polymorphic: true

D
Douwe Maan 已提交
12
  validates :user, presence: true, unless: :invite?
D
Dmitriy Zaporozhets 已提交
13
  validates :source, presence: true
14
  validates :user_id, uniqueness: { scope: [:source_type, :source_id],
D
Douwe Maan 已提交
15 16
                                    message: "already exists in source",
                                    allow_nil: true }
17
  validates :access_level, inclusion: { in: Gitlab::Access.all_values }, presence: true
D
Douwe Maan 已提交
18 19 20 21
  validates :invite_email,
    presence: {
      if: :invite?
    },
22
    email: {
D
Douwe Maan 已提交
23 24 25 26 27 28
      allow_nil: true
    },
    uniqueness: {
      scope: [:source_type, :source_id],
      allow_nil: true
    }
D
Dmitriy Zaporozhets 已提交
29

R
Rémy Coutable 已提交
30
  scope :invite, -> { where.not(invite_token: nil) }
R
Rémy Coutable 已提交
31
  scope :non_invite, -> { where(invite_token: nil) }
R
Rémy Coutable 已提交
32
  scope :request, -> { where.not(requested_at: nil) }
33
  scope :has_access, -> { where('access_level > 0') }
34

35 36 37 38 39
  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) }
R
Rémy Coutable 已提交
40
  scope :owners_and_masters,  -> { where(access_level: [OWNER, MASTER]) }
41

D
Douwe Maan 已提交
42
  before_validation :generate_invite_token, on: :create, if: -> (member) { member.invite_email.present? }
43

44
  after_create :send_invite, if: :invite?, unless: :importing?
J
James Lopez 已提交
45 46 47 48
  after_create :send_request, if: :request?, unless: :importing?
  after_create :create_notification_setting, unless: [:pending?, :importing?]
  after_create :post_create_hook, unless: [:pending?, :importing?]
  after_update :post_update_hook, unless: [:pending?, :importing?]
49
  after_destroy :post_destroy_hook, unless: :pending?
D
Douwe Maan 已提交
50

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

53 54
  default_value_for :notification_level, NotificationSetting.levels[:global]

55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
  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)
74

75 76 77 78 79 80 81
      # `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
82

83
      if can_update_member?(current_user, member) || project_creator?(member, access_level)
84 85
        member.created_by ||= current_user
        member.access_level = access_level
86

87 88
        member.save
      end
89
    end
90 91 92

    private

93
    def can_update_member?(current_user, member)
D
Douwe Maan 已提交
94 95 96
      # There is no current user for bulk actions, in which case anything is allowed
      !current_user ||
        current_user.can?(:update_group_member, member) ||
97
        current_user.can?(:update_project_member, member)
98
    end
99 100 101 102 103

    def project_creator?(member, access_level)
      member.new_record? && member.owner? &&
        access_level.to_i == ProjectMember::MASTER
    end
D
Douwe Maan 已提交
104 105
  end

R
Rémy Coutable 已提交
106 107 108 109
  def real_source_type
    source_type
  end

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

114
  def request?
R
Rémy Coutable 已提交
115
    requested_at.present?
116 117
  end

R
Rémy Coutable 已提交
118 119
  def pending?
    invite? || request?
D
Douwe Maan 已提交
120 121
  end

R
Rémy Coutable 已提交
122
  def accept_request
123 124
    return false unless request?

R
Rémy Coutable 已提交
125
    updated = self.update(requested_at: nil)
R
Rémy Coutable 已提交
126
    after_accept_request if updated
127

R
Rémy Coutable 已提交
128
    updated
129 130
  end

D
Douwe Maan 已提交
131
  def accept_invite!(new_user)
D
Douwe Maan 已提交
132
    return false unless invite?
133

D
Douwe Maan 已提交
134 135 136 137 138 139 140 141 142 143 144 145
    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 已提交
146 147 148 149 150 151 152 153 154 155
  def decline_invite!
    return false unless invite?

    destroyed = self.destroy

    after_decline_invite if destroyed

    destroyed
  end

D
Douwe Maan 已提交
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
  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

174
  def create_notification_setting
175
    user.notification_settings.find_or_create_for(source)
176 177
  end

178
  def notification_setting
179
    @notification_setting ||= user.notification_settings_for(source)
180 181
  end

D
Douwe Maan 已提交
182 183 184 185 186 187
  private

  def send_invite
    # override in subclass
  end

R
Rémy Coutable 已提交
188
  def send_request
R
Rémy Coutable 已提交
189
    notification_service.new_access_request(self)
D
Douwe Maan 已提交
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
  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 已提交
208 209 210 211
  def after_decline_invite
    # override in subclass
  end

R
Rémy Coutable 已提交
212
  def after_accept_request
D
Douwe Maan 已提交
213 214 215 216 217 218 219 220 221 222
    post_create_hook
  end

  def system_hook_service
    SystemHooksService.new
  end

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