Class: Group

Defined Under Namespace

Classes: CrmSettings

Constant Summary collapse

README_PROJECT_PATH =
'gitlab-profile'

Constants included from WithUploads

WithUploads::FILE_UPLOADERS

Constants included from Avatarable

Avatarable::ALLOWED_IMAGE_SCALER_WIDTHS, Avatarable::COMBINED_AVATAR_SIZES, Avatarable::COMBINED_AVATAR_SIZES_RETINA, Avatarable::GROUP_AVATAR_SIZES, Avatarable::MAXIMUM_FILE_SIZE, Avatarable::PROJECT_AVATAR_SIZES, Avatarable::USER_AVATAR_SIZES

Constants inherited from Namespace

Namespace::NUMBER_OF_ANCESTORS_ALLOWED, Namespace::SHARED_RUNNERS_SETTINGS, Namespace::SR_DISABLED_AND_OVERRIDABLE, Namespace::SR_DISABLED_AND_UNOVERRIDABLE, Namespace::SR_ENABLED, Namespace::STATISTICS_COLUMNS, Namespace::URL_MAX_LENGTH

Constants included from BlocksUnsafeSerialization

BlocksUnsafeSerialization::UnsafeSerializationError

Constants included from Namespaces::Traversal::Linear

Namespaces::Traversal::Linear::UnboundedSearch

Constants included from Gitlab::SQL::Pattern

Gitlab::SQL::Pattern::MIN_CHARS_FOR_PARTIAL_MATCHING, Gitlab::SQL::Pattern::REGEX_QUOTED_TERM

Constants included from Gitlab::VisibilityLevel

Gitlab::VisibilityLevel::INTERNAL, Gitlab::VisibilityLevel::PRIVATE, Gitlab::VisibilityLevel::PUBLIC

Constants included from CacheMarkdownField

CacheMarkdownField::INVALIDATED_BY

Constants inherited from ApplicationRecord

ApplicationRecord::MAX_PLUCK

Constants included from HasCheckConstraints

HasCheckConstraints::NOT_NULL_CHECK_PATTERN

Constants included from ResetOnColumnErrors

ResetOnColumnErrors::MAX_RESET_PERIOD

Instance Attribute Summary

Attributes included from Importable

#importing, #user_contributions

Attributes inherited from Namespace

#emails_enabled_memoized, #root_ancestor

Attributes included from CacheMarkdownField

#skip_markdown_cache_validation

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Gitlab::Utils::Override

extended, extensions, included, method_added, override, prepended, queue_verification, verify!

Methods included from RunnerTokenExpirationInterval

#effective_runner_token_expiration_interval, #effective_runner_token_expiration_interval_human_readable, #enforced_runner_token_expiration_interval_human_readable

Methods included from ChronicDurationAttribute

#chronic_duration_attributes, #output_chronic_duration_attribute

Methods included from GroupAPICompatibility

#project_creation_level_str, #project_creation_level_str=, #subgroup_creation_level_str, #subgroup_creation_level_str=

Methods included from WithUploads

#retrieve_upload

Methods included from FastDestroyAll::Helpers

#perform_fast_destroy

Methods included from AfterCommitQueue

#run_after_commit, #run_after_commit_or_now

Methods included from GroupDescendant

build_hierarchy, #hierarchy

Methods included from LoadedInGroupList

#children_count, #guest_count, #has_subgroups?, #member_count, #project_count, #subgroup_count

Methods included from Avatarable

#avatar_path, #avatar_type, #uncached_avatar_path, #upload_paths

Methods included from AccessRequestable

#request_access

Methods included from Gitlab::ConfigHelper

#gitlab_config, #gitlab_config_features

Methods inherited from Namespace

#actual_limits, #actual_plan, #actual_plan_name, #aggregation_scheduled?, #all_ancestors_have_runner_registration_enabled?, #all_catalog_resources, #all_container_repositories, #all_project_ids_except, #all_projects, #all_projects_except_soft_deleted, #all_projects_with_pages, #allow_runner_registration_token?, #any_project_has_container_registry_tags?, #any_project_with_pages_deployed?, #any_project_with_shared_runners_enabled?, #archive, #archived?, #auto_devops_enabled?, #bot_user_namespace?, by_path, #certificate_based_clusters_enabled?, #changing_allow_descendants_override_disabled_shared_runners_is_allowed, #changing_shared_runners_enabled_is_allowed, clean_name, clean_path, #closest_setting, #container_repositories_size, #container_repositories_size_cache_key, #default_branch_protected?, #default_branch_protection, #default_branch_protection_settings, #emails_disabled?, #emails_enabled?, #enabled_git_access_protocol, find_by_path_or_name, #find_fork_of, find_top_level, #first_auto_devops_config, #first_project_with_container_registry_tags, #full_path_before_last_save, gfm_autocomplete_search, #group_namespace?, #has_parent?, #issue_repositioning_disabled?, #kind, #licensed_feature_available?, #linked_to_subscription?, #multiple_issue_boards_available?, #owner_required?, #package_settings, #pages_access_control_forced_by_ancestor?, #pages_access_control_forced_by_self_or_ancestor?, #pages_access_control_trie, #paid?, #pipeline_variables_default_role, #project_namespace?, #recent?, reference_pattern, reference_prefix, #root?, search, #self_deletion_in_progress?, #self_or_ancestor_archived?, #send_update_instructions, #shared_runners, #shared_runners_setting, #shared_runners_setting_higher_than?, sti_class_for, #subgroup?, sum_project_statistics_column, #to_param, #to_reference, #to_reference_base, #traversal_ids_as_sql, #unarchive, #uploads_sharding_key, #user_namespace?, username_reserved?, #visibility_level_field, #web_url

Methods included from Referable

#referable_inspect, #reference_link_text, #to_reference, #to_reference_base

Methods included from Ci::NamespaceSettings

#allow_stale_runner_pruning=, #allow_stale_runner_pruning?

Methods included from BlocksUnsafeSerialization

#serializable_hash

Methods included from Namespaces::AdjournedDeletable

#delayed_deletion_ready?, #deletion_adjourned_period, #deletion_in_progress_or_scheduled_in_hierarchy_chain?, #first_scheduled_for_deletion_in_hierarchy_chain, #scheduled_for_deletion_in_hierarchy_chain?, #self_deletion_in_progress?, #self_deletion_scheduled?, #self_deletion_scheduled_deletion_created_on

Methods included from Namespaces::Traversal::Cached

#all_project_ids, #self_and_descendant_ids

Methods included from Namespaces::Traversal::Linear

#all_project_ids, #ancestor_ids, #ancestors, #ancestors_upto, #descendants, #parent=, #parent_id=, #root_ancestor, #self_and_ancestor_ids, #self_and_ancestors, #self_and_descendant_ids, #self_and_descendants, #self_and_hierarchy, #traversal_path, #use_traversal_ids?

Methods included from Namespaces::Traversal::Recursive

#all_project_ids, #ancestor_ids, #ancestors, #ancestors_upto, #descendants, #object_hierarchy, #root_ancestor, #self_and_ancestor_ids, #self_and_ancestors, #self_and_descendant_ids, #self_and_descendants, #self_and_hierarchy

Methods included from FeatureGate

#flipper_id

Methods included from Gitlab::SQL::Pattern

split_query_to_search_terms

Methods included from Routable

#build_full_path, find_by_full_path, #full_name, #full_path, #full_path_components, #parent_loaded?, #route_loaded?

Methods included from Gitlab::VisibilityLevel

allowed_for?, allowed_level?, allowed_levels, allowed_levels_for_user, closest_allowed_level, #internal?, level_name, level_value, levels_for_user, non_restricted_level?, options, #private?, #public?, public_visibility_restricted?, restricted_level?, string_level, string_options, string_values, valid_level?, #visibility, #visibility=, #visibility_attribute_present?, #visibility_attribute_value, #visibility_level_attributes, #visibility_level_previous_changes, #visibility_level_value

Methods included from CacheMarkdownField

#attribute_invalidated?, #banzai_render_context, #cached_html_for, #cached_html_up_to_date?, #can_cache_field?, #invalidated_markdown_cache?, #latest_cached_markdown_version, #mentionable_attributes_changed?, #mentioned_filtered_user_ids_for, #parent_user, #refresh_markdown_cache, #refresh_markdown_cache!, #rendered_field_content, #skip_project_check?, #store_mentions!, #store_mentions_after_commit?, #updated_cached_html_for

Methods inherited from ApplicationRecord

===, cached_column_list, #create_or_load_association, declarative_enum, default_select_columns, id_in, id_not_in, iid_in, nullable_column?, 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 ResetOnColumnErrors

#reset_on_union_error, #reset_on_unknown_attribute_error

Methods included from Gitlab::SensitiveSerializableHash

#serializable_hash

Class Method Details

.descendant_groups_countsObject



450
451
452
# File 'app/models/group.rb', line 450

def descendant_groups_counts
  left_joins(:children).group(:id).count(:children_namespaces)
end

.get_ids_by_ids_or_paths(ids, paths) ⇒ Object



446
447
448
# File 'app/models/group.rb', line 446

def get_ids_by_ids_or_paths(ids, paths)
  by_ids_or_paths(ids, paths).pluck(:id)
end

.group_members_countsObject



458
459
460
# File 'app/models/group.rb', line 458

def group_members_counts
  left_joins(:group_members).group(:id).count(:members)
end

.groups_user_can(groups, user, action, same_root: false) ⇒ Object



411
412
413
414
415
# File 'app/models/group.rb', line 411

def groups_user_can(groups, user, action, same_root: false)
  DeclarativePolicy.user_scope do
    groups.select { |group| Ability.allowed?(user, action, group) }
  end
end

.ids_with_disabled_email(groups) ⇒ Object

Returns the ids of the passed group models where the ‘emails_enabled` column is set to false anywhere in the ancestor hierarchy.



428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
# File 'app/models/group.rb', line 428

def ids_with_disabled_email(groups)
  inner_groups = Group.where('id = namespaces_with_emails_disabled.id')
  inner_query = inner_groups
    .self_and_ancestors
    .joins(:namespace_settings)
    .where(namespace_settings: { emails_enabled: false })
    .select('1')
    .limit(1)

  group_ids = Namespace
    .from('(SELECT * FROM namespaces) as namespaces_with_emails_disabled')
    .where(namespaces_with_emails_disabled: { id: groups })
    .where('EXISTS (?)', inner_query)
    .pluck(:id)

  Set.new(group_ids)
end

.preset_root_ancestor_for(groups) ⇒ Object

This method can be used only if all groups have the same top-level group



419
420
421
422
423
424
# File 'app/models/group.rb', line 419

def preset_root_ancestor_for(groups)
  return groups if groups.size < 2

  root = groups.first.root_ancestor
  groups.drop(1).each { |group| group.root_ancestor = root }
end

.prevent_project_creation?(user, project_creation_setting) ⇒ Boolean

Returns:

  • (Boolean)


496
497
498
499
500
501
# File 'app/models/group.rb', line 496

def prevent_project_creation?(user, project_creation_setting)
  return true if project_creation_setting == ::Gitlab::Access::NO_ONE_PROJECT_ACCESS
  return false if user.can_admin_all_resources?

  project_creation_setting == ::Gitlab::Access::ADMINISTRATOR_PROJECT_ACCESS
end

.project_creation_levels_for_user(user) ⇒ Object

Handle project creation permissions based on application setting and group setting. The ‘default_project_creation` application setting is the default value and can be overridden by the `project_creation_level` group setting. `nil` value of namespaces.project_creation_level` means that allowed creation level has not been explicitly set by the group owner and is a placeholder value for inheriting the value from the ApplicationSetting.



470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
# File 'app/models/group.rb', line 470

def project_creation_levels_for_user(user)
  project_creation_allowed_on_levels = [
    ::Gitlab::Access::DEVELOPER_PROJECT_ACCESS,
    ::Gitlab::Access::MAINTAINER_PROJECT_ACCESS,
    ::Gitlab::Access::OWNER_PROJECT_ACCESS,
    nil
  ]

  if user.can_admin_all_resources?
    project_creation_allowed_on_levels << ::Gitlab::Access::ADMINISTRATOR_PROJECT_ACCESS
  end

  default_project_creation = ::Gitlab::CurrentSettings.default_project_creation
  prevent_project_creation_by_default = prevent_project_creation?(user, default_project_creation)

  # Remove nil (i.e. inherited `default_project_creation`) when the application setting is:
  # 1. NO_ONE_PROJECT_ACCESS
  # 2. ADMINISTRATOR_PROJECT_ACCESS and the user is not an admin
  #
  # To prevent showing groups in the namespaces dropdown on the project creation page that have no explicit group
  # setting for `project_creation_level`.
  project_creation_allowed_on_levels.delete(nil) if prevent_project_creation_by_default

  project_creation_allowed_on_levels
end

.projects_countsObject



454
455
456
# File 'app/models/group.rb', line 454

def projects_counts
  left_joins(:non_archived_projects).group(:id).count(:projects)
end

.public_or_visible_to_user(user) ⇒ Object

WARNING: This method should never be used on its own please do make sure the number of rows you are filtering is small enough for this query



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

def public_or_visible_to_user(user)
  return public_to_user unless user

  public_for_user = public_to_user_arel(user)
  visible_for_user = visible_to_user_arel(user)
  public_or_visible = public_for_user.or(visible_for_user)

  where(public_or_visible)
end

.select_for_project_authorizationObject



392
393
394
395
396
397
398
399
400
# File 'app/models/group.rb', line 392

def select_for_project_authorization
  if current_scope.joins_values.include?(:shared_projects)
    joins('INNER JOIN namespaces project_namespace ON project_namespace.id = projects.namespace_id')
      .where(project_namespace: { share_with_group_lock: false })
      .select("projects.id AS project_id", "LEAST(project_group_links.group_access, members.access_level) AS access_level")
  else
    super
  end
end

.sort_by_attribute(method) ⇒ Object



364
365
366
367
368
369
370
371
372
373
374
375
376
377
# File 'app/models/group.rb', line 364

def sort_by_attribute(method)
  case method.to_s
  when 'storage_size_desc'
    # storage_size is a virtual column so we need to
    # pass a string to avoid AR adding the table name
    reorder('storage_size DESC, namespaces.id DESC')
  when 'path_asc'
    order_path_asc
  when 'path_desc'
    order_path_desc
  else
    order_by(method)
  end
end

.sti_nameObject



32
33
34
# File 'app/models/group.rb', line 32

def self.sti_name
  'Group'
end

.supported_keyset_orderingsObject



36
37
38
# File 'app/models/group.rb', line 36

def self.supported_keyset_orderings
  { name: [:asc] }
end

.with_api_scopesObject



462
463
464
# File 'app/models/group.rb', line 462

def with_api_scopes
  preload(:namespace_settings, :group_feature, :parent, :deletion_schedule)
end

.without_integration(integration) ⇒ Object



402
403
404
405
406
407
408
409
# File 'app/models/group.rb', line 402

def without_integration(integration)
  integrations = Integration
    .select('1')
    .where("#{Integration.table_name}.group_id = namespaces.id")
    .where(type: integration.type)

  where('NOT EXISTS (?)', integrations)
end

Instance Method Details

#access_level_rolesObject



992
993
994
# File 'app/models/group.rb', line 992

def access_level_roles
  GroupMember.access_level_roles
end

#access_level_valuesObject



996
997
998
# File 'app/models/group.rb', line 996

def access_level_values
  access_level_roles.values
end

#access_request_approvers_to_be_notifiedObject



932
933
934
# File 'app/models/group.rb', line 932

def access_request_approvers_to_be_notified
  members.owners.connected_to_user..limit(Member::ACCESS_REQUEST_APPROVERS_TO_BE_NOTIFIED_LIMIT)
end

#active?Boolean

Returns:

  • (Boolean)


1199
1200
1201
# File 'app/models/group.rb', line 1199

def active?
  self_and_ancestors.inactive.none?
end

#add_developer(user, current_user = nil) ⇒ Object



639
640
641
# File 'app/models/group.rb', line 639

def add_developer(user, current_user = nil)
  add_member(user, :developer, current_user: current_user)
end

#add_guest(user, current_user = nil) ⇒ Object



627
628
629
# File 'app/models/group.rb', line 627

def add_guest(user, current_user = nil)
  add_member(user, :guest, current_user: current_user)
end

#add_maintainer(user, current_user = nil) ⇒ Object



643
644
645
# File 'app/models/group.rb', line 643

def add_maintainer(user, current_user = nil)
  add_member(user, :maintainer, current_user: current_user)
end

#add_member(user, access_level) ⇒ Object



623
624
625
# File 'app/models/group.rb', line 623

def add_member(user, access_level, ...)
  Members::Groups::CreatorService.add_member(self, user, access_level, ...) # rubocop:disable CodeReuse/ServiceClass
end

#add_members(users, access_level, current_user: nil, expires_at: nil) ⇒ Object



613
614
615
616
617
618
619
620
621
# File 'app/models/group.rb', line 613

def add_members(users, access_level, current_user: nil, expires_at: nil)
  Members::Groups::CreatorService.add_members( # rubocop:disable CodeReuse/ServiceClass
    self,
    users,
    access_level,
    current_user: current_user,
    expires_at: expires_at
  )
end

#add_owner(user, current_user = nil) ⇒ Object



647
648
649
# File 'app/models/group.rb', line 647

def add_owner(user, current_user = nil)
  add_member(user, :owner, current_user: current_user)
end

#add_planner(user, current_user = nil) ⇒ Object



631
632
633
# File 'app/models/group.rb', line 631

def add_planner(user, current_user = nil)
  add_member(user, :planner, current_user: current_user)
end

#add_reporter(user, current_user = nil) ⇒ Object



635
636
637
# File 'app/models/group.rb', line 635

def add_reporter(user, current_user = nil)
  add_member(user, :reporter, current_user: current_user)
end

#authorizable_members_with_parentsObject



800
801
802
# File 'app/models/group.rb', line 800

def authorizable_members_with_parents
  Members::MembersWithParents.new(self).all_members.authorizable
end

#blocked_ownersObject



663
664
665
# File 'app/models/group.rb', line 663

def blocked_owners
  members.blocked.where(access_level: Gitlab::Access::OWNER)
end

#botsObject



897
898
899
# File 'app/models/group.rb', line 897

def bots
  users.project_bot
end

#cluster_agentsObject



1195
1196
1197
# File 'app/models/group.rb', line 1195

def cluster_agents
  ::Clusters::Agent.for_projects(all_projects)
end

#continue_indented_text_feature_flag_enabled?Boolean

Returns:

  • (Boolean)


1080
1081
1082
# File 'app/models/group.rb', line 1080

def continue_indented_text_feature_flag_enabled?
  feature_flag_enabled_for_self_or_ancestor?(:continue_indented_text, type: :wip)
end

#create_group_level_work_items_feature_flag_enabled?Boolean

Returns:

  • (Boolean)


1097
1098
1099
# File 'app/models/group.rb', line 1097

def create_group_level_work_items_feature_flag_enabled?
  ::Feature.enabled?(:create_group_level_work_items, self, type: :wip)
end

#crm_enabled?Boolean

Returns:

  • (Boolean)


1040
1041
1042
# File 'app/models/group.rb', line 1040

def crm_enabled?
  crm_settings.nil? || crm_settings.enabled?
end

#crm_groupObject



1168
1169
1170
1171
1172
1173
# File 'app/models/group.rb', line 1168

def crm_group
  Group.id_in_ordered(traversal_ids.reverse)
    .joins(:crm_settings)
    .where.not(crm_settings: { source_group_id: nil })
    .first&.crm_settings&.source_group || root_ancestor
end

#crm_group?Boolean

Returns:

  • (Boolean)


1176
1177
1178
1179
1180
# File 'app/models/group.rb', line 1176

def crm_group?
  return true if root? && crm_settings&.source_group_id.nil?

  crm_targets.present?
end

#default_branch_nameObject



988
989
990
# File 'app/models/group.rb', line 988

def default_branch_name
  namespace_settings&.default_branch_name
end

#delete_contactsObject



1187
1188
1189
# File 'app/models/group.rb', line 1187

def delete_contacts
  CustomerRelations::Contact.where(group_id: id).delete_all
end

#delete_organizationsObject



1191
1192
1193
# File 'app/models/group.rb', line 1191

def delete_organizations
  CustomerRelations::Organization.where(group_id: id).delete_all
end

#dependency_proxy_feature_available?Boolean

Returns:

  • (Boolean)


544
545
546
# File 'app/models/group.rb', line 544

def dependency_proxy_feature_available?
  ::Gitlab.config.dependency_proxy.enabled
end

#dependency_proxy_for_containers_policy_subjectObject



1137
1138
1139
# File 'app/models/group.rb', line 1137

def dependency_proxy_for_containers_policy_subject
  ::Packages::Policies::DependencyProxy::Group.new(self)
end

#dependency_proxy_image_prefixObject



554
555
556
557
558
559
560
561
# File 'app/models/group.rb', line 554

def dependency_proxy_image_prefix
  # The namespace path can include uppercase letters, which
  # Docker doesn't allow. The proxy expects it to be downcased.
  url = "#{Gitlab::Routing.url_helpers.group_url(self).downcase}#{DependencyProxy::URL_SUFFIX}"

  # Docker images do not include the protocol
  url.partition('//').last
end

#dependency_proxy_image_ttl_policyObject



1028
1029
1030
# File 'app/models/group.rb', line 1028

def dependency_proxy_image_ttl_policy
  super || build_dependency_proxy_image_ttl_policy
end

#dependency_proxy_settingObject



1032
1033
1034
# File 'app/models/group.rb', line 1032

def dependency_proxy_setting
  super || build_dependency_proxy_setting
end

#descendant_project_members_with_inactiveObject



835
836
837
838
839
840
# File 'app/models/group.rb', line 835

def descendant_project_members_with_inactive
  ProjectMember
    .with_source_id(all_projects)
    .non_request
    .non_invite
end

#direct_membersObject



794
795
796
797
798
# File 'app/models/group.rb', line 794

def direct_members
  GroupMember.active_without_invites_and_requests
             .non_minimal_access
             .where(source_id: id)
end

#enforced_runner_token_expiration_intervalObject



1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
# File 'app/models/group.rb', line 1048

def enforced_runner_token_expiration_interval
  all_parent_groups = Gitlab::ObjectHierarchy.new(Group.where(id: id)).ancestors
  all_group_settings = NamespaceSetting.where(namespace_id: all_parent_groups)
  group_interval = all_group_settings.where.not(subgroup_runner_token_expiration_interval: nil).minimum(:subgroup_runner_token_expiration_interval)&.seconds

  [
    Gitlab::CurrentSettings.group_runner_token_expiration_interval&.seconds,
    group_interval
  ].compact.min
end

#execute_hooks(data, hooks_scope) ⇒ Object



960
961
962
963
# File 'app/models/group.rb', line 960

def execute_hooks(data, hooks_scope)
  # NOOP
  # TODO: group hooks https://gitlab.com/gitlab-org/gitlab/-/issues/216904
end

#execute_integrations(data, hooks_scope) ⇒ Object



969
970
971
972
973
# File 'app/models/group.rb', line 969

def execute_integrations(data, hooks_scope)
  integrations.public_send(hooks_scope).each do |integration| # rubocop:disable GitlabSecurity/PublicSend
    integration.async_execute(data)
  end
end

#export_archive_exists?(user) ⇒ Boolean

Returns:

  • (Boolean)


956
957
958
# File 'app/models/group.rb', line 956

def export_archive_exists?(user)
  import_export_upload_by_user(user)&.export_archive_exists?
end

#export_file(user) ⇒ Object



952
953
954
# File 'app/models/group.rb', line 952

def export_file(user)
  import_export_upload_by_user(user)&.export_file
end

#export_file_exists?(user) ⇒ Boolean

Returns:

  • (Boolean)


948
949
950
# File 'app/models/group.rb', line 948

def export_file_exists?(user)
  import_export_upload_by_user(user)&.export_file_exists?
end

#feature_available?(feature, user = nil) ⇒ Boolean

Returns:

  • (Boolean)


1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
# File 'app/models/group.rb', line 1116

def feature_available?(feature, user = nil)
  # when we check the :issues feature at group level we need to check the `epics` license feature instead
  feature = feature == :issues ? :epics : feature

  if ::Groups::FeatureSetting.available_features.include?(feature)
    group_feature.feature_available?(feature, user) # rubocop:disable Gitlab/FeatureAvailableUsage
  else
    super
  end
end

#find_or_initialize_integration(integration) ⇒ Object



965
966
967
# File 'app/models/group.rb', line 965

def find_or_initialize_integration(integration)
  Integration.find_or_initialize_non_project_specific_integration(integration, group_id: id)
end

#first_ownerObject



982
983
984
985
986
# File 'app/models/group.rb', line 982

def first_owner
  first_owner_member = all_group_members.all_owners.order(:user_id).first

  first_owner_member&.user || parent&.first_owner || owner
end

#gitlab_deploy_tokenObject



1127
1128
1129
1130
1131
# File 'app/models/group.rb', line 1127

def gitlab_deploy_token
  strong_memoize(:gitlab_deploy_token) do
    deploy_tokens.gitlab_deploy_token
  end
end

#glql_integration_feature_flag_enabled?Boolean

Returns:

  • (Boolean)


1084
1085
1086
# File 'app/models/group.rb', line 1084

def glql_integration_feature_flag_enabled?
  feature_flag_enabled_for_self_or_ancestor?(:glql_integration)
end

#glql_load_on_click_feature_flag_enabled?Boolean

Returns:

  • (Boolean)


1088
1089
1090
# File 'app/models/group.rb', line 1088

def glql_load_on_click_feature_flag_enabled?
  feature_flag_enabled_for_self_or_ancestor?(:glql_load_on_click)
end

#group_featureObject



1036
1037
1038
# File 'app/models/group.rb', line 1036

def group_feature
  super || build_group_feature
end

#group_readmeObject



1154
1155
1156
# File 'app/models/group.rb', line 1154

def group_readme
  readme_project&.repository&.readme
end

#has_container_repository_including_subgroups?Boolean

Returns:

  • (Boolean)


673
674
675
# File 'app/models/group.rb', line 673

def has_container_repository_including_subgroups?
  ::ContainerRepository.for_group_and_its_subgroups(self).exists?
end

#has_issues_with_contacts?Boolean

Returns:

  • (Boolean)


1183
1184
1185
# File 'app/models/group.rb', line 1183

def has_issues_with_contacts?
  CustomerRelations::IssueContact.joins(:issue).where(issue: { project_id: Project.where(namespace_id: self_and_descendant_ids) }).exists?
end

#has_maintainer?(user) ⇒ Boolean

Returns:

  • (Boolean)


667
668
669
670
671
# File 'app/models/group.rb', line 667

def has_maintainer?(user)
  return false unless user

  members_with_parents.maintainers.exists?(user_id: user)
end

#has_owner?(user) ⇒ Boolean

Returns:

  • (Boolean)


657
658
659
660
661
# File 'app/models/group.rb', line 657

def has_owner?(user)
  return false unless user

  members_with_parents.all_owners.exists?(user_id: user)
end

#has_project_with_service_desk_enabled?Boolean

Returns:

  • (Boolean)


1007
1008
1009
# File 'app/models/group.rb', line 1007

def has_project_with_service_desk_enabled?
  ::ServiceDesk.supported? && all_projects.service_desk_enabled.exists?
end

#has_user?(user) ⇒ Boolean

Only for direct and not requested members with higher access level than MIMIMAL_ACCESS It returns true for non-active users

Returns:

  • (Boolean)


788
789
790
791
792
# File 'app/models/group.rb', line 788

def has_user?(user)
  return false unless user

  group_members.non_invite.exists?(user: user)
end

#hashed_storage?(_feature) ⇒ Boolean

Returns:

  • (Boolean)


907
908
909
# File 'app/models/group.rb', line 907

def hashed_storage?(_feature)
  false
end

#hierarchy_membersObject

Returns all members that are part of the group, it’s subgroups, and ancestor groups



822
823
824
825
826
# File 'app/models/group.rb', line 822

def hierarchy_members
  GroupMember
    .active_without_invites_and_requests
    .where(source_id: self_and_hierarchy.reorder(nil).select(:id))
end

#hierarchy_members_with_inactiveObject



828
829
830
831
832
833
# File 'app/models/group.rb', line 828

def hierarchy_members_with_inactive
  GroupMember
    .non_request
    .non_invite
    .where(source_id: self_and_hierarchy.reorder(nil).select(:id))
end

#highest_group_member(user) ⇒ Object



889
890
891
892
893
894
895
# File 'app/models/group.rb', line 889

def highest_group_member(user)
  GroupMember
    .where(source_id: self_and_ancestors_ids, user_id: user.id)
    .non_request
    .order(:access_level)
    .last
end

#hook_attrsObject



1159
1160
1161
1162
1163
1164
1165
1166
# File 'app/models/group.rb', line 1159

def hook_attrs
  {
    group_name: name,
    group_path: path,
    group_id: id,
    full_path: full_path
  }
end

#human_nameObject



563
564
565
# File 'app/models/group.rb', line 563

def human_name
  full_name
end

#import_export_upload_by_user(user) ⇒ Object



944
945
946
# File 'app/models/group.rb', line 944

def import_export_upload_by_user(user)
  import_export_uploads.find_by(user_id: user.id)
end

#last_owner?(user) ⇒ Boolean

Check if user is a last owner of the group. Excludes non-direct owners for top-level group Excludes project_bots

Returns:

  • (Boolean)


680
681
682
683
684
685
# File 'app/models/group.rb', line 680

def last_owner?(user)
  return false unless user

  all_owners = member_owners_excluding_project_bots
  last_owner_in_list?(user, all_owners)
end

#last_owner_in_list?(user, all_owners) ⇒ Boolean

This is used in BillableMember Entity to avoid multiple “member_owners_excluding_project_bots” calls for each billable members

Returns:

  • (Boolean)


690
691
692
693
694
# File 'app/models/group.rb', line 690

def last_owner_in_list?(user, all_owners)
  return false unless user

  all_owners.size == 1 && all_owners.first.user_id == user.id
end

#ldap_synced?Boolean

Returns:

  • (Boolean)


716
717
718
# File 'app/models/group.rb', line 716

def ldap_synced?
  false
end

#lfs_enabled?Boolean

Returns:

  • (Boolean)


600
601
602
603
604
605
# File 'app/models/group.rb', line 600

def lfs_enabled?
  return false unless Gitlab.config.lfs.enabled
  return Gitlab.config.lfs.enabled if self[:lfs_enabled].nil?

  self[:lfs_enabled]
end

#mattermost_team_paramsObject



871
872
873
874
875
876
877
878
879
# File 'app/models/group.rb', line 871

def mattermost_team_params
  max_length = 59

  {
    name: path[0..max_length],
    display_name: name[0..max_length],
    type: public? ? 'O' : 'I' # Open vs Invite-only
  }
end

#max_member_access_for_user(user, only_concrete_membership: false) ⇒ Object

Return the highest access level for a user

A special case is handled here when the user is a GitLab admin which implies it has “OWNER” access everywhere, but should not officially appear as a member of a group unless specifically added to it

Parameters:

  • user (User)
  • only_concrete_membership (Bool) (defaults to: false)

    whether require admin concrete membership status



860
861
862
863
864
865
866
867
868
869
# File 'app/models/group.rb', line 860

def max_member_access_for_user(user, only_concrete_membership: false)
  return GroupMember::NO_ACCESS unless user

  unless only_concrete_membership
    return GroupMember::OWNER if user.can_admin_all_resources?
    return GroupMember::OWNER if user.can_admin_organization?(organization)
  end

  max_member_access(user)
end

#member(user) ⇒ Object



881
882
883
884
885
886
887
# File 'app/models/group.rb', line 881

def member(user)
  if group_members.loaded?
    group_members.find { |gm| gm.user_id == user.id }
  else
    group_members.find_by(user_id: user)
  end
end

#member?(user, min_access_level = Gitlab::Access::GUEST) ⇒ Boolean

Returns:

  • (Boolean)


651
652
653
654
655
# File 'app/models/group.rb', line 651

def member?(user, min_access_level = Gitlab::Access::GUEST)
  return false unless user

  max_member_access_for_user(user) >= min_access_level
end

#member_owners_excluding_project_botsObject

Excludes non-direct owners for top-level group Excludes project_bots



698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
# File 'app/models/group.rb', line 698

def member_owners_excluding_project_bots
  members_from_hiearchy = if root?
                            members.non_minimal_access.without_invites_and_requests
                          else
                            members_with_parents(only_active_users: false)
                          end

  owners = []

  members_from_hiearchy.all_owners.non_invite.each_batch do |relation|
    owners += relation.preload(:user, :source).load.reject do |member|
      member.user.nil? || member.user.project_bot?
    end
  end

  owners
end

#members_from_self_and_ancestors_with_effective_access_levelObject



810
811
812
813
# File 'app/models/group.rb', line 810

def members_from_self_and_ancestors_with_effective_access_level
  members_with_parents.select([:user_id, 'MAX(access_level) AS access_level'])
                      .group(:user_id)
end

#members_with_descendantsObject



815
816
817
818
819
# File 'app/models/group.rb', line 815

def members_with_descendants
  GroupMember
    .active_without_invites_and_requests
    .where(source_id: self_and_descendants.reorder(nil).select(:id))
end

#members_with_parents(only_active_users: true) ⇒ Object



804
805
806
807
808
# File 'app/models/group.rb', line 804

def members_with_parents(only_active_users: true)
  Members::MembersWithParents
    .new(self)
    .members(active_users: only_active_users)
end

#membership_locked?Boolean

Returns:

  • (Boolean)


936
937
938
# File 'app/models/group.rb', line 936

def membership_locked?
  false # to support project and group calling this as 'source'
end

#namespace_work_items_enabled?Boolean

Note: this method is overridden in EE to check the work_item_epics feature flag which also enables this feature

Returns:

  • (Boolean)


1093
1094
1095
# File 'app/models/group.rb', line 1093

def namespace_work_items_enabled?
  ::Feature.enabled?(:namespace_level_work_items, self, type: :development)
end

#notification_email_for(user) ⇒ Object



548
549
550
551
552
# File 'app/models/group.rb', line 548

def notification_email_for(user)
  # Finds the closest notification_setting with a `notification_email`
  notification_settings = notification_settings_for(user, hierarchy_order: :asc)
  notification_settings.find { |n| n.notification_email.present? }&.notification_email
end

#notification_groupObject



1150
1151
1152
# File 'app/models/group.rb', line 1150

def notification_group
  self
end

#notification_settings(hierarchy_order: nil) ⇒ Object

Overrides notification_settings has_many association This allows to apply notification settings from parent groups to child groups and projects.



524
525
526
527
528
529
530
531
532
533
534
# File 'app/models/group.rb', line 524

def notification_settings(hierarchy_order: nil)
  source_type = self.class.base_class.name
  settings = NotificationSetting.where(source_type: source_type, source_id: self_and_ancestors_ids)

  return settings unless hierarchy_order && self_and_ancestors_ids.length > 1

  settings
    .joins("LEFT JOIN (#{self_and_ancestors(hierarchy_order: hierarchy_order).to_sql}) AS ordered_groups ON notification_settings.source_id = ordered_groups.id")
    .select('notification_settings.*, ordered_groups.depth AS depth')
    .order("ordered_groups.depth #{hierarchy_order}")
end

#notification_settings_for(user, hierarchy_order: nil) ⇒ Object



536
537
538
# File 'app/models/group.rb', line 536

def notification_settings_for(user, hierarchy_order: nil)
  notification_settings(hierarchy_order: hierarchy_order).where(user: user)
end

#open_issues_count(current_user = nil) ⇒ Object

rubocop: disable CodeReuse/ServiceClass



1013
1014
1015
# File 'app/models/group.rb', line 1013

def open_issues_count(current_user = nil)
  Groups::OpenIssuesCountService.new(self, current_user).count
end

#open_merge_requests_count(current_user = nil) ⇒ Object

rubocop: disable CodeReuse/ServiceClass



1019
1020
1021
# File 'app/models/group.rb', line 1019

def open_merge_requests_count(current_user = nil)
  Groups::MergeRequestsCountService.new(self, current_user).count
end

#owned_by?(user) ⇒ Boolean

Returns:

  • (Boolean)


607
608
609
610
611
# File 'app/models/group.rb', line 607

def owned_by?(user)
  return false unless user

  non_invite_owner_members.exists?(user: user)
end

#packages_feature_enabled?Boolean

Returns:

  • (Boolean)


540
541
542
# File 'app/models/group.rb', line 540

def packages_feature_enabled?
  ::Gitlab.config.packages.enabled
end

#packages_policy_subjectObject



1133
1134
1135
# File 'app/models/group.rb', line 1133

def packages_policy_subject
  ::Packages::Policies::Group.new(self)
end

#parent_allows_two_factor_authentication?Boolean

Returns:

  • (Boolean)


1000
1001
1002
1003
1004
1005
# File 'app/models/group.rb', line 1000

def parent_allows_two_factor_authentication?
  return true unless has_parent?

  ancestor_settings = ancestors.find_top_level.namespace_settings
  ancestor_settings.allow_mfa_for_subgroups
end

#pending_delete?Boolean

Returns:

  • (Boolean)


1203
1204
1205
1206
1207
# File 'app/models/group.rb', line 1203

def pending_delete?
  return false unless deletion_schedule

  deletion_schedule.marked_for_deletion_on.future?
end

#post_create_hookObject



720
721
722
723
724
# File 'app/models/group.rb', line 720

def post_create_hook
  Gitlab::AppLogger.info("Group \"#{name}\" was created")

  system_hook_service.execute_hooks_for(self, :create)
end

#post_destroy_hookObject



726
727
728
729
730
# File 'app/models/group.rb', line 726

def post_destroy_hook
  Gitlab::AppLogger.info("Group \"#{name}\" was removed")

  system_hook_service.execute_hooks_for(self, :destroy)
end


975
976
977
978
979
980
# File 'app/models/group.rb', line 975

def preload_shared_group_links
  ActiveRecord::Associations::Preloader.new(
    records: [self],
    associations: { shared_with_group_links: [shared_with_group: :route] }
  ).call
end

#project_creation_levelObject



924
925
926
# File 'app/models/group.rb', line 924

def project_creation_level
  super || ::Gitlab::CurrentSettings.default_project_creation
end

#readme_projectObject



1145
1146
1147
# File 'app/models/group.rb', line 1145

def readme_project
  projects.find_by(path: README_PROJECT_PATH)
end

#refresh_members_authorized_projects(priority: UserProjectAccessChangedService::HIGH_PRIORITY, direct_members_only: false) ⇒ Object

rubocop: disable CodeReuse/ServiceClass



739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
# File 'app/models/group.rb', line 739

def refresh_members_authorized_projects(
  priority: UserProjectAccessChangedService::HIGH_PRIORITY,
  direct_members_only: false
)

  user_ids = if direct_members_only
               users_ids_of_direct_members
             else
               user_ids_for_project_authorizations
             end

  UserProjectAccessChangedService
    .new(user_ids)
    .execute(priority: priority)
end

#refresh_project_authorizationsObject



911
912
913
# File 'app/models/group.rb', line 911

def refresh_project_authorizations
  refresh_members_authorized_projects
end


901
902
903
904
905
# File 'app/models/group.rb', line 901

def related_group_ids
  [id,
    *ancestors.pluck(:id),
    *shared_with_group_links.pluck(:shared_with_group_id)]
end

#runners_tokenObject

each existing group needs to have a ‘runners_token`. we do this on read since migrating all existing groups is not a feasible solution.



918
919
920
921
922
# File 'app/models/group.rb', line 918

def runners_token
  return unless allow_runner_registration_token?

  ensure_runners_token!
end

#self_and_ancestors_ascObject



781
782
783
# File 'app/models/group.rb', line 781

def self_and_ancestors_asc
  self_and_ancestors(hierarchy_order: :asc)
end

#self_and_ancestors_idsObject



769
770
771
772
773
# File 'app/models/group.rb', line 769

def self_and_ancestors_ids
  strong_memoize(:self_and_ancestors_ids) do
    self_and_ancestors.pluck(:id)
  end
end

#self_and_descendants_idsObject



775
776
777
778
779
# File 'app/models/group.rb', line 775

def self_and_descendants_ids
  strong_memoize(:self_and_descendants_ids) do
    self_and_descendants.pluck(:id)
  end
end

#self_and_hierarchy_intersecting_with_user_groups(user) ⇒ Object



764
765
766
767
# File 'app/models/group.rb', line 764

def self_and_hierarchy_intersecting_with_user_groups(user)
  user_groups = GroupsFinder.new(user).execute.unscope(:order)
  self_and_hierarchy.unscope(:order).where(id: user_groups)
end


1044
1045
1046
# File 'app/models/group.rb', line 1044

def shared_with_group_links_visible_to_user(user)
  shared_with_group_links.preload_shared_with_groups.filter { |link| Ability.allowed?(user, :read_group, link.shared_with_group) }
end

#subgroup_creation_levelObject



928
929
930
# File 'app/models/group.rb', line 928

def subgroup_creation_level
  super || ::Gitlab::Access::OWNER_SUBGROUP_ACCESS
end

#supports_events?Boolean

Returns:

  • (Boolean)


940
941
942
# File 'app/models/group.rb', line 940

def supports_events?
  false
end

#supports_lock_on_merge?Boolean

Returns:

  • (Boolean)


1101
1102
1103
# File 'app/models/group.rb', line 1101

def supports_lock_on_merge?
  feature_flag_enabled_for_self_or_ancestor?(:enforce_locked_labels_on_merge, type: :ops)
end

#supports_saved_replies?Boolean

Returns:

  • (Boolean)


1109
1110
1111
# File 'app/models/group.rb', line 1109

def supports_saved_replies?
  false
end

#system_hook_serviceObject

rubocop: disable CodeReuse/ServiceClass



733
734
735
# File 'app/models/group.rb', line 733

def system_hook_service
  SystemHooksService.new
end

#timelogsObject

rubocop: enable CodeReuse/ServiceClass



1024
1025
1026
# File 'app/models/group.rb', line 1024

def timelogs
  Timelog.in_group(self)
end

#to_human_reference(from = nil) ⇒ Object



567
568
569
570
571
# File 'app/models/group.rb', line 567

def to_human_reference(from = nil)
  return unless cross_namespace_reference?(from)

  human_name
end

#update_two_factor_requirement_for_membersObject



1141
1142
1143
# File 'app/models/group.rb', line 1141

def update_two_factor_requirement_for_members
  hierarchy_members.find_each(&:update_two_factor_requirement)
end

#usage_quotas_enabled?Boolean

Returns:

  • (Boolean)


1105
1106
1107
# File 'app/models/group.rb', line 1105

def usage_quotas_enabled?
  root?
end

#user_ids_for_project_authorizationsObject



760
761
762
# File 'app/models/group.rb', line 760

def user_ids_for_project_authorizations
  members_with_parents.pluck(Arel.sql('DISTINCT members.user_id'))
end

#users_countObject



848
849
850
# File 'app/models/group.rb', line 848

def users_count
  members.count
end

#users_ids_of_direct_membersObject

rubocop: enable CodeReuse/ServiceClass



756
757
758
# File 'app/models/group.rb', line 756

def users_ids_of_direct_members
  direct_members.pluck_user_ids
end

#users_with_descendantsObject



842
843
844
845
846
# File 'app/models/group.rb', line 842

def users_with_descendants
  User
    .where(id: members_with_descendants.select(:user_id))
    .reorder(nil)
end

#visibility_level_allowed?(level = self.visibility_level) ⇒ Boolean

Returns:

  • (Boolean)


593
594
595
596
597
598
# File 'app/models/group.rb', line 593

def visibility_level_allowed?(level = self.visibility_level)
  visibility_level_allowed_by_organization?(level) &&
    visibility_level_allowed_by_parent?(level) &&
    visibility_level_allowed_by_projects?(level) &&
    visibility_level_allowed_by_sub_groups?(level)
end

#visibility_level_allowed_by_organization?(level = self.visibility_level) ⇒ Boolean

Returns:

  • (Boolean)


573
574
575
576
577
# File 'app/models/group.rb', line 573

def visibility_level_allowed_by_organization?(level = self.visibility_level)
  return true unless organization

  level <= organization.visibility_level
end

#visibility_level_allowed_by_parent?(level = self.visibility_level) ⇒ Boolean

Returns:

  • (Boolean)


579
580
581
582
583
# File 'app/models/group.rb', line 579

def visibility_level_allowed_by_parent?(level = self.visibility_level)
  return true unless parent_id && parent_id.nonzero?

  level <= parent.visibility_level
end

#visibility_level_allowed_by_projects?(level = self.visibility_level) ⇒ Boolean

Returns:

  • (Boolean)


585
586
587
# File 'app/models/group.rb', line 585

def visibility_level_allowed_by_projects?(level = self.visibility_level)
  !projects.not_aimed_for_deletion.where('visibility_level > ?', level).exists?
end

#visibility_level_allowed_by_sub_groups?(level = self.visibility_level) ⇒ Boolean

Returns:

  • (Boolean)


589
590
591
# File 'app/models/group.rb', line 589

def visibility_level_allowed_by_sub_groups?(level = self.visibility_level)
  !children.where('visibility_level > ?', level).exists?
end

#work_item_status_feature_available?Boolean

Returns:

  • (Boolean)


1071
1072
1073
1074
# File 'app/models/group.rb', line 1071

def work_item_status_feature_available?
  feature_flag_enabled_for_self_or_ancestor?(:work_item_status_feature_flag, type: :wip) &&
    licensed_feature_available?(:work_item_status)
end

#work_item_status_transitions_enabled?Boolean

Returns:

  • (Boolean)


1076
1077
1078
# File 'app/models/group.rb', line 1076

def work_item_status_transitions_enabled?
  feature_flag_enabled_for_self_or_ancestor?(:work_item_status_transitions, type: :wip)
end

#work_items_alpha_feature_flag_enabled?Boolean

Returns:

  • (Boolean)


1067
1068
1069
# File 'app/models/group.rb', line 1067

def work_items_alpha_feature_flag_enabled?
  feature_flag_enabled_for_self_or_ancestor?(:work_items_alpha)
end

#work_items_beta_feature_flag_enabled?Boolean

Returns:

  • (Boolean)


1063
1064
1065
# File 'app/models/group.rb', line 1063

def work_items_beta_feature_flag_enabled?
  feature_flag_enabled_for_self_or_ancestor?(:work_items_beta, type: :beta)
end

#work_items_feature_flag_enabled?Boolean

Returns:

  • (Boolean)


1059
1060
1061
# File 'app/models/group.rb', line 1059

def work_items_feature_flag_enabled?
  feature_flag_enabled_for_self_or_ancestor?(:work_items)
end