Read about our upcoming Code of Conduct on this issue

projects_helper.rb 21.8 KB
Newer Older
1
2
# frozen_string_literal: true

randx's avatar
randx committed
3
module ProjectsHelper
4
5
  prepend_if_ee('::EE::ProjectsHelper') # rubocop: disable Cop/InjectEnterpriseEditionModule

6
7
8
9
10
  def project_incident_management_setting
    @project_incident_management_setting ||= @project.incident_management_setting ||
      @project.build_incident_management_setting
  end

11
  def link_to_project(project)
12
    link_to namespace_project_path(namespace_id: project.namespace, id: project), title: h(project.name) do
13
      title = content_tag(:span, project.name, class: 'project-name')
14
15

      if project.namespace
16
        namespace = content_tag(:span, "#{project.namespace.human_name} / ", class: 'namespace-name')
17
18
19
20
21
        title = namespace + title
      end

      title
    end
22
  end
randx's avatar
randx committed
23

Jacob Schatz's avatar
Jacob Schatz committed
24
  def link_to_member_avatar(author, opts = {})
25
    default_opts = { size: 16, lazy_load: false }
Jacob Schatz's avatar
Jacob Schatz committed
26
    opts = default_opts.merge(opts)
27
28

    classes = %W[avatar avatar-inline s#{opts[:size]}]
Maxim Rydkin's avatar
Maxim Rydkin committed
29
30
    classes << opts[:avatar_class] if opts[:avatar_class]

31
    avatar = avatar_icon_for_user(author, opts[:size])
32
33
34
    src = opts[:lazy_load] ? nil : avatar

    image_tag(src, width: opts[:size], class: classes, alt: '', "data-src" => avatar)
Jacob Schatz's avatar
Jacob Schatz committed
35
36
  end

37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
  def author_content_tag(author, opts = {})
    default_opts = { author_class: 'author', tooltip: false, by_username: false }
    opts = default_opts.merge(opts)

    has_tooltip = !opts[:by_username] && opts[:tooltip]

    username = opts[:by_username] ? author.to_reference : author.name
    name_tag_options = { class: [opts[:author_class]] }

    if has_tooltip
      name_tag_options[:title] = author.to_reference
      name_tag_options[:data] = { placement: 'top' }
      name_tag_options[:class] << 'has-tooltip'
    end

52
53
    # NOTE: ActionView::Helpers::TagHelper#content_tag HTML escapes username
    content_tag(:span, username, name_tag_options)
54
55
  end

Phil Hughes's avatar
Phil Hughes committed
56
  def link_to_member(project, author, opts = {}, &block)
57
    default_opts = { avatar: true, name: true, title: ":name" }
58
59
    opts = default_opts.merge(opts)

60
61
62
63
64
65
    data_attrs = {
      user_id: author.id,
      username: author.username,
      name: author.name
    }

66
67
    return "(deleted)" unless author

68
    author_html = []
69

70
    # Build avatar image tag
71
    author_html << link_to_member_avatar(author, opts) if opts[:avatar]
72

73
    # Build name span tag
74
    author_html << author_content_tag(author, opts) if opts[:name]
75

Phil Hughes's avatar
Phil Hughes committed
76
77
    author_html << capture(&block) if block

78
    author_html = author_html.join.html_safe
79

80
    if opts[:name]
81
      link_to(author_html, user_path(author), class: "author-link js-user-link #{"#{opts[:extra_class]}" if opts[:extra_class]} #{"#{opts[:mobile_classes]}" if opts[:mobile_classes]}", data: data_attrs).html_safe
82
    else
83
      title = opts[:title].sub(":name", sanitize(author.name))
84
      link_to(author_html, user_path(author), class: "author-link has-tooltip", title: title, data: { container: 'body', qa_selector: 'assignee_link' }).html_safe
85
    end
randx's avatar
randx committed
86
  end
87

88
  def project_title(project)
89
90
    namespace_link =
      if project.group
Phil Hughes's avatar
Phil Hughes committed
91
        group_title(project.group, nil, nil)
92
93
94
      else
        owner = project.namespace.owner
        link_to(simple_sanitize(owner.name), user_path(owner))
95
      end
96

Phil Hughes's avatar
Phil Hughes committed
97
    project_link = link_to project_path(project) do
98
99
      icon = project_icon(project, alt: project.name, class: 'avatar-tile', width: 15, height: 15) if project.avatar_url && !Rails.env.test?
      [icon, content_tag("span", simple_sanitize(project.name), class: "breadcrumb-item-text js-breadcrumb-item-text")].join.html_safe
Phil Hughes's avatar
Phil Hughes committed
100
    end
101

Phil Hughes's avatar
Phil Hughes committed
102
103
    namespace_link = breadcrumb_list_item(namespace_link) unless project.group
    project_link = breadcrumb_list_item project_link
Phil Hughes's avatar
Phil Hughes committed
104

Phil Hughes's avatar
Phil Hughes committed
105
    "#{namespace_link} #{project_link}".html_safe
106
  end
107
108

  def remove_project_message(project)
109
110
    _("You are going to remove %{project_full_name}. Removed project CANNOT be restored! Are you ABSOLUTELY sure?") %
      { project_full_name: project.full_name }
111
  end
112

113
  def transfer_project_message(project)
114
115
    _("You are going to transfer %{project_full_name} to another owner. Are you ABSOLUTELY sure?") %
      { project_full_name: project.full_name }
116
117
  end

118
119
  def remove_fork_project_description_message(project)
    source = visible_fork_source(project)
120

121
    if source
122
      msg = _('This will remove the fork relationship between this project and %{fork_source}.') %
123
        { fork_source: link_to(source.full_name, project_path(source)) }
124
125

      msg.html_safe
126
    else
127
      _('This will remove the fork relationship between this project and other projects in the fork network.')
128
    end
129
130
  end

131
132
133
134
135
136
137
138
139
  def remove_fork_project_warning_message(project)
    _("You are going to remove the fork relationship from %{project_full_name}. Are you ABSOLUTELY sure?") %
      { project_full_name: project.full_name }
  end

  def visible_fork_source(project)
    project.fork_source if project.fork_source && can?(current_user, :read_project, project.fork_source)
  end

140
141
142
143
  def project_nav_tabs
    @nav_tabs ||= get_project_nav_tabs(@project, current_user)
  end

144
145
146
147
148
149
  def project_search_tabs?(tab)
    abilities = Array(search_tab_ability_map[tab])

    abilities.any? { |ability| can?(current_user, ability, @project) }
  end

150
151
152
153
  def project_nav_tab?(name)
    project_nav_tabs.include? name
  end

Douwe Maan's avatar
Douwe Maan committed
154
  def project_for_deploy_key(deploy_key)
155
    if deploy_key.has_access_to?(@project)
Douwe Maan's avatar
Douwe Maan committed
156
157
      @project
    else
Lin Jen-Shin's avatar
Lin Jen-Shin committed
158
159
160
      deploy_key.projects.find do |project|
        can?(current_user, :read_project, project)
      end
Douwe Maan's avatar
Douwe Maan committed
161
162
163
    end
  end

Valery Sizov's avatar
Valery Sizov committed
164
165
166
  def can_change_visibility_level?(project, current_user)
    return false unless can?(current_user, :change_visibility_level, project)

167
168
    if project.fork_source
      project.fork_source.visibility_level > Gitlab::VisibilityLevel::PRIVATE
Valery Sizov's avatar
Valery Sizov committed
169
170
171
172
173
    else
      true
    end
  end

174
175
176
  def can_disable_emails?(project, current_user)
    return false if project.group&.emails_disabled?

177
    can?(current_user, :set_emails_disabled, project)
178
179
  end

180
  def last_push_event
181
    current_user&.recent_push(@project)
182
183
  end

184
  def link_to_autodeploy_doc
185
    link_to _('About auto deploy'), help_page_path('autodevops/index.md#auto-deploy'), target: '_blank'
186
187
  end

Valery Sizov's avatar
Valery Sizov committed
188
  def autodeploy_flash_notice(branch_name)
189
190
191
    translation = _("Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, choose a GitLab CI Yaml template and commit your changes. %{link_to_autodeploy_doc}") %
      { branch_name: truncate(sanitize(branch_name)), link_to_autodeploy_doc: link_to_autodeploy_doc }
    translation.html_safe
Valery Sizov's avatar
Valery Sizov committed
192
193
  end

194
  def project_list_cache_key(project, pipeline_status: true)
195
    key = [
196
      project.route.cache_key,
197
      project.cache_key,
198
      project.last_activity_date,
199
200
      controller.controller_name,
      controller.action_name,
201
      Gitlab::CurrentSettings.cache_key,
202
      "cross-project:#{can?(current_user, :read_cross_project)}",
203
      max_project_member_access_cache_key(project),
204
      pipeline_status,
205
      Gitlab::I18n.locale,
Mark Chao's avatar
Mark Chao committed
206
      'v2.6'
207
208
    ]

209
    key << pipeline_status_cache_key(project.pipeline_status) if pipeline_status && project.pipeline_status.has_status?
210
211
212
213

    key
  end

214
  def load_pipeline_status(projects)
215
216
    Gitlab::Cache::Ci::ProjectPipelineStatus
      .load_in_batch_for_projects(projects)
217
218
  end

219
  def show_no_ssh_key_message?
220
221
222
223
    Gitlab::CurrentSettings.user_show_add_ssh_key_message? &&
      cookies[:hide_no_ssh_message].blank? &&
      !current_user.hide_no_ssh_key &&
      current_user.require_ssh_key?
224
225
226
227
  end

  def show_no_password_message?
    cookies[:hide_no_password_message].blank? && !current_user.hide_no_password &&
228
      current_user.require_extra_setup_for_git_auth?
229
230
  end

231
232
  def show_auto_devops_implicitly_enabled_banner?(project, user)
    return false unless user_can_see_auto_devops_implicitly_enabled_banner?(project, user)
233

234
    cookies["hide_auto_devops_implicitly_enabled_banner_#{project.id}".to_sym].blank?
235
236
  end

237
  def link_to_set_password
238
    if current_user.require_password_creation_for_git?
239
240
241
242
243
244
      link_to s_('SetPasswordToCloneLink|set a password'), edit_profile_password_path
    else
      link_to s_('CreateTokenToCloneLink|create a personal access token'), profile_personal_access_tokens_path
    end
  end

245
246
247
248
249
250
251
252
253
  # Returns true if any projects are present.
  #
  # If the relation has a LIMIT applied we'll cast the relation to an Array
  # since repeated any? checks would otherwise result in multiple COUNT queries
  # being executed.
  #
  # If no limit is applied we'll just issue a COUNT since the result set could
  # be too large to load into memory.
  def any_projects?(projects)
254
255
    return projects.any? if projects.is_a?(Array)

256
257
258
259
260
261
262
    if projects.limit_value
      projects.to_a.any?
    else
      projects.except(:offset).any?
    end
  end

263
  # TODO: Remove this method when removing the feature flag
264
  # https://gitlab.com/gitlab-org/gitlab/merge_requests/11209#note_162234863
265
  # make sure to remove from the EE specific controller as well: ee/app/controllers/ee/dashboard/projects_controller.rb
266
  def show_projects?(projects, params)
267
    Feature.enabled?(:project_list_filter_bar) || !!(params[:personal] || params[:name] || any_projects?(projects))
268
269
  end

270
271
272
273
274
275
276
277
278
279
280
  def push_to_create_project_command(user = current_user)
    repository_url =
      if Gitlab::CurrentSettings.current_application_settings.enabled_git_access_protocol == 'http'
        user_url(user)
      else
        Gitlab.config.gitlab_shell.ssh_path_prefix + user.username
      end

    "git push --set-upstream #{repository_url}/$(git rev-parse --show-toplevel | xargs basename).git $(git rev-parse --abbrev-ref HEAD)"
  end

281
282
283
284
285
286
287
288
  def show_xcode_link?(project = @project)
    browser.platform.mac? && project.repository.xcode_project?
  end

  def xcode_uri_to_repo(project = @project)
    "xcode://clone?repo=#{CGI.escape(default_url_to_repo(project))}"
  end

289
290
291
292
  def link_to_bfg
    link_to 'BFG', 'https://rtyley.github.io/bfg-repo-cleaner/', target: '_blank', rel: 'noopener noreferrer'
  end

293
294
295
296
297
298
299
300
301
302
303
304
305
306
  def explore_projects_tab?
    current_page?(explore_projects_path) ||
      current_page?(trending_explore_projects_path) ||
      current_page?(starred_explore_projects_path)
  end

  def show_merge_request_count?(disabled: false, compact_mode: false)
    !disabled && !compact_mode && Feature.enabled?(:project_list_show_mr_count, default_enabled: true)
  end

  def show_issue_count?(disabled: false, compact_mode: false)
    !disabled && !compact_mode && Feature.enabled?(:project_list_show_issue_count, default_enabled: true)
  end

307
308
  # overridden in EE
  def settings_operations_available?
309
    can?(current_user, :read_environment, @project)
310
311
  end

Reuben Pereira's avatar
Reuben Pereira committed
312
313
314
315
316
317
318
319
320
321
322
323
324
325
  def error_tracking_setting_project_json
    setting = @project.error_tracking_setting

    return if setting.blank? || setting.project_slug.blank? ||
        setting.organization_slug.blank?

    {
      name: setting.project_name,
      organization_name: setting.organization_name,
      organization_slug: setting.organization_slug,
      slug: setting.project_slug
    }.to_json
  end

326
327
328
329
  def directory?
    @path.present?
  end

330
331
332
333
334
335
336
337
338
339
  def external_classification_label_help_message
    default_label = ::Gitlab::CurrentSettings.current_application_settings
                      .external_authorization_service_default_label

    s_(
      "ExternalAuthorizationService|When no classification label is set the "\
        "default label `%{default_label}` will be used."
    ) % { default_label: default_label }
  end

340
341
342
343
  def can_import_members?
    Ability.allowed?(current_user, :admin_project_member, @project)
  end

344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
  def project_can_be_shared?
    !membership_locked? || @project.allowed_to_share_with_group?
  end

  def membership_locked?
    false
  end

  def share_project_description(project)
    share_with_group   = project.allowed_to_share_with_group?
    share_with_members = !membership_locked?

    description =
      if share_with_group && share_with_members
        _("You can invite a new member to <strong>%{project_name}</strong> or invite another group.")
      elsif share_with_group
        _("You can invite another group to <strong>%{project_name}</strong>.")
      elsif share_with_members
        _("You can invite a new member to <strong>%{project_name}</strong>.")
      end

    description.html_safe % { project_name: project.name }
  end

368
369
370
371
  def metrics_external_dashboard_url
    @project.metrics_setting_external_dashboard_url
  end

372
373
374
375
  def grafana_integration_url
    @project.grafana_integration&.grafana_url
  end

376
377
  def grafana_integration_masked_token
    @project.grafana_integration&.masked_token
378
379
  end

380
381
382
383
  def grafana_integration_enabled?
    @project.grafana_integration&.enabled?
  end

384
385
386
387
388
389
390
391
  def project_license_name(project)
    project.repository.license&.name
  rescue GRPC::Unavailable, GRPC::DeadlineExceeded, Gitlab::Git::CommandError => e
    Gitlab::ErrorTracking.track_exception(e)

    nil
  end

392
393
394
  private

  def get_project_nav_tabs(project, current_user)
395
    nav_tabs = [:home]
396

397
398
399
    unless project.empty_repo?
      nav_tabs << [:files, :commits, :network, :graphs, :forks] if can?(current_user, :download_code, project)
      nav_tabs << :releases if can?(current_user, :read_release, project)
400
401
    end

402
    if project.repo_exists? && can?(current_user, :read_merge_request, project)
403
404
405
      nav_tabs << :merge_requests
    end

406
    if Gitlab.config.registry.enabled && can?(current_user, :read_container_image, project)
407
      nav_tabs << :container_registry
Kamil Trzcinski's avatar
Kamil Trzcinski committed
408
409
    end

410
411
    # Pipelines feature is tied to presence of builds
    if can?(current_user, :read_build, project)
412
      nav_tabs << :pipelines
413
414
415
    end

    if can?(current_user, :read_environment, project) || can?(current_user, :read_cluster, project)
416
      nav_tabs << :operations
417
418
    end

419
420
421
422
    if can?(current_user, :read_cycle_analytics, project)
      nav_tabs << :cycle_analytics
    end

423
424
425
426
427
428
    tab_ability_map.each do |tab, ability|
      if can?(current_user, ability, project)
        nav_tabs << tab
      end
    end

429
430
    nav_tabs << external_nav_tabs(project)

431
432
433
    nav_tabs.flatten
  end

434
435
436
  def external_nav_tabs(project)
    [].tap do |tabs|
      tabs << :external_issue_tracker if project.external_issue_tracker
437
      tabs << :external_wiki if project.external_wiki
438
439
440
    end
  end

441
442
  def tab_ability_map
    {
443
444
      environments:     :read_environment,
      milestones:       :read_milestone,
445
      snippets:         :read_snippet,
446
447
      settings:         :admin_project,
      builds:           :read_build,
448
      clusters:         :read_cluster,
449
      serverless:       :read_cluster,
450
      error_tracking:   :read_sentry_issue,
451
452
453
454
      labels:           :read_label,
      issues:           :read_issue,
      project_members:  :read_project_member,
      wiki:             :read_wiki
455
    }
456
  end
457

458
459
460
461
462
  def search_tab_ability_map
    @search_tab_ability_map ||= tab_ability_map.merge(
      blobs:          :download_code,
      commits:        :download_code,
      merge_requests: :read_merge_request,
463
      notes:          [:read_merge_request, :download_code, :read_issue, :read_snippet],
464
      members:        :read_project_member
465
    )
466
  end
467

468
469
  def project_lfs_status(project)
    if project.lfs_enabled?
470
      content_tag(:span, class: 'lfs-enabled') do
471
        s_('LFSStatus|Enabled')
472
473
      end
    else
474
      content_tag(:span, class: 'lfs-disabled') do
475
        s_('LFSStatus|Disabled')
476
477
478
479
      end
    end
  end

480
481
  def git_user_name
    if current_user
482
      current_user.name.gsub('"', '\"')
483
    else
484
      _("Your name")
485
486
487
488
489
    end
  end

  def git_user_email
    if current_user
490
      current_user.commit_email
491
492
493
494
    else
      "your@email.com"
    end
  end
495

496
  def default_url_to_repo(project = @project)
497
498
    case default_clone_protocol
    when 'ssh'
499
500
      project.ssh_url_to_repo
    else
501
      project.http_url_to_repo
502
    end
503
  end
504

505
506
507
508
  def default_clone_label
    _("Copy %{protocol} clone URL") % { protocol: default_clone_protocol.upcase }
  end

509
  def default_clone_protocol
510
511
    if allowed_protocols_present?
      enabled_protocol
512
    else
513
514
515
516
517
518
519
520
521
      extra_default_clone_protocol
    end
  end

  def extra_default_clone_protocol
    if !current_user || current_user.require_ssh_key?
      gitlab_config.protocol
    else
      'ssh'
522
    end
523
  end
524

Matija Čupić's avatar
Matija Čupić committed
525
526
527
528
  def sidebar_operations_link_path(project = @project)
    metrics_project_environments_path(project) if can?(current_user, :read_environment, project)
  end

529
530
  def project_last_activity(project)
    if project.last_activity_at
531
      time_ago_with_tooltip(project.last_activity_at, placement: 'bottom', html_class: 'last_activity_time_ago')
532
    else
533
      s_("ProjectLastActivity|Never")
534
535
    end
  end
536

537
538
  def project_wiki_path_with_version(proj, page, version, is_newest)
    url_params = is_newest ? {} : { version_id: version }
539
    project_wiki_path(proj, page, url_params)
540
  end
541

Valery Sizov's avatar
Valery Sizov committed
542
543
544
  def project_status_css_class(status)
    case status
    when "started"
545
      "table-active"
Valery Sizov's avatar
Valery Sizov committed
546
    when "failed"
547
      "table-danger"
Valery Sizov's avatar
Valery Sizov committed
548
    when "finished"
549
      "table-success"
Valery Sizov's avatar
Valery Sizov committed
550
551
    end
  end
552

553
  def readme_cache_key
554
    sha = @project.commit.try(:sha) || 'nil'
555
    [@project.full_path, sha, "readme"].join('-')
556
  end
557

558
559
560
  def current_ref
    @ref || @repository.try(:root_ref)
  end
561

562
563
564
  def project_child_container_class(view_path)
    view_path == "projects/issues/issues" ? "prepend-top-default" : "project-show-#{view_path}"
  end
565
566
567
568

  def project_issues(project)
    IssuesFinder.new(current_user, project_id: project.id).execute
  end
Luke "Jared" Bennett's avatar
Luke "Jared" Bennett committed
569

570
571
572
  def restricted_levels
    return [] if current_user.admin?

573
    Gitlab::CurrentSettings.restricted_visibility_levels || []
Luke "Jared" Bennett's avatar
Luke "Jared" Bennett committed
574
  end
575

576
577
578
579
580
581
582
  def project_permissions_settings(project)
    feature = project.project_feature
    {
      visibilityLevel: project.visibility_level,
      requestAccessEnabled: !!project.request_access_enabled,
      issuesAccessLevel: feature.issues_access_level,
      repositoryAccessLevel: feature.repository_access_level,
583
      forkingAccessLevel: feature.forking_access_level,
584
585
586
587
      mergeRequestsAccessLevel: feature.merge_requests_access_level,
      buildsAccessLevel: feature.builds_access_level,
      wikiAccessLevel: feature.wiki_access_level,
      snippetsAccessLevel: feature.snippets_access_level,
588
      pagesAccessLevel: feature.pages_access_level,
589
      containerRegistryEnabled: !!project.container_registry_enabled,
590
591
      lfsEnabled: !!project.lfs_enabled,
      emailsDisabled: project.emails_disabled?
592
593
594
595
    }
  end

  def project_permissions_panel_data(project)
596
    {
597
      currentSettings: project_permissions_settings(project),
598
      canDisableEmails: can_disable_emails?(project, current_user),
599
600
601
602
      canChangeVisibilityLevel: can_change_visibility_level?(project, current_user),
      allowedVisibilityOptions: project_allowed_visibility_levels(project),
      visibilityHelpPath: help_page_path('public_access/public_access'),
      registryAvailable: Gitlab.config.registry.enabled,
603
      registryHelpPath: help_page_path('user/packages/container_registry/index'),
604
      lfsAvailable: Gitlab.config.lfs.enabled,
605
      lfsHelpPath: help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs'),
606
607
      lfsObjectsExist: project.lfs_objects.exists?,
      lfsObjectsRemovalHelpPath: help_page_path('administration/lfs/manage_large_binaries_with_git_lfs', anchor: 'removing-objects-from-lfs'),
608
609
      pagesAvailable: Gitlab.config.pages.enabled,
      pagesAccessControlEnabled: Gitlab.config.pages.access_control,
610
      pagesAccessControlForced: ::Gitlab::Pages.access_control_is_forced?,
611
      pagesHelpPath: help_page_path('user/project/pages/introduction', anchor: 'gitlab-pages-access-control-core')
612
    }
613
  end
614

615
616
  def project_permissions_panel_data_json(project)
    project_permissions_panel_data(project).to_json.html_safe
617
618
619
620
621
622
623
624
  end

  def project_allowed_visibility_levels(project)
    Gitlab::VisibilityLevel.values.select do |level|
      project.visibility_level_allowed?(level) && !restricted_levels.include?(level)
    end
  end

625
626
627
628
629
630
631
  def find_file_path
    return unless @project && !@project.empty_repo?

    ref = @ref || @project.repository.root_ref

    project_find_file_path(@project, ref)
  end
632
633
634
635

  def can_show_last_commit_in_list?(project)
    can?(current_user, :read_cross_project) && project.commit
  end
Rob Watson's avatar
Rob Watson committed
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653

  def pages_https_only_disabled?
    !@project.pages_domains.all?(&:https?)
  end

  def pages_https_only_title
    return unless pages_https_only_disabled?

    "You must enable HTTPS for all your domains first"
  end

  def pages_https_only_label_class
    if pages_https_only_disabled?
      "list-label disabled"
    else
      "list-label"
    end
  end
654

655
656
657
658
659
  def filter_starrer_path(options = {})
    options = params.slice(:sort).merge(options).permit!
    "#{request.path}?#{options.to_param}"
  end

660
661
662
663
  def sidebar_projects_paths
    %w[
      projects#show
      projects#activity
Filipa Lacerda's avatar
Filipa Lacerda committed
664
      releases#index
665
666
667
    ]
  end

668
669
670
671
672
673
  def sidebar_settings_paths
    %w[
      projects#edit
      project_members#index
      integrations#show
      services#edit
674
675
676
      hooks#index
      hooks#edit
      hook_logs#show
677
678
      repository#show
      ci_cd#show
679
      operations#show
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
      badges#index
      pages#show
    ]
  end

  def sidebar_repository_paths
    %w[
      tree
      blob
      blame
      edit_tree
      new_tree
      find_file
      commit
      commits
      compare
      projects/repositories
      tags
      branches
      graphs
      network
    ]
  end
703
704
705
706
707

  def sidebar_operations_paths
    %w[
      environments
      clusters
708
      functions
709
      error_tracking
710
711
      user
      gcp
712
      logs
713
714
    ]
  end
715
716
717
718
719
720
721

  def user_can_see_auto_devops_implicitly_enabled_banner?(project, user)
    Ability.allowed?(user, :admin_project, project) &&
      project.has_auto_devops_implicitly_enabled? &&
      project.builds_enabled? &&
      !project.repository.gitlab_ci_yml
  end
722
723

  def vue_file_list_enabled?
724
    Feature.enabled?(:vue_file_list, @project, default_enabled: true)
725
  end
726

727
728
729
730
  def native_code_navigation_enabled?(project)
    Feature.enabled?(:code_navigation, project)
  end

731
732
733
  def show_visibility_confirm_modal?(project)
    project.unlink_forks_upon_visibility_decrease_enabled? && project.visibility_level > Gitlab::VisibilityLevel::PRIVATE && project.forks_count > 0
  end
734
735

  def settings_container_registry_expiration_policy_available?(project)
736
    Gitlab.config.registry.enabled &&
737
      can?(current_user, :destroy_container_image, project)
738
  end
randx's avatar
randx committed
739
end