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, #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?, #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, #deleted?, #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, #paid?, #project_namespace?, #recent?, reference_pattern, reference_prefix, #root?, search, #send_update_instructions, #service_desk_alias_address, #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, #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::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_ids, #traversal_ids=, #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!, #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



386
387
388
# File 'app/models/group.rb', line 386

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

.get_ids_by_ids_or_paths(ids, paths) ⇒ Object



382
383
384
# File 'app/models/group.rb', line 382

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

.group_members_countsObject



394
395
396
# File 'app/models/group.rb', line 394

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

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



347
348
349
350
351
# File 'app/models/group.rb', line 347

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.



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

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



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

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)


431
432
433
434
435
436
# File 'app/models/group.rb', line 431

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.



406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
# File 'app/models/group.rb', line 406

def project_creation_levels_for_user(user)
  project_creation_allowed_on_levels = [
    ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS,
    ::Gitlab::Access::MAINTAINER_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



390
391
392
# File 'app/models/group.rb', line 390

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



318
319
320
321
322
323
324
325
326
# File 'app/models/group.rb', line 318

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



328
329
330
331
332
333
334
335
336
# File 'app/models/group.rb', line 328

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



300
301
302
303
304
305
306
307
308
309
310
311
312
313
# File 'app/models/group.rb', line 300

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



398
399
400
# File 'app/models/group.rb', line 398

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

.without_integration(integration) ⇒ Object



338
339
340
341
342
343
344
345
# File 'app/models/group.rb', line 338

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



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

def access_level_roles
  GroupMember.access_level_roles
end

#access_level_valuesObject



905
906
907
# File 'app/models/group.rb', line 905

def access_level_values
  access_level_roles.values
end

#access_request_approvers_to_be_notifiedObject



841
842
843
# File 'app/models/group.rb', line 841

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

#add_developer(user, current_user = nil) ⇒ Object



561
562
563
# File 'app/models/group.rb', line 561

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

#add_guest(user, current_user = nil) ⇒ Object



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

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

#add_maintainer(user, current_user = nil) ⇒ Object



565
566
567
# File 'app/models/group.rb', line 565

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

#add_member(user, access_level) ⇒ Object



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

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



535
536
537
538
539
540
541
542
543
# File 'app/models/group.rb', line 535

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



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

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

#add_planner(user, current_user = nil) ⇒ Object



553
554
555
# File 'app/models/group.rb', line 553

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

#add_reporter(user, current_user = nil) ⇒ Object



557
558
559
# File 'app/models/group.rb', line 557

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

#adjourned_deletion?Boolean

Returns:

  • (Boolean)


869
870
871
# File 'app/models/group.rb', line 869

def adjourned_deletion?
  false
end

#authorizable_members_with_parentsObject



709
710
711
# File 'app/models/group.rb', line 709

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

#blocked_ownersObject



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

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

#botsObject



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

def bots
  users.project_bot
end

#create_group_level_work_items_feature_flag_enabled?Boolean

Returns:

  • (Boolean)


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

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)


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

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

#crm_groupObject



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

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)


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

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

  crm_targets.present?
end

#default_branch_nameObject



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

def default_branch_name
  namespace_settings&.default_branch_name
end

#delete_contactsObject



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

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

#delete_organizationsObject



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

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

#dependency_proxy_feature_available?Boolean

Returns:

  • (Boolean)


479
480
481
# File 'app/models/group.rb', line 479

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

#dependency_proxy_for_containers_policy_subjectObject



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

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

#dependency_proxy_image_prefixObject



489
490
491
492
493
494
495
496
# File 'app/models/group.rb', line 489

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



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

def dependency_proxy_image_ttl_policy
  super || build_dependency_proxy_image_ttl_policy
end

#dependency_proxy_settingObject



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

def dependency_proxy_setting
  super || build_dependency_proxy_setting
end

#descendant_project_members_with_inactiveObject



744
745
746
747
748
749
# File 'app/models/group.rb', line 744

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

#direct_membersObject



703
704
705
706
707
# File 'app/models/group.rb', line 703

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

#enforced_runner_token_expiration_intervalObject



957
958
959
960
961
962
963
964
965
966
# File 'app/models/group.rb', line 957

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



873
874
875
876
# File 'app/models/group.rb', line 873

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



878
879
880
881
882
# File 'app/models/group.rb', line 878

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)


865
866
867
# File 'app/models/group.rb', line 865

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

#export_file(user) ⇒ Object



861
862
863
# File 'app/models/group.rb', line 861

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

#export_file_exists?(user) ⇒ Boolean

Returns:

  • (Boolean)


857
858
859
# File 'app/models/group.rb', line 857

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

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

Returns:

  • (Boolean)


1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
# File 'app/models/group.rb', line 1012

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

#first_ownerObject



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

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



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

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)


980
981
982
# File 'app/models/group.rb', line 980

def glql_integration_feature_flag_enabled?
  feature_flag_enabled_for_self_or_ancestor?(:glql_integration)
end

#group_featureObject



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

def group_feature
  super || build_group_feature
end

#group_readmeObject



1050
1051
1052
# File 'app/models/group.rb', line 1050

def group_readme
  readme_project&.repository&.readme
end

#has_container_repository_including_subgroups?Boolean

Returns:

  • (Boolean)


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

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

#has_issues_with_contacts?Boolean

Returns:

  • (Boolean)


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

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)


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

def has_maintainer?(user)
  return false unless user

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

#has_owner?(user) ⇒ Boolean

Returns:

  • (Boolean)


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

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)


916
917
918
# File 'app/models/group.rb', line 916

def has_project_with_service_desk_enabled?
  Gitlab::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)


697
698
699
700
701
# File 'app/models/group.rb', line 697

def has_user?(user)
  return false unless user

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

#hashed_storage?(_feature) ⇒ Boolean

Returns:

  • (Boolean)


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

def hashed_storage?(_feature)
  false
end

#hierarchy_membersObject

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



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

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

#hierarchy_members_with_inactiveObject



737
738
739
740
741
742
# File 'app/models/group.rb', line 737

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



798
799
800
801
802
803
804
# File 'app/models/group.rb', line 798

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



1055
1056
1057
1058
1059
1060
1061
1062
# File 'app/models/group.rb', line 1055

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

#human_nameObject



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

def human_name
  full_name
end

#import_export_upload_by_user(user) ⇒ Object



853
854
855
# File 'app/models/group.rb', line 853

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)


602
603
604
605
606
607
608
# File 'app/models/group.rb', line 602

def last_owner?(user)
  return false unless user

  all_owners = member_owners_excluding_project_bots

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

#ldap_synced?Boolean

Returns:

  • (Boolean)


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

def ldap_synced?
  false
end

#lfs_enabled?Boolean

Returns:

  • (Boolean)


522
523
524
525
526
527
# File 'app/models/group.rb', line 522

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



780
781
782
783
784
785
786
787
788
# File 'app/models/group.rb', line 780

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



769
770
771
772
773
774
775
776
777
778
# File 'app/models/group.rb', line 769

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



790
791
792
793
794
795
796
# File 'app/models/group.rb', line 790

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)


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

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



612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
# File 'app/models/group.rb', line 612

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



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

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



724
725
726
727
728
# File 'app/models/group.rb', line 724

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



713
714
715
716
717
# File 'app/models/group.rb', line 713

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

#membership_locked?Boolean

Returns:

  • (Boolean)


845
846
847
# File 'app/models/group.rb', line 845

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)


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

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

#notification_email_for(user) ⇒ Object



483
484
485
486
487
# File 'app/models/group.rb', line 483

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



1046
1047
1048
# File 'app/models/group.rb', line 1046

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.



459
460
461
462
463
464
465
466
467
468
469
# File 'app/models/group.rb', line 459

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



471
472
473
# File 'app/models/group.rb', line 471

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



922
923
924
# File 'app/models/group.rb', line 922

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



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

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

#owned_by?(user) ⇒ Boolean

Returns:

  • (Boolean)


529
530
531
532
533
# File 'app/models/group.rb', line 529

def owned_by?(user)
  return false unless user

  non_invite_owner_members.exists?(user: user)
end

#packages_feature_enabled?Boolean

Returns:

  • (Boolean)


475
476
477
# File 'app/models/group.rb', line 475

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

#packages_policy_subjectObject



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

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

#parent_allows_two_factor_authentication?Boolean

Returns:

  • (Boolean)


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

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

#post_create_hookObject



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

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

  system_hook_service.execute_hooks_for(self, :create)
end

#post_destroy_hookObject



640
641
642
643
644
# File 'app/models/group.rb', line 640

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

  system_hook_service.execute_hooks_for(self, :destroy)
end


884
885
886
887
888
889
# File 'app/models/group.rb', line 884

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



833
834
835
# File 'app/models/group.rb', line 833

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

#readme_projectObject



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

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



653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
# File 'app/models/group.rb', line 653

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



820
821
822
# File 'app/models/group.rb', line 820

def refresh_project_authorizations
  refresh_members_authorized_projects
end


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

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.



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

def runners_token
  return unless allow_runner_registration_token?

  ensure_runners_token!
end

#self_and_ancestors_idsObject



683
684
685
686
687
# File 'app/models/group.rb', line 683

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

#self_and_descendants_idsObject



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

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



678
679
680
681
# File 'app/models/group.rb', line 678

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


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

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



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

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

#supports_events?Boolean

Returns:

  • (Boolean)


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

def supports_events?
  false
end

#supports_lock_on_merge?Boolean

Returns:

  • (Boolean)


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

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)


1005
1006
1007
# File 'app/models/group.rb', line 1005

def supports_saved_replies?
  false
end

#system_hook_serviceObject

rubocop: disable CodeReuse/ServiceClass



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

def system_hook_service
  SystemHooksService.new
end

#timelogsObject

rubocop: enable CodeReuse/ServiceClass



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

def timelogs
  Timelog.in_group(self)
end

#update_two_factor_requirement_for_membersObject



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

def update_two_factor_requirement_for_members
  hierarchy_members.find_each(&:update_two_factor_requirement)
end

#usage_quotas_enabled?Boolean

Returns:

  • (Boolean)


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

def usage_quotas_enabled?
  root?
end

#user_ids_for_project_authorizationsObject



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

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

#users_countObject



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

def users_count
  members.count
end

#users_ids_of_direct_membersObject

rubocop: enable CodeReuse/ServiceClass



670
671
672
# File 'app/models/group.rb', line 670

def users_ids_of_direct_members
  direct_members.pluck_user_ids
end

#users_with_descendantsObject



751
752
753
754
755
# File 'app/models/group.rb', line 751

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)


516
517
518
519
520
# File 'app/models/group.rb', line 516

def visibility_level_allowed?(level = self.visibility_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_parent?(level = self.visibility_level) ⇒ Boolean

Returns:

  • (Boolean)


502
503
504
505
506
# File 'app/models/group.rb', line 502

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)


508
509
510
# File 'app/models/group.rb', line 508

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)


512
513
514
# File 'app/models/group.rb', line 512

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

#wiki_comments_feature_flag_enabled?Boolean

Returns:

  • (Boolean)


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

def wiki_comments_feature_flag_enabled?
  feature_flag_enabled_for_self_or_ancestor?(:wiki_comments, type: :wip)
end

#work_items_alpha_feature_flag_enabled?Boolean

Returns:

  • (Boolean)


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

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)


972
973
974
# File 'app/models/group.rb', line 972

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)


968
969
970
# File 'app/models/group.rb', line 968

def work_items_feature_flag_enabled?
  feature_flag_enabled_for_self_or_ancestor?(:work_items)
end