Class: Member

Direct Known Subclasses

GroupMember, ProjectMember

Constant Summary collapse

AVATAR_SIZE =
40
ACCESS_REQUEST_APPROVERS_TO_BE_NOTIFIED_LIMIT =
10
STATE_ACTIVE =
0
STATE_AWAITING =
1

Constants included from UpdateHighestRole

UpdateHighestRole::HIGHEST_ROLE_JOB_DELAY, UpdateHighestRole::HIGHEST_ROLE_LEASE_TIMEOUT

Constants included from Gitlab::Access

Gitlab::Access::AccessDeniedError, Gitlab::Access::DEVELOPER, Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS, Gitlab::Access::GUEST, Gitlab::Access::MAINTAINER, Gitlab::Access::MAINTAINER_PROJECT_ACCESS, Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS, Gitlab::Access::MINIMAL_ACCESS, Gitlab::Access::NO_ACCESS, Gitlab::Access::NO_ONE_PROJECT_ACCESS, Gitlab::Access::OWNER, Gitlab::Access::OWNER_SUBGROUP_ACCESS, Gitlab::Access::PROTECTION_DEV_CAN_MERGE, Gitlab::Access::PROTECTION_DEV_CAN_PUSH, Gitlab::Access::PROTECTION_FULL, Gitlab::Access::PROTECTION_NONE, Gitlab::Access::REPORTER

Constants included from Expirable

Expirable::DAYS_TO_EXPIRE

Constants inherited from ApplicationRecord

ApplicationRecord::MAX_PLUCK

Instance Attribute Summary collapse

Attributes included from Importable

#imported, #importing

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Gitlab::Utils::StrongMemoize

#clear_memoization, #strong_memoize, #strong_memoized?

Methods included from Presentable

#present

Methods included from Gitlab::Access

all_values, human_access, #human_access, #human_access_with_none, human_access_with_none, options, options_with_none, options_with_owner, #owner?, project_creation_level_name, project_creation_options, project_creation_string_options, project_creation_string_values, project_creation_values, protection_options, protection_values, subgroup_creation_options, subgroup_creation_string_options, subgroup_creation_string_values, subgroup_creation_values, sym_options, sym_options_with_owner

Methods included from Expirable

#expired?, #expires?, #expires_soon?

Methods included from AfterCommitQueue

#run_after_commit, #run_after_commit_or_now

Methods inherited from ApplicationRecord

cached_column_list, #create_or_load_association, declarative_enum, default_select_columns, id_in, id_not_in, iid_in, pluck_primary_key, primary_key_in, #readable_by?, safe_ensure_unique, safe_find_or_create_by, safe_find_or_create_by!, #to_ability_name, underscore, where_exists, where_not_exists, with_fast_read_statement_timeout, without_order

Methods included from SensitiveSerializableHash

#serializable_hash

Instance Attribute Details

#blocking_refresh=(value) ⇒ Object

Sets the attribute blocking_refresh

Parameters:

  • value

    the value to set the attribute blocking_refresh to.


25
26
27
# File 'app/models/member.rb', line 25

def blocking_refresh=(value)
  @blocking_refresh = value
end

#raw_invite_tokenObject

Returns the value of attribute raw_invite_token.


24
25
26
# File 'app/models/member.rb', line 24

def raw_invite_token
  @raw_invite_token
end

Class Method Details

.access_for_user_ids(user_ids) ⇒ Object


260
261
262
# File 'app/models/member.rb', line 260

def access_for_user_ids(user_ids)
  where(user_id: user_ids).has_access.pluck(:user_id, :access_level).to_h
end

.filter_by_2fa(value) ⇒ Object


228
229
230
231
232
233
234
235
236
237
# File 'app/models/member.rb', line 228

def filter_by_2fa(value)
  case value
  when 'enabled'
    left_join_users.merge(User.with_two_factor)
  when 'disabled'
    left_join_users.merge(User.without_two_factor)
  else
    all
  end
end

.find_by_invite_token(raw_invite_token) ⇒ Object


264
265
266
267
# File 'app/models/member.rb', line 264

def find_by_invite_token(raw_invite_token)
  invite_token = Devise.token_generator.digest(self, :invite_token, raw_invite_token)
  find_by(invite_token: invite_token)
end

.left_join_usersObject


256
257
258
# File 'app/models/member.rb', line 256

def left_join_users
  left_outer_joins(:user)
end

.search(query) ⇒ Object


220
221
222
# File 'app/models/member.rb', line 220

def search(query)
  joins(:user).merge(User.search(query, use_minimum_char_limit: false))
end

.search_invite_email(query) ⇒ Object


224
225
226
# File 'app/models/member.rb', line 224

def search_invite_email(query)
  invite.where(['invite_email ILIKE ?', "%#{query}%"])
end

.sort_by_attribute(method) ⇒ Object


239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# File 'app/models/member.rb', line 239

def sort_by_attribute(method)
  case method.to_s
  when 'access_level_asc' then reorder(access_level: :asc)
  when 'access_level_desc' then reorder(access_level: :desc)
  when 'recent_sign_in' then 
  when 'oldest_sign_in' then 
  when 'recent_created_user' then order_recent_created_user
  when 'oldest_created_user' then order_oldest_created_user
  when 'recent_last_activity' then order_recent_last_activity
  when 'oldest_last_activity' then order_oldest_last_activity
  when 'last_joined' then order_created_desc
  when 'oldest_joined' then order_created_asc
  else
    order_by(method)
  end
end

.valid_email?(email) ⇒ Boolean

Returns:

  • (Boolean)

269
270
271
# File 'app/models/member.rb', line 269

def valid_email?(email)
  Devise.email_regexp.match?(email)
end

Instance Method Details

#accept_invite!(new_user) ⇒ Object


309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
# File 'app/models/member.rb', line 309

def accept_invite!(new_user)
  return false unless invite?
  return false unless new_user

  self.user = new_user
  return false unless self.user.save

  self.invite_token = nil
  self.invite_accepted_at = Time.current.utc

  saved = self.save

  after_accept_invite if saved

  saved
end

#accept_requestObject


300
301
302
303
304
305
306
307
# File 'app/models/member.rb', line 300

def accept_request
  return false unless request?

  updated = self.update(requested_at: nil)
  after_accept_request if updated

  updated
end

#access_fieldObject


278
279
280
# File 'app/models/member.rb', line 278

def access_field
  access_level
end

#create_notification_settingObject


362
363
364
# File 'app/models/member.rb', line 362

def create_notification_setting
  user.notification_settings.find_or_create_for(source)
end

#created_by_nameObject


396
397
398
# File 'app/models/member.rb', line 396

def created_by_name
  created_by&.name
end

#decline_invite!Object


326
327
328
329
330
331
332
333
334
# File 'app/models/member.rb', line 326

def decline_invite!
  return false unless invite?

  destroyed = self.destroy

  after_decline_invite if destroyed

  destroyed
end

#destroy_notification_settingObject


366
367
368
# File 'app/models/member.rb', line 366

def destroy_notification_setting
  notification_setting&.destroy
end

#generate_invite_tokenObject


336
337
338
339
340
# File 'app/models/member.rb', line 336

def generate_invite_token
  raw, enc = Devise.token_generator.generate(self.class, :invite_token)
  @raw_invite_token = raw
  self.invite_token = enc
end

#generate_invite_token!Object


342
343
344
# File 'app/models/member.rb', line 342

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

#highest_group_memberObject

Find the user's group member with a highest access level


384
385
386
387
388
389
390
# File 'app/models/member.rb', line 384

def highest_group_member
  strong_memoize(:highest_group_member) do
    next unless user_id && source&.ancestors&.any?

    GroupMember.where(source: source.ancestors, user_id: user_id).order(:access_level).last
  end
end

#hook_prerequisites_met?Boolean

Returns:

  • (Boolean)

294
295
296
297
298
# File 'app/models/member.rb', line 294

def hook_prerequisites_met?
  # It is essential that an associated user record exists
  # so that we can successfully fire any member related hooks/notifications.
  user.present?
end

#invite?Boolean

Returns:

  • (Boolean)

282
283
284
# File 'app/models/member.rb', line 282

def invite?
  self.invite_token.present?
end

#invite_to_unknown_user?Boolean

Returns:

  • (Boolean)

392
393
394
# File 'app/models/member.rb', line 392

def invite_to_unknown_user?
  invite? && user_id.nil?
end

#notifiable?(type, opts = {}) ⇒ Boolean

rubocop: disable CodeReuse/ServiceClass

Returns:

  • (Boolean)

375
376
377
378
379
380
# File 'app/models/member.rb', line 375

def notifiable?(type, opts = {})
  # always notify when there isn't a user yet
  return true if user.blank?

  NotificationRecipients::BuildService.notifiable?(user, type, notifiable_options.merge(opts))
end

#notification_settingObject


370
371
372
# File 'app/models/member.rb', line 370

def notification_setting
  @notification_setting ||= user&.notification_settings_for(source)
end

#pending?Boolean

Returns:

  • (Boolean)

290
291
292
# File 'app/models/member.rb', line 290

def pending?
  invite? || request?
end

#real_source_typeObject


274
275
276
# File 'app/models/member.rb', line 274

def real_source_type
  source_type
end

#request?Boolean

Returns:

  • (Boolean)

286
287
288
# File 'app/models/member.rb', line 286

def request?
  requested_at.present?
end

#resend_inviteObject


346
347
348
349
350
351
352
# File 'app/models/member.rb', line 346

def resend_invite
  return unless invite?

  generate_invite_token! unless @raw_invite_token

  send_invite
end

#send_invitation_reminder(reminder_index) ⇒ Object


354
355
356
357
358
359
360
# File 'app/models/member.rb', line 354

def send_invitation_reminder(reminder_index)
  return unless invite?

  generate_invite_token! unless @raw_invite_token

  run_after_commit_or_now { notification_service.invite_member_reminder(self, @raw_invite_token, reminder_index) }
end