diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-05-19 15:44:42 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-05-19 15:44:42 +0000 |
commit | 4555e1b21c365ed8303ffb7a3325d773c9b8bf31 (patch) | |
tree | 5423a1c7516cffe36384133ade12572cf709398d /app/helpers | |
parent | e570267f2f6b326480d284e0164a6464ba4081bc (diff) | |
download | gitlab-ce-4555e1b21c365ed8303ffb7a3325d773c9b8bf31.tar.gz |
Add latest changes from gitlab-org/gitlab@13-12-stable-eev13.12.0-rc42
Diffstat (limited to 'app/helpers')
81 files changed, 895 insertions, 810 deletions
diff --git a/app/helpers/analytics/navbar_helper.rb b/app/helpers/analytics/navbar_helper.rb index 33a5028cdf1..091571ff15a 100644 --- a/app/helpers/analytics/navbar_helper.rb +++ b/app/helpers/analytics/navbar_helper.rb @@ -13,14 +13,6 @@ module Analytics end end - def project_analytics_navbar_links(project, current_user) - [ - cycle_analytics_navbar_link(project, current_user), - repository_analytics_navbar_link(project, current_user), - ci_cd_analytics_navbar_link(project, current_user) - ].compact - end - def group_analytics_navbar_links(group, current_user) [] end @@ -30,40 +22,7 @@ module Analytics def navbar_sub_item(args) NavbarSubItem.new(**args) end - - def cycle_analytics_navbar_link(project, current_user) - return unless project_nav_tab?(:cycle_analytics) - - navbar_sub_item( - title: _('Value Stream'), - path: 'cycle_analytics#show', - link: project_cycle_analytics_path(project), - link_to_options: { class: 'shortcuts-project-cycle-analytics' } - ) - end - - def repository_analytics_navbar_link(project, current_user) - return if project.empty_repo? - - navbar_sub_item( - title: _('Repository'), - path: 'graphs#charts', - link: charts_project_graph_path(project, current_ref), - link_to_options: { class: 'shortcuts-repository-charts' } - ) - end - - def ci_cd_analytics_navbar_link(project, current_user) - return unless project_nav_tab?(:pipelines) - return unless project.feature_available?(:builds, current_user) || !project.empty_repo? - - navbar_sub_item( - title: _('CI/CD'), - path: 'pipelines#charts', - link: charts_project_pipelines_path(project) - ) - end end end -Analytics::NavbarHelper.prepend_if_ee('EE::Analytics::NavbarHelper') +Analytics::NavbarHelper.prepend_mod_with('Analytics::NavbarHelper') diff --git a/app/helpers/appearances_helper.rb b/app/helpers/appearances_helper.rb index 65feea4f6e0..60e37c96f61 100644 --- a/app/helpers/appearances_helper.rb +++ b/app/helpers/appearances_helper.rb @@ -83,5 +83,4 @@ module AppearancesHelper end end -AppearancesHelper.prepend_if_ee('EE::AppearancesHelper') -AppearancesHelper.prepend_if_jh('JH::AppearancesHelper') +AppearancesHelper.prepend_mod diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index a2ef2f1207c..2e15b3f22c2 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -72,7 +72,7 @@ module ApplicationHelper else 'Never' end - rescue + rescue StandardError 'Never' end @@ -382,15 +382,26 @@ module ApplicationHelper def autocomplete_data_sources(object, noteable_type) return {} unless object && noteable_type - { - members: members_project_autocomplete_sources_path(object, type: noteable_type, type_id: params[:id]), - issues: issues_project_autocomplete_sources_path(object), - mergeRequests: merge_requests_project_autocomplete_sources_path(object), - labels: labels_project_autocomplete_sources_path(object, type: noteable_type, type_id: params[:id]), - milestones: milestones_project_autocomplete_sources_path(object), - commands: commands_project_autocomplete_sources_path(object, type: noteable_type, type_id: params[:id]), - snippets: snippets_project_autocomplete_sources_path(object) - } + if object.is_a?(Group) + { + members: members_group_autocomplete_sources_path(object, type: noteable_type, type_id: params[:id]), + issues: issues_group_autocomplete_sources_path(object), + mergeRequests: merge_requests_group_autocomplete_sources_path(object), + labels: labels_group_autocomplete_sources_path(object, type: noteable_type, type_id: params[:id]), + milestones: milestones_group_autocomplete_sources_path(object), + commands: commands_group_autocomplete_sources_path(object, type: noteable_type, type_id: params[:id]) + } + else + { + members: members_project_autocomplete_sources_path(object, type: noteable_type, type_id: params[:id]), + issues: issues_project_autocomplete_sources_path(object), + mergeRequests: merge_requests_project_autocomplete_sources_path(object), + labels: labels_project_autocomplete_sources_path(object, type: noteable_type, type_id: params[:id]), + milestones: milestones_project_autocomplete_sources_path(object), + commands: commands_project_autocomplete_sources_path(object, type: noteable_type, type_id: params[:id]), + snippets: snippets_project_autocomplete_sources_path(object) + } + end end def asset_to_string(name) @@ -409,5 +420,4 @@ module ApplicationHelper end end -ApplicationHelper.prepend_if_ee('EE::ApplicationHelper') -ApplicationHelper.prepend_if_jh('JH::ApplicationHelper') +ApplicationHelper.prepend_mod diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb index 504ebb5606e..0e3dff27da9 100644 --- a/app/helpers/application_settings_helper.rb +++ b/app/helpers/application_settings_helper.rb @@ -233,6 +233,7 @@ module ApplicationSettingsHelper :external_pipeline_validation_service_token, :external_pipeline_validation_service_url, :first_day_of_week, + :floc_enabled, :force_pages_access_control, :gitaly_timeout_default, :gitaly_timeout_medium, @@ -302,6 +303,7 @@ module ApplicationSettingsHelper :sourcegraph_public_only, :spam_check_endpoint_enabled, :spam_check_endpoint_url, + :spam_check_api_key, :terminal_max_session_time, :terms, :throttle_authenticated_api_enabled, @@ -310,9 +312,15 @@ module ApplicationSettingsHelper :throttle_authenticated_web_enabled, :throttle_authenticated_web_period_in_seconds, :throttle_authenticated_web_requests_per_period, + :throttle_authenticated_packages_api_enabled, + :throttle_authenticated_packages_api_period_in_seconds, + :throttle_authenticated_packages_api_requests_per_period, :throttle_unauthenticated_enabled, :throttle_unauthenticated_period_in_seconds, :throttle_unauthenticated_requests_per_period, + :throttle_unauthenticated_packages_api_enabled, + :throttle_unauthenticated_packages_api_period_in_seconds, + :throttle_unauthenticated_packages_api_requests_per_period, :throttle_protected_paths_enabled, :throttle_protected_paths_period_in_seconds, :throttle_protected_paths_requests_per_period, @@ -358,7 +366,8 @@ module ApplicationSettingsHelper :rate_limiting_response_text, :container_registry_expiration_policies_worker_capacity, :container_registry_cleanup_tags_service_max_list_size, - :keep_latest_artifact + :keep_latest_artifact, + :whats_new_variant ] end @@ -387,7 +396,7 @@ module ApplicationSettingsHelper end def integration_expanded?(substring) - @application_setting.errors.any? { |k| k.to_s.start_with?(substring) } + @application_setting.errors.messages.any? { |k, _| k.to_s.start_with?(substring) } end def instance_clusters_enabled? @@ -429,8 +438,8 @@ module ApplicationSettingsHelper end end -ApplicationSettingsHelper.prepend_if_ee('EE::ApplicationSettingsHelper') +ApplicationSettingsHelper.prepend_mod_with('ApplicationSettingsHelper') # The methods in `EE::ApplicationSettingsHelper` should be available as both # instance and class methods. -ApplicationSettingsHelper.extend_if_ee('EE::ApplicationSettingsHelper') +ApplicationSettingsHelper.extend_mod_with('ApplicationSettingsHelper') diff --git a/app/helpers/auth_helper.rb b/app/helpers/auth_helper.rb index cacf9c7ad0b..a0c3a6f2f52 100644 --- a/app/helpers/auth_helper.rb +++ b/app/helpers/auth_helper.rb @@ -16,7 +16,7 @@ module AuthHelper twitter ).freeze LDAP_PROVIDER = /\Aldap/.freeze - TRIAL_REGISTRATION_PROVIDERS = %w(google_oauth2 github).freeze + POPULAR_PROVIDERS = %w(google_oauth2 github).freeze def ldap_enabled? Gitlab::Auth::Ldap::Config.enabled? @@ -116,19 +116,12 @@ module AuthHelper providers = button_based_providers.map(&:to_s) - disabled_providers providers.sort_by do |provider| - case provider - when 'google_oauth2' - 0 - when 'github' - 1 - else - 2 - end + POPULAR_PROVIDERS.index(provider) || POPULAR_PROVIDERS.length end end - def trial_enabled_button_based_providers - enabled_button_based_providers & TRIAL_REGISTRATION_PROVIDERS + def popular_enabled_button_based_providers + enabled_button_based_providers & POPULAR_PROVIDERS end def button_based_providers_enabled? @@ -176,11 +169,23 @@ module AuthHelper !current_user end + def auth_app_owner_text(owner) + return unless owner + + if owner.is_a?(Group) + group_link = link_to(owner.name, group_path(owner)) + _("This application was created for group %{group_link}.").html_safe % { group_link: group_link } + else + user_link = link_to(owner.name, user_path(owner)) + _("This application was created by %{user_link}.").html_safe % { user_link: user_link } + end + end + extend self end -AuthHelper.prepend_if_ee('EE::AuthHelper') +AuthHelper.prepend_mod_with('AuthHelper') # The methods added in EE should be available as both class and instance # methods, just like the methods provided by `AuthHelper` itself. -AuthHelper.extend_if_ee('EE::AuthHelper') +AuthHelper.extend_mod_with('AuthHelper') diff --git a/app/helpers/avatars_helper.rb b/app/helpers/avatars_helper.rb index 09f91f350bd..4cfa1528d9b 100644 --- a/app/helpers/avatars_helper.rb +++ b/app/helpers/avatars_helper.rb @@ -98,6 +98,14 @@ module AvatarsHelper end end + def avatar_without_link(resource, options = {}) + if resource.is_a?(User) + user_avatar_without_link(options.merge(user: resource)) + elsif resource.is_a?(Group) + group_icon(resource, options.merge(class: 'avatar')) + end + end + private def avatar_icon_by_user_email_or_gravatar(email, size, scale, only_path:) @@ -136,11 +144,12 @@ module AvatarsHelper def source_identicon(source, options = {}) bg_key = (source.id % 7) + 1 + size_class = "s#{options[:size]}" if options[:size] options[:class] = - [*options[:class], "identicon bg#{bg_key}"].join(' ') + [*options[:class], "identicon bg#{bg_key}", size_class].compact.join(' ') - content_tag(:div, class: options[:class].strip) do + content_tag(:span, class: options[:class].strip) do source.name[0, 1].upcase end end diff --git a/app/helpers/award_emoji_helper.rb b/app/helpers/award_emoji_helper.rb index af9ab93d459..196415bb363 100644 --- a/app/helpers/award_emoji_helper.rb +++ b/app/helpers/award_emoji_helper.rb @@ -17,4 +17,4 @@ module AwardEmojiHelper end end -AwardEmojiHelper.prepend_if_ee('EE::AwardEmojiHelper') +AwardEmojiHelper.prepend_mod_with('AwardEmojiHelper') diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 3144686bba9..dfd6de3f1d5 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -206,10 +206,6 @@ module BlobHelper @gitlab_ci_ymls ||= TemplateFinder.all_template_names(project, :gitlab_ci_ymls) end - def gitlab_ci_syntax_ymls(project) - @gitlab_ci_syntax_ymls ||= TemplateFinder.all_template_names(project, :gitlab_ci_syntax_ymls) - end - def metrics_dashboard_ymls(project) @metrics_dashboard_ymls ||= TemplateFinder.all_template_names(project, :metrics_dashboard_ymls) end diff --git a/app/helpers/boards_helper.rb b/app/helpers/boards_helper.rb index 49963d14934..f72f8bfd151 100644 --- a/app/helpers/boards_helper.rb +++ b/app/helpers/boards_helper.rb @@ -10,7 +10,7 @@ module BoardsHelper boards_endpoint: @boards_endpoint, lists_endpoint: board_lists_path(board), board_id: board.id, - disabled: (!can?(current_user, :create_non_backlog_issues, board)).to_s, + disabled: board.disabled_for?(current_user).to_s, root_path: root_path, full_path: full_path, bulk_update_path: @bulk_issues_path, @@ -89,6 +89,10 @@ module BoardsHelper @current_board_parent ||= @group || @project end + def current_board_namespace + @current_board_namespace = board.group_board? ? @group : @project.namespace + end + def can_update? can?(current_user, :admin_issue, board) end @@ -136,4 +140,4 @@ module BoardsHelper end end -BoardsHelper.prepend_if_ee('EE::BoardsHelper') +BoardsHelper.prepend_mod_with('BoardsHelper') diff --git a/app/helpers/branches_helper.rb b/app/helpers/branches_helper.rb index 8f87cd5bfe0..a500a695029 100644 --- a/app/helpers/branches_helper.rb +++ b/app/helpers/branches_helper.rb @@ -22,4 +22,4 @@ module BranchesHelper end end -BranchesHelper.prepend_if_ee('EE::BranchesHelper') +BranchesHelper.prepend_mod_with('BranchesHelper') diff --git a/app/helpers/button_helper.rb b/app/helpers/button_helper.rb index 1b00f583b55..27d6ee57d8b 100644 --- a/app/helpers/button_helper.rb +++ b/app/helpers/button_helper.rb @@ -100,4 +100,4 @@ module ButtonHelper end end -ButtonHelper.prepend_if_ee('EE::ButtonHelper') +ButtonHelper.prepend_mod_with('ButtonHelper') diff --git a/app/helpers/ci/jobs_helper.rb b/app/helpers/ci/jobs_helper.rb index a0d169c1358..23f2a082a68 100644 --- a/app/helpers/ci/jobs_helper.rb +++ b/app/helpers/ci/jobs_helper.rb @@ -15,7 +15,8 @@ module Ci "build_stage" => @build.stage, "log_state" => '', "build_options" => javascript_build_options, - "retry_outdated_job_docs_url" => help_page_path('ci/pipelines/settings', anchor: 'retry-outdated-jobs') + "retry_outdated_job_docs_url" => help_page_path('ci/pipelines/settings', anchor: 'retry-outdated-jobs'), + "code_quality_help_url" => help_page_path('user/project/merge_requests/code_quality', anchor: 'troubleshooting') } end @@ -36,4 +37,4 @@ module Ci end end -Ci::JobsHelper.prepend_if_ee('::EE::Ci::JobsHelper') +Ci::JobsHelper.prepend_mod_with('Ci::JobsHelper') diff --git a/app/helpers/ci/pipeline_editor_helper.rb b/app/helpers/ci/pipeline_editor_helper.rb index ceb18d90c92..8c8ee2d4d0f 100644 --- a/app/helpers/ci/pipeline_editor_helper.rb +++ b/app/helpers/ci/pipeline_editor_helper.rb @@ -9,21 +9,29 @@ module Ci end def js_pipeline_editor_data(project) + commit_sha = project.commit ? project.commit.sha : '' { "ci-config-path": project.ci_config_path_or_default, - "commit-sha" => project.commit ? project.commit.sha : '', + "ci-examples-help-page-path" => help_page_path('ci/examples/README'), + "ci-help-page-path" => help_page_path('ci/README'), + "commit-sha" => commit_sha, "default-branch" => project.default_branch, "empty-state-illustration-path" => image_path('illustrations/empty-state/empty-dag-md.svg'), "initial-branch-name": params[:branch_name], "lint-help-page-path" => help_page_path('ci/lint', anchor: 'validate-basic-logic-and-syntax'), + "needs-help-page-path" => help_page_path('ci/yaml/README', anchor: 'needs'), "new-merge-request-path" => namespace_project_new_merge_request_path, + "pipeline_etag" => project.commit ? graphql_etag_pipeline_sha_path(commit_sha) : '', + "pipeline-page-path" => project_pipelines_path(project), "project-path" => project.path, "project-full-path" => project.full_path, "project-namespace" => project.namespace.full_path, + "runner-help-page-path" => help_page_path('ci/runners/README'), + "total-branches" => project.repository.branches.length, "yml-help-page-path" => help_page_path('ci/yaml/README') } end end end -Ci::PipelineEditorHelper.prepend_if_ee('EE::Ci::PipelineEditorHelper') +Ci::PipelineEditorHelper.prepend_mod_with('Ci::PipelineEditorHelper') diff --git a/app/helpers/ci/pipelines_helper.rb b/app/helpers/ci/pipelines_helper.rb index cabb43f45fd..f42cd53ae3a 100644 --- a/app/helpers/ci/pipelines_helper.rb +++ b/app/helpers/ci/pipelines_helper.rb @@ -50,10 +50,9 @@ module Ci { name: 'Gradle', logo: image_path('illustrations/logos/gradle.svg') }, { name: 'Grails', logo: image_path('illustrations/logos/grails.svg') }, { name: 'dotNET', logo: image_path('illustrations/logos/dotnet.svg') }, - { name: 'Rails', logo: image_path('illustrations/logos/rails.svg') }, { name: 'Julia', logo: image_path('illustrations/logos/julia.svg') }, { name: 'Laravel', logo: image_path('illustrations/logos/laravel.svg') }, - { name: 'Latex', logo: image_path('illustrations/logos/latex.svg') }, + { name: 'LaTeX', logo: image_path('illustrations/logos/latex.svg') }, { name: 'Maven', logo: image_path('illustrations/logos/maven.svg') }, { name: 'Mono', logo: image_path('illustrations/logos/mono.svg') }, { name: 'Nodejs', logo: image_path('illustrations/logos/node_js.svg') }, diff --git a/app/helpers/ci/runners_helper.rb b/app/helpers/ci/runners_helper.rb index 82347053d6f..550fa4de2c5 100644 --- a/app/helpers/ci/runners_helper.rb +++ b/app/helpers/ci/runners_helper.rb @@ -75,4 +75,4 @@ module Ci end end -Ci::RunnersHelper.prepend_if_ee('EE::Ci::RunnersHelper') +Ci::RunnersHelper.prepend_mod_with('Ci::RunnersHelper') diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index e7a81eb5629..9b952ad127e 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -128,7 +128,7 @@ module CommitsHelper %w(btn gpg-status-box) + Array(additional_classes) end - def conditionally_paginate_diff_files(diffs, paginate:, per: Projects::CommitController::COMMIT_DIFFS_PER_PAGE) + def conditionally_paginate_diff_files(diffs, paginate:, per:) if paginate Kaminari.paginate_array(diffs.diff_files.to_a).page(params[:page]).per(per) else @@ -148,6 +148,27 @@ module CommitsHelper end end + # This is used to calculate a cache key for the app/views/projects/commits/_commit.html.haml + # partial. It takes some of the same parameters as used in the partial and will hash the + # current pipeline status. + # + # This includes a keyed hash for values that can be nil, to prevent invalid cache entries + # being served if the order should change in future. + def commit_partial_cache_key(commit, ref:, merge_request:, request:) + [ + commit, + commit.author, + ref, + { + merge_request: merge_request, + pipeline_status: commit.status_for(ref), + xhr: request.xhr?, + controller: controller.controller_path, + path: @path # referred to in #link_to_browse_code + } + ] + end + protected # Private: Returns a link to a person. If the person has a matching user and diff --git a/app/helpers/dashboard_helper.rb b/app/helpers/dashboard_helper.rb index 08f357916b5..95bbf2eff41 100644 --- a/app/helpers/dashboard_helper.rb +++ b/app/helpers/dashboard_helper.rb @@ -66,4 +66,4 @@ module DashboardHelper end end -DashboardHelper.prepend_if_ee('EE::DashboardHelper') +DashboardHelper.prepend_mod_with('DashboardHelper') diff --git a/app/helpers/dev_ops_report_helper.rb b/app/helpers/dev_ops_report_helper.rb index ab7e56fc1a2..c2200a4c3da 100644 --- a/app/helpers/dev_ops_report_helper.rb +++ b/app/helpers/dev_ops_report_helper.rb @@ -1,18 +1,80 @@ # frozen_string_literal: true module DevOpsReportHelper + def devops_score_metrics(metric) + return {} if metric.blank? + + { + averageScore: average_score_data(metric), + cards: devops_score_card_data(metric), + createdAt: metric.created_at.strftime('%Y-%m-%d %H:%M') + } + end + + private + + def format_score(score) + precision = score < 1 ? 2 : 1 + number_with_precision(score, precision: precision) + end + def score_level(score) if score < 33.33 - 'low' + { + label: s_('DevopsReport|Low'), + variant: 'muted' + } elsif score < 66.66 - 'average' + { + label: s_('DevopsReport|Moderate'), + variant: 'neutral' + } else - 'high' + { + label: s_('DevopsReport|High'), + variant: 'success' + } end end - def format_score(score) - precision = score < 1 ? 2 : 1 - number_with_precision(score, precision: precision) + def average_score_level(score) + if score < 33.33 + { + label: s_('DevopsReport|Low'), + variant: 'danger', + icon: 'status-failed' + } + elsif score < 66.66 + { + label: s_('DevopsReport|Moderate'), + variant: 'warning', + icon: 'status-alert' + } + else + { + label: s_('DevopsReport|High'), + variant: 'success', + icon: 'status_success_solid' + } + end + end + + def average_score_data(metric) + { + value: format_score(metric.average_percentage_score), + scoreLevel: average_score_level(metric.average_percentage_score) + } + end + + def devops_score_card_data(metric) + metric.cards.map do |card| + { + title: "#{card.title} #{card.description}", + usage: format_score(card.instance_score), + leadInstance: format_score(card.leader_score), + score: format_score(card.percentage_score), + scoreLevel: score_level(card.percentage_score) + } + end end end diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb index 7bf3cb6230b..e430b0f402b 100644 --- a/app/helpers/diff_helper.rb +++ b/app/helpers/diff_helper.rb @@ -23,14 +23,16 @@ module DiffHelper end end + def show_only_context_commits? + !!params[:only_context_commits] || @merge_request&.commits&.empty? + end + def diff_options options = { ignore_whitespace_change: hide_whitespace?, expanded: diffs_expanded? } if action_name == 'diff_for_path' options[:expanded] = true options[:paths] = params.values_at(:old_path, :new_path) - elsif action_name == 'show' - options[:include_context_commits] = true unless @project.context_commits_enabled? end options diff --git a/app/helpers/emails_helper.rb b/app/helpers/emails_helper.rb index b58ff21b257..0b1bdb68e50 100644 --- a/app/helpers/emails_helper.rb +++ b/app/helpers/emails_helper.rb @@ -298,4 +298,4 @@ module EmailsHelper end end -EmailsHelper.prepend_if_ee('EE::EmailsHelper') +EmailsHelper.prepend_mod_with('EmailsHelper') diff --git a/app/helpers/environments_helper.rb b/app/helpers/environments_helper.rb index 7f0c59f65a0..594c6fedef1 100644 --- a/app/helpers/environments_helper.rb +++ b/app/helpers/environments_helper.rb @@ -34,7 +34,7 @@ module EnvironmentsHelper def environment_logs_data(project, environment) { "environment_name": environment.name, - "environments_path": project_environments_path(project, format: :json), + "environments_path": api_v4_projects_environments_path(id: project.id), "environment_id": environment.id, "cluster_applications_documentation_path" => help_page_path('user/clusters/applications.md', anchor: 'elastic-stack'), "clusters_path": project_clusters_path(project, format: :json) @@ -62,7 +62,8 @@ module EnvironmentsHelper 'validate_query_path' => validate_query_project_prometheus_metrics_path(project), 'custom_metrics_available' => "#{custom_metrics_available?(project)}", 'prometheus_alerts_available' => "#{can?(current_user, :read_prometheus_alerts, project)}", - 'dashboard_timezone' => project.metrics_setting_dashboard_timezone.to_s.upcase + 'dashboard_timezone' => project.metrics_setting_dashboard_timezone.to_s.upcase, + 'has_managed_prometheus' => has_managed_prometheus?(project).to_s } end @@ -78,6 +79,10 @@ module EnvironmentsHelper } end + def has_managed_prometheus?(project) + project.prometheus_service&.prometheus_available? == true + end + def metrics_dashboard_base_path(environment, project) # This is needed to support our transition from environment scoped metric paths to project scoped. if project @@ -117,4 +122,4 @@ module EnvironmentsHelper end end -EnvironmentsHelper.prepend_if_ee('::EE::EnvironmentsHelper') +EnvironmentsHelper.prepend_mod_with('EnvironmentsHelper') diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb index 6b3abb4274e..03c3ee3363d 100644 --- a/app/helpers/events_helper.rb +++ b/app/helpers/events_helper.rb @@ -228,7 +228,7 @@ module EventsHelper def event_commit_title(message) message ||= '' (message.split("\n").first || "").truncate(70) - rescue + rescue StandardError "--broken encoding" end @@ -290,4 +290,4 @@ module EventsHelper end end -EventsHelper.prepend_if_ee('EE::EventsHelper') +EventsHelper.prepend_mod_with('EventsHelper') diff --git a/app/helpers/export_helper.rb b/app/helpers/export_helper.rb index 38a4f7f1b4b..92d06471384 100644 --- a/app/helpers/export_helper.rb +++ b/app/helpers/export_helper.rb @@ -25,4 +25,4 @@ module ExportHelper end end -ExportHelper.prepend_if_ee('EE::ExportHelper') +ExportHelper.prepend_mod_with('ExportHelper') diff --git a/app/helpers/feature_flags_helper.rb b/app/helpers/feature_flags_helper.rb index e50191a471f..2b8804bc07e 100644 --- a/app/helpers/feature_flags_helper.rb +++ b/app/helpers/feature_flags_helper.rb @@ -16,4 +16,4 @@ module FeatureFlagsHelper end end -FeatureFlagsHelper.prepend_if_ee('::EE::FeatureFlagsHelper') +FeatureFlagsHelper.prepend_mod_with('FeatureFlagsHelper') diff --git a/app/helpers/form_helper.rb b/app/helpers/form_helper.rb index d0276c91316..cf3e99eee49 100644 --- a/app/helpers/form_helper.rb +++ b/app/helpers/form_helper.rb @@ -131,4 +131,4 @@ module FormHelper end end -FormHelper.prepend_if_ee('::EE::FormHelper') +FormHelper.prepend_mod_with('FormHelper') diff --git a/app/helpers/gitlab_routing_helper.rb b/app/helpers/gitlab_routing_helper.rb index 48af4793fb0..0a684d92eb1 100644 --- a/app/helpers/gitlab_routing_helper.rb +++ b/app/helpers/gitlab_routing_helper.rb @@ -166,6 +166,16 @@ module GitlabRoutingHelper resend_invite_group_group_member_path(group_member.source, group_member) end + # Members + def source_members_url(member) + case member.source_type + when 'Namespace' + group_group_members_url(member.source) + when 'Project' + project_project_members_url(member.source) + end + end + # Artifacts # Rails path generators are slow because they need to do large regex comparisons @@ -354,6 +364,10 @@ module GitlabRoutingHelper [api_graphql_path, "pipelines/id/#{pipeline.id}"].join(':') end + def graphql_etag_pipeline_sha_path(sha) + [api_graphql_path, "pipelines/sha/#{sha}"].join(':') + end + private def snippet_query_params(snippet, *args) @@ -370,4 +384,4 @@ module GitlabRoutingHelper end end -GitlabRoutingHelper.include_if_ee('EE::GitlabRoutingHelper') +GitlabRoutingHelper.include_mod_with('GitlabRoutingHelper') diff --git a/app/helpers/graph_helper.rb b/app/helpers/graph_helper.rb index bcbc67957eb..3a94f7d47c2 100644 --- a/app/helpers/graph_helper.rb +++ b/app/helpers/graph_helper.rb @@ -28,4 +28,4 @@ module GraphHelper end end -GraphHelper.prepend_if_ee('EE::GraphHelper') +GraphHelper.prepend_mod_with('GraphHelper') diff --git a/app/helpers/groups/group_members_helper.rb b/app/helpers/groups/group_members_helper.rb index 3e7d6febabf..79191616c8f 100644 --- a/app/helpers/groups/group_members_helper.rb +++ b/app/helpers/groups/group_members_helper.rb @@ -13,31 +13,45 @@ module Groups::GroupMembersHelper render 'shared/members/invite_member', submit_url: group_group_members_path(group), access_levels: group.access_level_roles, default_access_level: default_access_level end - def group_group_links_data_json(group_links) - GroupLink::GroupGroupLinkSerializer.new.represent(group_links, { current_user: current_user }).to_json + def group_members_list_data_json(group, members, pagination = {}) + group_members_list_data(group, members, pagination).to_json end - def members_data_json(group, members) - MemberSerializer.new.represent(members, { current_user: current_user, group: group, source: group }).to_json + def group_group_links_list_data_json(group) + group_group_links_list_data(group).to_json + end + + private + + def group_members_serialized(group, members) + MemberSerializer.new.represent(members, { current_user: current_user, group: group, source: group }) + end + + def group_group_links_serialized(group_links) + GroupLink::GroupGroupLinkSerializer.new.represent(group_links, { current_user: current_user }) end # Overridden in `ee/app/helpers/ee/groups/group_members_helper.rb` - def group_members_list_data_attributes(group, members) + def group_members_list_data(group, members, pagination) { - members: members_data_json(group, members), + members: group_members_serialized(group, members), + pagination: members_pagination_data(members, pagination), member_path: group_group_member_path(group, ':id'), source_id: group.id, - can_manage_members: can?(current_user, :admin_group_member, group).to_s + can_manage_members: can?(current_user, :admin_group_member, group) } end - def group_group_links_list_data_attributes(group) + def group_group_links_list_data(group) + group_links = group.shared_with_group_links + { - members: group_group_links_data_json(group.shared_with_group_links), + members: group_group_links_serialized(group_links), + pagination: members_pagination_data(group_links), member_path: group_group_link_path(group, ':id'), source_id: group.id } end end -Groups::GroupMembersHelper.prepend_if_ee('EE::Groups::GroupMembersHelper') +Groups::GroupMembersHelper.prepend_mod_with('Groups::GroupMembersHelper') diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb index 5ce23baa226..8f647a49a64 100644 --- a/app/helpers/groups_helper.rb +++ b/app/helpers/groups_helper.rb @@ -7,7 +7,11 @@ module GroupsHelper groups#details groups#activity groups#subgroups - ] + ].tap do |paths| + break paths if Feature.disabled?(:sidebar_refactor, current_user, default_enabled: :yaml) + + paths.concat(['labels#index', 'group_members#index']) + end end def group_settings_nav_link_paths @@ -25,7 +29,9 @@ module GroupsHelper applications#index applications#show applications#edit - packages_and_registries#index + packages_and_registries#show + groups/runners#show + groups/runners#edit ] end @@ -36,6 +42,14 @@ module GroupsHelper ] end + def group_information_title(group) + if Feature.enabled?(:sidebar_refactor, current_user) + group.subgroup? ? _('Subgroup information') : _('Group information') + else + group.subgroup? ? _('Subgroup overview') : _('Group overview') + end + end + def group_container_registry_nav? Gitlab.config.registry.enabled && can?(current_user, :read_container_image, @group) @@ -113,9 +127,7 @@ module GroupsHelper @has_group_title = true full_title = [] - ancestors = group.ancestors.with_route - - ancestors.reverse_each.with_index do |parent, index| + sorted_ancestors(group).with_route.reverse_each.with_index do |parent, index| if index > 0 add_to_breadcrumb_dropdown(group_title_link(parent, hidable: false, show_avatar: true, for_dropdown: true), location: :before) else @@ -141,9 +153,9 @@ module GroupsHelper def projects_lfs_status(group) lfs_status = if group.lfs_enabled? - group.projects.select(&:lfs_enabled?).size + group.projects.count(&:lfs_enabled?) else - group.projects.reject(&:lfs_enabled?).size + group.projects.count { |project| !project.lfs_enabled? } end size = group.projects.size @@ -206,10 +218,9 @@ module GroupsHelper end def show_invite_banner?(group) - Feature.enabled?(:invite_your_teammates_banner_a, group) && - can?(current_user, :admin_group, group) && - !just_created? && - !multiple_members?(group) + can?(current_user, :admin_group, group) && + !just_created? && + !multiple_members?(group) end def render_setting_to_allow_project_access_token_creation?(group) @@ -231,7 +242,7 @@ module GroupsHelper end def multiple_members?(group) - group.member_count > 1 + group.member_count > 1 || group.members_with_parents.count > 1 end def get_group_sidebar_links @@ -285,11 +296,20 @@ module GroupsHelper end def oldest_consecutively_locked_ancestor(group) - group.ancestors.find do |group| + sorted_ancestors(group).find do |group| !group.has_parent? || !group.parent.share_with_group_lock? end end + # Ancestors sorted by hierarchy depth in bottom-top order. + def sorted_ancestors(group) + if group.root_ancestor.use_traversal_ids? + group.ancestors(hierarchy_order: :asc) + else + group.ancestors + end + end + def default_help s_("GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually.") end @@ -327,4 +347,4 @@ module GroupsHelper end end -GroupsHelper.prepend_if_ee('EE::GroupsHelper') +GroupsHelper.prepend_mod_with('GroupsHelper') diff --git a/app/helpers/hooks_helper.rb b/app/helpers/hooks_helper.rb index 9466a37ed93..2725d28c47c 100644 --- a/app/helpers/hooks_helper.rb +++ b/app/helpers/hooks_helper.rb @@ -38,4 +38,4 @@ module HooksHelper end end -HooksHelper.prepend_if_ee('EE::HooksHelper') +HooksHelper.prepend_mod_with('HooksHelper') diff --git a/app/helpers/ide_helper.rb b/app/helpers/ide_helper.rb index 61d8d0f779d..d1c84bd4141 100644 --- a/app/helpers/ide_helper.rb +++ b/app/helpers/ide_helper.rb @@ -17,7 +17,8 @@ module IdeHelper 'file-path' => @path, 'merge-request' => @merge_request, 'fork-info' => @fork_info&.to_json, - 'project' => convert_to_project_entity_json(@project) + 'project' => convert_to_project_entity_json(@project), + 'enable-environments-guidance' => enable_environments_guidance?.to_s } end @@ -28,6 +29,18 @@ module IdeHelper API::Entities::Project.represent(project).to_json end + + def enable_environments_guidance? + experiment(:in_product_guidance_environments_webide, project: @project) do |e| + e.try { !has_dismissed_ide_environments_callout? } + + e.run + end + end + + def has_dismissed_ide_environments_callout? + current_user.dismissed_callout?(feature_name: 'web_ide_ci_environments_guidance') + end end -::IdeHelper.prepend_if_ee('::EE::IdeHelper') +::IdeHelper.prepend_mod_with('IdeHelper') diff --git a/app/helpers/in_product_marketing_helper.rb b/app/helpers/in_product_marketing_helper.rb index 9e59a04d709..09546f251f9 100644 --- a/app/helpers/in_product_marketing_helper.rb +++ b/app/helpers/in_product_marketing_helper.rb @@ -1,381 +1,12 @@ # frozen_string_literal: true module InProductMarketingHelper - def subject_line(track, series) - { - create: [ - s_('InProductMarketing|Create a project in GitLab in 5 minutes'), - s_('InProductMarketing|Import your project and code from GitHub, Bitbucket and others'), - s_('InProductMarketing|Understand repository mirroring') - ], - verify: [ - s_('InProductMarketing|Feel the need for speed?'), - s_('InProductMarketing|3 ways to dive into GitLab CI/CD'), - s_('InProductMarketing|Explore the power of GitLab CI/CD') - ], - trial: [ - s_('InProductMarketing|Go farther with GitLab'), - s_('InProductMarketing|Automated security scans directly within GitLab'), - s_('InProductMarketing|Take your source code management to the next level') - ], - team: [ - s_('InProductMarketing|Working in GitLab = more efficient'), - s_("InProductMarketing|Multiple owners, confusing workstreams? We've got you covered"), - s_('InProductMarketing|Your teams can be more efficient') - ] - }[track][series] - end - - def in_product_marketing_logo(track, series) - inline_image_link('mailers/in_product_marketing', "#{track}-#{series}.png", { width: '150', style: 'width: 150px;' }) - end - - def about_link(folder, image, width) - link_to inline_image_link(folder, image, { width: width, style: "width: #{width}px;", alt: s_('InProductMarketing|go to about.gitlab.com') }), 'https://about.gitlab.com/' - end - - def in_product_marketing_tagline(track, series) - { - create: [ - s_('InProductMarketing|Get started today'), - s_('InProductMarketing|Get our import guides'), - s_('InProductMarketing|Need an alternative to importing?') - ], - verify: [ - s_('InProductMarketing|Use GitLab CI/CD'), - s_('InProductMarketing|Test, create, deploy'), - s_('InProductMarketing|Are your runners ready?') - ], - trial: [ - s_('InProductMarketing|Start a free trial of GitLab Ultimate – no CC required'), - s_('InProductMarketing|Improve app security with a 30-day trial'), - s_('InProductMarketing|Start with a GitLab Ultimate free trial') - ], - team: [ - s_('InProductMarketing|Invite your colleagues to join in less than one minute'), - s_('InProductMarketing|Get your team set up on GitLab'), - nil - ] - }[track][series] - end - - def in_product_marketing_title(track, series) - { - create: [ - s_('InProductMarketing|Take your first steps with GitLab'), - s_('InProductMarketing|Start by importing your projects'), - s_('InProductMarketing|How (and why) mirroring makes sense') - ], - verify: [ - s_('InProductMarketing|Rapid development, simplified'), - s_('InProductMarketing|Get started with GitLab CI/CD'), - s_('InProductMarketing|Launch GitLab CI/CD in 20 minutes or less') - ], - trial: [ - s_('InProductMarketing|Give us one minute...'), - s_("InProductMarketing|Security that's integrated into your development lifecycle"), - s_('InProductMarketing|Improve code quality and streamline reviews') - ], - team: [ - s_('InProductMarketing|Team work makes the dream work'), - s_('InProductMarketing|*GitLab*, noun: a synonym for efficient teams'), - s_('InProductMarketing|Find out how your teams are really doing') - ] - }[track][series] - end - - def in_product_marketing_subtitle(track, series) - { - create: [ - s_('InProductMarketing|Dig in and create a project and a repo'), - s_("InProductMarketing|Here's what you need to know"), - s_('InProductMarketing|Try it out') - ], - verify: [ - s_('InProductMarketing|How to build and test faster'), - s_('InProductMarketing|Explore the options'), - s_('InProductMarketing|Follow our steps') - ], - trial: [ - s_('InProductMarketing|...and you can get a free trial of GitLab Ultimate'), - s_('InProductMarketing|Try GitLab Ultimate for free'), - s_('InProductMarketing|Better code in less time') - ], - team: [ - s_('InProductMarketing|Actually, GitLab makes the team work (better)'), - s_('InProductMarketing|Our tool brings all the things together'), - s_("InProductMarketing|It's all in the stats") - ] - }[track][series] - end - - def in_product_marketing_body_line1(track, series, format: nil) - { - create: [ - s_("InProductMarketing|To understand and get the most out of GitLab, start at the beginning and %{project_link}. In GitLab, repositories are part of a project, so after you've created your project you can go ahead and %{repo_link}.") % { project_link: project_link(format), repo_link: repo_link(format) }, - s_("InProductMarketing|Making the switch? It's easier than you think to import your projects into GitLab. Move %{github_link}, or import something %{bitbucket_link}.") % { github_link: github_link(format), bitbucket_link: bitbucket_link(format) }, - s_("InProductMarketing|Sometimes you're not ready to make a full transition to a new tool. If you're not ready to fully commit, %{mirroring_link} gives you a safe way to try out GitLab in parallel with your current tool.") % { mirroring_link: mirroring_link(format) } - ], - verify: [ - s_("InProductMarketing|Tired of wrestling with disparate tool chains, information silos and inefficient processes? GitLab's CI/CD is built on a DevOps platform with source code management, planning, monitoring and more ready to go. Find out %{ci_link}.") % { ci_link: ci_link(format) }, - s_("InProductMarketing|GitLab's CI/CD makes software development easier. Don't believe us? Here are three ways you can take it for a fast (and satisfying) test drive:"), - s_("InProductMarketing|Get going with CI/CD quickly using our %{quick_start_link}. Start with an available runner and then create a CI .yml file – it's really that easy.") % { quick_start_link: quick_start_link(format) } - ], - trial: [ - [ - s_("InProductMarketing|GitLab's premium tiers are designed to make you, your team and your application more efficient and more secure with features including but not limited to:"), - list([ - s_('InProductMarketing|%{strong_start}Company wide portfolio management%{strong_end} — including multi-level epics, scoped labels').html_safe % strong_options(format), - s_('InProductMarketing|%{strong_start}Multiple approval roles%{strong_end} — including code owners and required merge approvals').html_safe % strong_options(format), - s_('InProductMarketing|%{strong_start}Advanced application security%{strong_end} — including SAST, DAST scanning, FUZZ testing, dependency scanning, license compliance, secrete detection').html_safe % strong_options(format), - s_('InProductMarketing|%{strong_start}Executive level insights%{strong_end} — including reporting on productivity, tasks by type, days to completion, value stream').html_safe % strong_options(format) - ], format) - ].join("\n"), - s_('InProductMarketing|GitLab provides static application security testing (SAST), dynamic application security testing (DAST), container scanning, and dependency scanning to help you deliver secure applications along with license compliance.'), - s_('InProductMarketing|By enabling code owners and required merge approvals the right person will review the right MR. This is a win-win: cleaner code and a more efficient review process.') - ], - team: [ - [ - s_('InProductMarketing|Did you know teams that use GitLab are far more efficient?'), - list([ - s_('InProductMarketing|Goldman Sachs went from 1 build every two weeks to thousands of builds a day'), - s_('InProductMarketing|Ticketmaster decreased their CI build time by 15X') - ], format) - ].join("\n"), - s_("InProductMarketing|We know a thing or two about efficiency and we don't want to keep that to ourselves. Sign up for a free trial of GitLab Ultimate and your teams will be on it from day one."), - [ - s_('InProductMarketing|Stop wondering and use GitLab to answer questions like:'), - list([ - s_('InProductMarketing|How long does it take us to close issues/MRs by types like feature requests, bugs, tech debt, security?'), - s_('InProductMarketing|How many days does it take our team to complete various tasks?'), - s_('InProductMarketing|What does our value stream timeline look like from product to development to review and production?') - ], format) - ].join("\n") - ] - }[track][series] - end - - def in_product_marketing_body_line2(track, series, format: nil) - { - create: [ - s_("InProductMarketing|That's all it takes to get going with GitLab, but if you're new to working with Git, check out our %{basics_link} for helpful tips and tricks for getting started.") % { basics_link: basics_link(format) }, - s_("InProductMarketing|Have a different instance you'd like to import? Here's our %{import_link}.") % { import_link: import_link(format) }, - s_("InProductMarketing|It's also possible to simply %{external_repo_link} in order to take advantage of GitLab's CI/CD.") % { external_repo_link: external_repo_link(format) } - ], - verify: [ - nil, - list([ - s_('InProductMarketing|Start by %{performance_link}').html_safe % { performance_link: performance_link(format) }, - s_('InProductMarketing|Move on to easily creating a Pages website %{ci_template_link}').html_safe % { ci_template_link: ci_template_link(format) }, - s_('InProductMarketing|And finally %{deploy_link} a Python application.').html_safe % { deploy_link: deploy_link(format) } - ], format), - nil - ], - trial: [ - s_('InProductMarketing|Start a GitLab Ultimate trial today in less than one minute, no credit card required.'), - s_('InProductMarketing|Get started today with a 30-day GitLab Ultimate trial, no credit card required.'), - s_('InProductMarketing|Code owners and required merge approvals are part of the paid tiers of GitLab. You can start a free 30-day trial of GitLab Ultimate and enable these features in less than 5 minutes with no credit card required.') - ], - team: [ - s_('InProductMarketing|Invite your colleagues and start shipping code faster.'), - s_("InProductMarketing|Streamline code review, know at a glance who's unavailable, communicate in comments or in email and integrate with Slack so everyone's on the same page."), - s_('InProductMarketing|When your team is on GitLab these answers are a click away.') - ] - }[track][series] - end - - def cta_link(track, series, group, format: nil) - case format - when :html - link_to in_product_marketing_cta_text(track, series), group_email_campaigns_url(group, track: track, series: series), target: '_blank', rel: 'noopener noreferrer' - else - [in_product_marketing_cta_text(track, series), group_email_campaigns_url(group, track: track, series: series)].join(' >> ') - end - end - - def in_product_marketing_progress(track, series, format: nil) - if Gitlab.com? - s_('InProductMarketing|This is email %{series} of 3 in the %{track} series.') % { series: series + 1, track: track.to_s.humanize } - else - s_('InProductMarketing|This is email %{series} of 3 in the %{track} series. To disable notification emails sent by your local GitLab instance, either contact your administrator or %{unsubscribe_link}.') % { series: series + 1, track: track.to_s.humanize, unsubscribe_link: unsubscribe_link(format) } - end - end - - def footer_links(format: nil) - links = [ - [s_('InProductMarketing|Blog'), 'https://about.gitlab.com/blog'], - [s_('InProductMarketing|Twitter'), 'https://twitter.com/gitlab'], - [s_('InProductMarketing|Facebook'), 'https://www.facebook.com/gitlab'], - [s_('InProductMarketing|YouTube'), 'https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg'] - ] - case format - when :html - links.map do |text, link| - link_to(text, link) - end - else - '| ' + links.map do |text, link| - [text, link].join(' ') - end.join("\n| ") - end - end - - def address(format: nil) - s_('InProductMarketing|%{strong_start}GitLab Inc.%{strong_end} 268 Bush Street, #350, San Francisco, CA 94104, USA').html_safe % strong_options(format) - end - - def unsubscribe(track, series, format: nil) - parts = Gitlab.com? ? unsubscribe_com(format) : unsubscribe_self_managed(track, series, format) - - case format - when :html - parts.join(' ') - else - parts.join("\n" + ' ' * 16) - end - end - - private - - def unsubscribe_com(format) - [ - s_('InProductMarketing|If you no longer wish to receive marketing emails from us,'), - s_('InProductMarketing|you may %{unsubscribe_link} at any time.') % { unsubscribe_link: unsubscribe_link(format) } - ] - end - - def unsubscribe_self_managed(track, series, format) - [ - s_('InProductMarketing|To opt out of these onboarding emails, %{unsubscribe_link}.') % { unsubscribe_link: unsubscribe_link(format) }, - s_("InProductMarketing|If you don't want to receive marketing emails directly from GitLab, %{marketing_preference_link}.") % { marketing_preference_link: marketing_preference_link(track, series, format) } - ] - end - - def in_product_marketing_cta_text(track, series) - { - create: [ - s_('InProductMarketing|Create your first project!'), - s_('InProductMarketing|Master the art of importing!'), - s_('InProductMarketing|Understand your project options') - ], - verify: [ - s_('InProductMarketing|Get to know GitLab CI/CD'), - s_('InProductMarketing|Try it yourself'), - s_('InProductMarketing|Explore GitLab CI/CD') - ], - trial: [ - s_('InProductMarketing|Start a trial'), - s_('InProductMarketing|Beef up your security'), - s_('InProductMarketing|Start your trial now!') - ], - team: [ - s_('InProductMarketing|Invite your colleagues today'), - s_('InProductMarketing|Invite your team in less than 60 seconds'), - s_('InProductMarketing|Invite your team now') - ] - }[track][series] - end - - def project_link(format) - link(s_('InProductMarketing|create a project'), help_page_url('gitlab-basics/create-project'), format) - end - - def repo_link(format) - link(s_('InProductMarketing|set up a repo'), help_page_url('user/project/repository/index', anchor: 'create-a-repository'), format) - end - - def github_link(format) - link(s_('InProductMarketing|GitHub Enterprise projects to GitLab'), help_page_url('integration/github'), format) - end - - def bitbucket_link(format) - link(s_('InProductMarketing|from Bitbucket'), help_page_url('user/project/import/bitbucket_server'), format) - end - - def mirroring_link(format) - link(s_('InProductMarketing|repository mirroring'), help_page_url('user/project/repository/repository_mirroring'), format) - end - - def ci_link(format) - link(s_('InProductMarketing|how easy it is to get started'), help_page_url('ci/README'), format) - end - - def performance_link(format) - link(s_('InProductMarketing|testing browser performance'), help_page_url('user/project/merge_requests/browser_performance_testing'), format) - end - - def ci_template_link(format) - link(s_('InProductMarketing|using a CI/CD template'), help_page_url('user/project/pages/getting_started/pages_ci_cd_template'), format) - end - - def deploy_link(format) - link(s_('InProductMarketing|test and deploy'), help_page_url('ci/examples/test-and-deploy-python-application-to-heroku'), format) - end - - def quick_start_link(format) - link(s_('InProductMarketing|quick start guide'), help_page_url('ci/quick_start/README'), format) - end - - def basics_link(format) - link(s_('InProductMarketing|Git basics'), help_page_url('gitlab-basics/README'), format) - end - - def import_link(format) - link(s_('InProductMarketing|comprehensive guide'), help_page_url('user/project/import/index'), format) - end - - def external_repo_link(format) - link(s_('InProductMarketing|connect an external repository'), new_project_url(anchor: 'cicd_for_external_repo'), format) - end - - def unsubscribe_link(format) - unsubscribe_url = Gitlab.com? ? '%tag_unsubscribe_url%' : profile_notifications_url - - link(s_('InProductMarketing|unsubscribe'), unsubscribe_url, format) - end - - def marketing_preference_link(track, series, format) - params = { - utm_source: 'SM', - utm_medium: 'email', - utm_campaign: 'onboarding', - utm_term: "#{track}_#{series}" - } - - preference_link = "https://about.gitlab.com/company/preference-center/?#{params.to_query}" - - link(s_('InProductMarketing|update your preferences'), preference_link, format) - end - - def link(text, link, format) - case format - when :html - link_to text, link - else - "#{text} (#{link})" - end - end - - def list(array, format) - case format - when :html - tag.ul { array.map { |item| concat tag.li item} } - else - '- ' + array.join("\n- ") - end - end - - def strong_options(format) - case format - when :html - { strong_start: '<b>'.html_safe, strong_end: '</b>'.html_safe } - else - { strong_start: '', strong_end: '' } - end + def inline_image_link(image, options) + attachments.inline[image] = File.read(Rails.root.join("app/assets/images", image)) + image_tag attachments[image].url, **options end - def inline_image_link(folder, image, options) - attachments.inline[image] = File.read(Rails.root.join("app/assets/images", folder, image)) - image_tag attachments[image].url, **options + def about_link(image, width) + link_to inline_image_link(image, { width: width, style: "width: #{width}px;", alt: s_('InProductMarketing|go to about.gitlab.com') }), 'https://about.gitlab.com/' end end diff --git a/app/helpers/invite_members_helper.rb b/app/helpers/invite_members_helper.rb index 62d83ebe79e..889c058cb21 100644 --- a/app/helpers/invite_members_helper.rb +++ b/app/helpers/invite_members_helper.rb @@ -3,12 +3,12 @@ module InviteMembersHelper include Gitlab::Utils::StrongMemoize - def can_invite_members_for_group?(group) - Feature.enabled?(:invite_members_group_modal, group) && can?(current_user, :admin_group_member, group) + def can_invite_members_for_project?(project) + Feature.enabled?(:invite_members_group_modal, project.group) && can_manage_project_members?(project) end - def can_invite_members_for_project?(project) - Feature.enabled?(:invite_members_group_modal, project.group) && can_import_members? + def can_invite_group_for_project?(project) + Feature.enabled?(:invite_members_group_modal, project.group) && project.allowed_to_share_with_group? end def directly_invite_members? @@ -17,20 +17,6 @@ module InviteMembersHelper end end - def indirectly_invite_members? - strong_memoize(:indirectly_invite_members) do - experiment_enabled?(:invite_members_version_b) && !can_import_members? - end - end - - def show_invite_members_track_event - if directly_invite_members? - 'show_invite_members' - elsif indirectly_invite_members? - 'show_invite_members_version_b' - end - end - def invite_group_members?(group) experiment_enabled?(:invite_members_empty_group_version_a) && Ability.allowed?(current_user, :admin_group_member, group) end @@ -46,6 +32,17 @@ module InviteMembersHelper end end + def invite_accepted_notice(member) + case member.source + when Project + _("You have been granted %{member_human_access} access to project %{name}.") % + { member_human_access: member.human_access, name: member.source.name } + when Group + _("You have been granted %{member_human_access} access to group %{name}.") % + { member_human_access: member.human_access, name: member.source.name } + end + end + private def invite_members_url(form_model) diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index 8ebc773bb25..c662dabe453 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -199,7 +199,7 @@ module IssuablesHelper count = issuables_count_for_state(issuable_type, state) if count != -1 - html << " " << content_tag(:span, number_with_delimiter(count), class: 'badge badge-pill') + html << " " << content_tag(:span, number_with_delimiter(count), class: 'badge badge-muted badge-pill gl-badge gl-tab-counter-badge sm') end html.html_safe @@ -332,6 +332,18 @@ module IssuablesHelper end end + def state_name_with_icon(issuable) + if issuable.is_a?(MergeRequest) && issuable.merged? + [_("Merged"), "git-merge"] + elsif issuable.is_a?(MergeRequest) && issuable.closed? + [_("Closed"), "close"] + elsif issuable.closed? + [_("Closed"), "mobile-issue-close"] + else + [_("Open"), "issue-open-m"] + end + end + private def sidebar_gutter_collapsed? @@ -386,11 +398,11 @@ module IssuablesHelper rootPath: root_path, fullPath: issuable[:project_full_path], iid: issuable[:iid], + id: issuable[:id], severity: issuable[:severity], timeTrackingLimitToHours: Gitlab::CurrentSettings.time_tracking_limit_to_hours, createNoteEmail: issuable[:create_note_email], - issuableType: issuable[:type], - projectMembersPath: project_project_members_path(@project, sort: :access_level_desc) + issuableType: issuable[:type] } end @@ -414,4 +426,4 @@ module IssuablesHelper end end -IssuablesHelper.prepend_if_ee('EE::IssuablesHelper') +IssuablesHelper.prepend_mod_with('IssuablesHelper') diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 0a83e707412..1449725fb2b 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -9,6 +9,22 @@ module IssuesHelper classes.join(' ') end + def issue_manual_ordering_class + is_sorting_by_relative_position = @sort == 'relative_position' + + if is_sorting_by_relative_position && !issue_repositioning_disabled? + "manual-ordering" + end + end + + def issue_repositioning_disabled? + if @group + @group.root_ancestor.issue_repositioning_disabled? + elsif @project + @project.root_namespace.issue_repositioning_disabled? + end + end + def status_box_class(item) if item.try(:expired?) 'status-box-expired' @@ -165,23 +181,32 @@ module IssuesHelper def issues_list_data(project, current_user, finder) { + autocomplete_users_path: autocomplete_users_path(active: true, current_user: true, project_id: project.id, format: :json), + autocomplete_award_emojis_path: autocomplete_award_emojis_path, calendar_path: url_for(safe_params.merge(calendar_url_options)), can_bulk_update: can?(current_user, :admin_issue, project).to_s, can_edit: can?(current_user, :admin_project, project).to_s, can_import_issues: can?(current_user, :import_issues, @project).to_s, email: current_user&.notification_email, + emails_help_page_path: help_page_path('development/emails', anchor: 'email-namespace'), empty_state_svg_path: image_path('illustrations/issues.svg'), endpoint: expose_path(api_v4_projects_issues_path(id: project.id)), export_csv_path: export_csv_project_issues_path(project), - full_path: project.full_path, has_issues: project_issues(project).exists?.to_s, import_csv_issues_path: import_csv_namespace_project_issues_path, + initial_email: project.new_issuable_address(current_user, 'issue'), is_signed_in: current_user.present?.to_s, issues_path: project_issues_path(project), jira_integration_path: help_page_url('user/project/integrations/jira', anchor: 'view-jira-issues'), + markdown_help_path: help_page_path('user/markdown'), max_attachment_size: number_to_human_size(Gitlab::CurrentSettings.max_attachment_size.megabytes), new_issue_path: new_project_issue_path(project, issue: { assignee_id: finder.assignee.try(:id), milestone_id: finder.milestones.first.try(:id) }), project_import_jira_path: project_import_jira_path(project), + project_labels_path: project_labels_path(project, include_ancestor_groups: true, format: :json), + project_milestones_path: project_milestones_path(project, format: :json), + project_path: project.full_path, + quick_actions_help_path: help_page_path('user/project/quick_actions'), + reset_path: new_issuable_address_project_path(project, issuable_type: 'issue'), rss_path: url_for(safe_params.merge(rss_url_options)), show_new_issue_link: show_new_issue_link?(project).to_s, sign_in_path: new_user_session_path @@ -200,4 +225,4 @@ module IssuesHelper end end -IssuesHelper.prepend_if_ee('EE::IssuesHelper') +IssuesHelper.prepend_mod_with('IssuesHelper') diff --git a/app/helpers/kerberos_spnego_helper.rb b/app/helpers/kerberos_spnego_helper.rb index ed09ed755fe..0f6812bc31b 100644 --- a/app/helpers/kerberos_spnego_helper.rb +++ b/app/helpers/kerberos_spnego_helper.rb @@ -10,4 +10,4 @@ module KerberosSpnegoHelper end end -KerberosSpnegoHelper.prepend_if_ee('EE::KerberosSpnegoHelper') +KerberosSpnegoHelper.prepend_mod_with('KerberosSpnegoHelper') diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb index cfc4075100b..2150729cb2a 100644 --- a/app/helpers/labels_helper.rb +++ b/app/helpers/labels_helper.rb @@ -275,4 +275,4 @@ module LabelsHelper end end -LabelsHelper.prepend_if_ee('EE::LabelsHelper') +LabelsHelper.prepend_mod_with('LabelsHelper') diff --git a/app/helpers/learn_gitlab_helper.rb b/app/helpers/learn_gitlab_helper.rb index 81896fb9fa4..a3a8a275f67 100644 --- a/app/helpers/learn_gitlab_helper.rb +++ b/app/helpers/learn_gitlab_helper.rb @@ -3,11 +3,21 @@ module LearnGitlabHelper def learn_gitlab_experiment_enabled?(project) return false unless current_user - return false unless experiment_enabled_for_user? + return false unless continous_onboarding_experiment_enabled_for_user? learn_gitlab_onboarding_available?(project) end + def learn_gitlab_experiment_tracking_category + return unless current_user + + if Gitlab::Experimentation.in_experiment_group?(:learn_gitlab_a, subject: current_user) + Gitlab::Experimentation.get_experiment(:learn_gitlab_a).tracking_category + elsif Gitlab::Experimentation.in_experiment_group?(:learn_gitlab_b, subject: current_user) + Gitlab::Experimentation.get_experiment(:learn_gitlab_b).tracking_category + end + end + def onboarding_actions_data(project) attributes = onboarding_progress(project).attributes.symbolize_keys @@ -21,42 +31,42 @@ module LearnGitlabHelper end end - private + def continous_onboarding_experiment_enabled_for_user? + Gitlab::Experimentation.in_experiment_group?(:learn_gitlab_a, subject: current_user) || + Gitlab::Experimentation.in_experiment_group?(:learn_gitlab_b, subject: current_user) + end - ACTION_ISSUE_IDS = { - issue_created: 4, - git_write: 6, - pipeline_created: 7, - merge_request_created: 9, - user_added: 8, - trial_started: 2, - required_mr_approvals_enabled: 11, - code_owners_enabled: 10 - }.freeze - - ACTION_DOC_URLS = { - security_scan_enabled: 'https://docs.gitlab.com/ee/user/application_security/security_dashboard/#gitlab-security-dashboard-security-center-and-vulnerability-reports' - }.freeze + def onboarding_sections_data + { + workspace: { + svg: image_path("learn_gitlab/section_workspace.svg") + }, + plan: { + svg: image_path("learn_gitlab/section_plan.svg") + }, + deploy: { + svg: image_path("learn_gitlab/section_deploy.svg") + } + } + end + + def learn_gitlab_onboarding_available?(project) + OnboardingProgress.onboarding?(project.namespace) && + LearnGitlab::Project.new(current_user).available? + end + + private def action_urls - ACTION_ISSUE_IDS.transform_values { |id| project_issue_url(learn_gitlab_project, id) }.merge(ACTION_DOC_URLS) + LearnGitlab::Onboarding::ACTION_ISSUE_IDS.transform_values { |id| project_issue_url(learn_gitlab_project, id) } + .merge(LearnGitlab::Onboarding::ACTION_DOC_URLS) end def learn_gitlab_project - @learn_gitlab_project ||= LearnGitlab.new(current_user).project + @learn_gitlab_project ||= LearnGitlab::Project.new(current_user).project end def onboarding_progress(project) OnboardingProgress.find_by(namespace: project.namespace) # rubocop: disable CodeReuse/ActiveRecord end - - def experiment_enabled_for_user? - Gitlab::Experimentation.in_experiment_group?(:learn_gitlab_a, subject: current_user) || - Gitlab::Experimentation.in_experiment_group?(:learn_gitlab_b, subject: current_user) - end - - def learn_gitlab_onboarding_available?(project) - OnboardingProgress.onboarding?(project.namespace) && - LearnGitlab.new(current_user).available? - end end diff --git a/app/helpers/markup_helper.rb b/app/helpers/markup_helper.rb index ad206d0e5b5..05a55a09271 100644 --- a/app/helpers/markup_helper.rb +++ b/app/helpers/markup_helper.rb @@ -318,4 +318,4 @@ module MarkupHelper extend self end -MarkupHelper.prepend_if_ee('EE::MarkupHelper') +MarkupHelper.prepend_mod_with('MarkupHelper') diff --git a/app/helpers/members_helper.rb b/app/helpers/members_helper.rb index 5dc636ad996..d3db5d24207 100644 --- a/app/helpers/members_helper.rb +++ b/app/helpers/members_helper.rb @@ -65,4 +65,14 @@ module MembersHelper 'group and any subresources' end + + def members_pagination_data(members, pagination = {}) + { + current_page: members.respond_to?(:current_page) ? members.current_page : nil, + per_page: members.respond_to?(:limit_value) ? members.limit_value : nil, + total_items: members.respond_to?(:total_count) ? members.total_count : members.count, + param_name: pagination[:param_name] || nil, + params: pagination[:params] || {} + } + end end diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb index df7fcb0f3da..514f5fafd65 100644 --- a/app/helpers/merge_requests_helper.rb +++ b/app/helpers/merge_requests_helper.rb @@ -29,16 +29,6 @@ module MergeRequestsHelper classes.join(' ') end - def state_name_with_icon(merge_request) - if merge_request.merged? - [_("Merged"), "git-merge"] - elsif merge_request.closed? - [_("Closed"), "close"] - else - [_("Open"), "issue-open-m"] - end - end - def merge_path_description(merge_request, separator) if merge_request.for_fork? "Project:Branches: #{@merge_request.source_project_path}:#{@merge_request.source_branch} #{separator} #{@merge_request.target_project.full_path}:#{@merge_request.target_branch}" @@ -223,4 +213,4 @@ module MergeRequestsHelper end end -MergeRequestsHelper.prepend_if_ee('EE::MergeRequestsHelper') +MergeRequestsHelper.prepend_mod_with('MergeRequestsHelper') diff --git a/app/helpers/mirror_helper.rb b/app/helpers/mirror_helper.rb index 9d23ab87b98..3dfd30f07db 100644 --- a/app/helpers/mirror_helper.rb +++ b/app/helpers/mirror_helper.rb @@ -17,4 +17,4 @@ module MirrorHelper end end -MirrorHelper.prepend_if_ee('EE::MirrorHelper') +MirrorHelper.prepend_mod_with('MirrorHelper') diff --git a/app/helpers/namespace_storage_limit_alert_helper.rb b/app/helpers/namespace_storage_limit_alert_helper.rb index d7174c38254..ed11f89a7dd 100644 --- a/app/helpers/namespace_storage_limit_alert_helper.rb +++ b/app/helpers/namespace_storage_limit_alert_helper.rb @@ -6,4 +6,4 @@ module NamespaceStorageLimitAlertHelper end end -NamespaceStorageLimitAlertHelper.prepend_if_ee('EE::NamespaceStorageLimitAlertHelper') +NamespaceStorageLimitAlertHelper.prepend_mod_with('NamespaceStorageLimitAlertHelper') diff --git a/app/helpers/namespaces_helper.rb b/app/helpers/namespaces_helper.rb index a4521541bf9..39a8f506ba2 100644 --- a/app/helpers/namespaces_helper.rb +++ b/app/helpers/namespaces_helper.rb @@ -83,6 +83,15 @@ module NamespacesHelper } end + def cascading_namespace_setting_locked?(attribute, group, **args) + return false if group.nil? + + method_name = "#{attribute}_locked?" + return false unless group.namespace_settings.respond_to?(method_name) + + group.namespace_settings.public_send(method_name, **args) # rubocop:disable GitlabSecurity/PublicSend + end + private # Many importers create a temporary Group, so use the real @@ -116,4 +125,4 @@ module NamespacesHelper end end -NamespacesHelper.prepend_if_ee('EE::NamespacesHelper') +NamespacesHelper.prepend_mod_with('NamespacesHelper') diff --git a/app/helpers/nav/top_nav_helper.rb b/app/helpers/nav/top_nav_helper.rb new file mode 100644 index 00000000000..159b7ca87f9 --- /dev/null +++ b/app/helpers/nav/top_nav_helper.rb @@ -0,0 +1,243 @@ +# frozen_string_literal: true + +module Nav + module TopNavHelper + PROJECTS_VIEW = :projects + GROUPS_VIEW = :groups + + def top_nav_view_model(project:, group:) + builder = ::Gitlab::Nav::TopNavViewModelBuilder.new + + if current_user + build_view_model(builder: builder, project: project, group: group) + else + build_anonymous_view_model(builder: builder) + end + + builder.build + end + + private + + def build_anonymous_view_model(builder:) + # These come from `app/views/layouts/nav/_explore.html.ham` + if explore_nav_link?(:projects) + builder.add_primary_menu_item( + **projects_menu_item_attrs.merge( + { + active: active_nav_link?(path: %w[dashboard#show root#show projects#trending projects#starred projects#index]), + href: explore_root_path + }) + ) + end + + if explore_nav_link?(:groups) + builder.add_primary_menu_item( + **groups_menu_item_attrs.merge( + { + active: active_nav_link?(controller: [:groups, 'groups/milestones', 'groups/group_members']), + href: explore_groups_path + }) + ) + end + + if explore_nav_link?(:snippets) + builder.add_primary_menu_item( + **snippets_menu_item_attrs.merge( + { + active: active_nav_link?(controller: :snippets), + href: explore_snippets_path + }) + ) + end + end + + def build_view_model(builder:, project:, group:) + # These come from `app/views/layouts/nav/_dashboard.html.haml` + if dashboard_nav_link?(:projects) + current_item = project ? current_project(project: project) : {} + + builder.add_primary_menu_item( + **projects_menu_item_attrs.merge({ + active: active_nav_link?(path: %w[root#index projects#trending projects#starred dashboard/projects#index]), + css_class: 'qa-projects-dropdown', + data: { track_label: "projects_dropdown", track_event: "click_dropdown", track_experiment: "new_repo" }, + view: PROJECTS_VIEW + }) + ) + builder.add_view(PROJECTS_VIEW, container_view_props(namespace: 'projects', current_item: current_item, submenu: projects_submenu)) + end + + if dashboard_nav_link?(:groups) + current_item = group ? current_group(group: group) : {} + + builder.add_primary_menu_item( + **groups_menu_item_attrs.merge({ + active: active_nav_link?(path: %w[dashboard/groups explore/groups]), + css_class: 'qa-groups-dropdown', + data: { track_label: "groups_dropdown", track_event: "click_dropdown" }, + view: GROUPS_VIEW + }) + ) + builder.add_view(GROUPS_VIEW, container_view_props(namespace: 'groups', current_item: current_item, submenu: groups_submenu)) + end + + if dashboard_nav_link?(:milestones) + builder.add_primary_menu_item( + id: 'milestones', + title: 'Milestones', + active: active_nav_link?(controller: 'dashboard/milestones'), + icon: 'clock', + data: { qa_selector: 'milestones_link' }, + href: dashboard_milestones_path + ) + end + + if dashboard_nav_link?(:snippets) + builder.add_primary_menu_item( + **snippets_menu_item_attrs.merge({ + active: active_nav_link?(controller: 'dashboard/snippets'), + data: { qa_selector: 'snippets_link' }, + href: dashboard_snippets_path + }) + ) + end + + if dashboard_nav_link?(:activity) + builder.add_primary_menu_item( + id: 'activity', + title: 'Activity', + active: active_nav_link?(path: 'dashboard#activity'), + icon: 'history', + data: { qa_selector: 'activity_link' }, + href: activity_dashboard_path + ) + end + + # Using admin? is generally discouraged because it does not check for + # "admin_mode". In this case we are migrating code and check both, so + # we should be good. + # rubocop: disable Cop/UserAdmin + if current_user&.admin? + builder.add_secondary_menu_item( + id: 'admin', + title: _('Admin'), + active: active_nav_link?(controller: 'admin/dashboard'), + icon: 'admin', + css_class: 'qa-admin-area-link', + href: admin_root_path + ) + end + + if Gitlab::CurrentSettings.admin_mode + if header_link?(:admin_mode) + builder.add_secondary_menu_item( + id: 'leave_admin_mode', + title: _('Leave Admin Mode'), + active: active_nav_link?(controller: 'admin/sessions'), + icon: 'lock-open', + href: destroy_admin_session_path, + method: :post + ) + elsif current_user.admin? + builder.add_secondary_menu_item( + id: 'enter_admin_mode', + title: _('Enter Admin Mode'), + active: active_nav_link?(controller: 'admin/sessions'), + icon: 'lock', + href: new_admin_session_path + ) + end + end + # rubocop: enable Cop/UserAdmin + + if Gitlab::Sherlock.enabled? + builder.add_secondary_menu_item( + id: 'sherlock', + title: _('Sherlock Transactions'), + icon: 'admin', + href: sherlock_transactions_path + ) + end + end + + def projects_menu_item_attrs + { + id: 'project', + title: _('Projects'), + icon: 'project' + } + end + + def groups_menu_item_attrs + { + id: 'groups', + title: 'Groups', + icon: 'group' + } + end + + def snippets_menu_item_attrs + { + id: 'snippets', + title: _('Snippets'), + icon: 'snippet' + } + end + + def container_view_props(namespace:, current_item:, submenu:) + { + namespace: namespace, + currentUserName: current_user&.username, + currentItem: current_item, + linksPrimary: submenu[:primary], + linksSecondary: submenu[:secondary] + } + end + + def current_project(project:) + return {} unless project.persisted? + + { + id: project.id, + name: project.name, + namespace: project.full_name, + webUrl: project_path(project), + avatarUrl: project.avatar_url + } + end + + def current_group(group:) + return {} unless group.persisted? + + { + id: group.id, + name: group.name, + namespace: group.full_name, + webUrl: group_path(group), + avatarUrl: group.avatar_url + } + end + + def projects_submenu + # These project links come from `app/views/layouts/nav/projects_dropdown/_show.html.haml` + builder = ::Gitlab::Nav::TopNavMenuBuilder.new + builder.add_primary_menu_item(id: 'your', title: _('Your projects'), href: dashboard_projects_path) + builder.add_primary_menu_item(id: 'starred', title: _('Starred projects'), href: starred_dashboard_projects_path) + builder.add_primary_menu_item(id: 'explore', title: _('Explore projects'), href: explore_root_path) + builder.add_secondary_menu_item(id: 'create', title: _('Create new project'), href: new_project_path) + builder.build + end + + def groups_submenu + # These group links come from `app/views/layouts/nav/groups_dropdown/_show.html.haml` + builder = ::Gitlab::Nav::TopNavMenuBuilder.new + builder.add_primary_menu_item(id: 'your', title: _('Your groups'), href: dashboard_groups_path) + builder.add_primary_menu_item(id: 'explore', title: _('Explore groups'), href: explore_groups_path) + builder.add_secondary_menu_item(id: 'create', title: _('Create group'), href: new_group_path(anchor: 'create-group-pane')) + builder.build + end + end +end + +Nav::TopNavHelper.prepend_mod diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb index db144f63f92..aab1a44bdfb 100644 --- a/app/helpers/nav_helper.rb +++ b/app/helpers/nav_helper.rb @@ -12,6 +12,7 @@ module NavHelper def page_with_sidebar_class class_name = page_gutter_class class_name << 'page-with-contextual-sidebar' if defined?(@left_sidebar) && @left_sidebar + class_name << 'sidebar-refactoring' if Feature.enabled?(:sidebar_refactor, current_user) class_name << 'page-with-icon-sidebar' if collapsed_sidebar? && @left_sidebar class_name -= ['right-sidebar-expanded'] if defined?(@right_sidebar) && !@right_sidebar @@ -68,7 +69,14 @@ module NavHelper end def group_issues_sub_menu_items - %w(groups#issues labels#index milestones#index boards#index boards#show) + %w[ + groups#issues + milestones#index + boards#index + boards#show + ].tap do |paths| + paths << 'labels#index' if Feature.disabled?(:sidebar_refactor, current_user, default_enabled: :yaml) + end end private @@ -100,4 +108,4 @@ module NavHelper end end -NavHelper.prepend_if_ee('EE::NavHelper') +NavHelper.prepend_mod_with('NavHelper') diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb index 62580124c0f..fff7e5d1c7f 100644 --- a/app/helpers/notes_helper.rb +++ b/app/helpers/notes_helper.rb @@ -215,4 +215,4 @@ module NotesHelper end end -NotesHelper.prepend_if_ee('EE::NotesHelper') +NotesHelper.prepend_mod_with('NotesHelper') diff --git a/app/helpers/notify_helper.rb b/app/helpers/notify_helper.rb index 03da679cfdd..38c98776fdf 100644 --- a/app/helpers/notify_helper.rb +++ b/app/helpers/notify_helper.rb @@ -18,7 +18,7 @@ module NotifyHelper when "Developer" s_("InviteEmail|As a developer, you have full access to projects, so you can take an idea from concept to production.") when "Maintainer" - s_("InviteEmail|As a maintainer, you have full access to projects. You can push commits to master and deploy to production.") + s_("InviteEmail|As a maintainer, you have full access to projects. You can push commits to the default branch and deploy to production.") when "Owner" s_("InviteEmail|As an owner, you have full access to projects and can manage access to the group, including inviting new members.") when "Minimal Access" diff --git a/app/helpers/operations_helper.rb b/app/helpers/operations_helper.rb index 51f4304911b..df07baa2d03 100644 --- a/app/helpers/operations_helper.rb +++ b/app/helpers/operations_helper.rb @@ -44,4 +44,4 @@ module OperationsHelper end end -OperationsHelper.prepend_if_ee('EE::OperationsHelper') +OperationsHelper.prepend_mod_with('OperationsHelper') diff --git a/app/helpers/page_layout_helper.rb b/app/helpers/page_layout_helper.rb index 6997c8cffda..2729951d685 100644 --- a/app/helpers/page_layout_helper.rb +++ b/app/helpers/page_layout_helper.rb @@ -162,7 +162,6 @@ module PageLayoutHelper default_properties = { current_emoji: '', current_message: '', - can_set_user_availability: Feature.enabled?(:set_user_availability_status, user, default_enabled: :yaml), default_emoji: UserStatus::DEFAULT_EMOJI } diff --git a/app/helpers/preferences_helper.rb b/app/helpers/preferences_helper.rb index add6e1eaf6f..d851ed3db8f 100644 --- a/app/helpers/preferences_helper.rb +++ b/app/helpers/preferences_helper.rb @@ -4,8 +4,8 @@ module PreferencesHelper def layout_choices [ - ['Fixed', :fixed], - ['Fluid', :fluid] + ['Fixed', :fixed], + ['Fluid', :fluid] ] end @@ -76,7 +76,7 @@ module PreferencesHelper def language_choices options_for_select( - Gitlab::I18n.selectable_locales.map(&:reverse).sort, + selectable_locales_with_translation_level.sort, current_user.preferred_language ) end @@ -107,6 +107,18 @@ module PreferencesHelper def default_first_day_of_week first_day_of_week_choices.rassoc(Gitlab::CurrentSettings.first_day_of_week).first end + + def selectable_locales_with_translation_level + Gitlab::I18n.selectable_locales.map do |code, language| + [ + s_("i18n|%{language} (%{percent_translated}%% translated)") % { + language: language, + percent_translated: Gitlab::I18n.percentage_translated_for(code) + }, + code + ] + end + end end -PreferencesHelper.prepend_if_ee('EE::PreferencesHelper') +PreferencesHelper.prepend_mod_with('PreferencesHelper') diff --git a/app/helpers/profiles_helper.rb b/app/helpers/profiles_helper.rb index 3219620de71..f6ed567c9ea 100644 --- a/app/helpers/profiles_helper.rb +++ b/app/helpers/profiles_helper.rb @@ -51,4 +51,4 @@ module ProfilesHelper end end -ProfilesHelper.prepend_ee_mod +ProfilesHelper.prepend_mod diff --git a/app/helpers/projects/alert_management_helper.rb b/app/helpers/projects/alert_management_helper.rb index b705258f133..b46e3eb3bc3 100644 --- a/app/helpers/projects/alert_management_helper.rb +++ b/app/helpers/projects/alert_management_helper.rb @@ -10,6 +10,7 @@ module Projects::AlertManagementHelper 'empty-alert-svg-path' => image_path('illustrations/alert-management-empty-state.svg'), 'user-can-enable-alert-management' => can?(current_user, :admin_operations, project).to_s, 'alert-management-enabled' => alert_management_enabled?(project).to_s, + 'has-managed-prometheus' => has_managed_prometheus?(project).to_s, 'text-query': params[:search], 'assignee-username-query': params[:assignee_username] } @@ -27,6 +28,10 @@ module Projects::AlertManagementHelper private + def has_managed_prometheus?(project) + project.prometheus_service&.prometheus_available? == true + end + def alert_management_enabled?(project) !!( project.alert_management_alerts.any? || diff --git a/app/helpers/projects/incidents_helper.rb b/app/helpers/projects/incidents_helper.rb index 63504cb55b9..dde2980817f 100644 --- a/app/helpers/projects/incidents_helper.rb +++ b/app/helpers/projects/incidents_helper.rb @@ -16,4 +16,4 @@ module Projects::IncidentsHelper end end -Projects::IncidentsHelper.prepend_if_ee('EE::Projects::IncidentsHelper') +Projects::IncidentsHelper.prepend_mod_with('Projects::IncidentsHelper') diff --git a/app/helpers/projects/project_members_helper.rb b/app/helpers/projects/project_members_helper.rb index 662afbcfd25..fa68bbad135 100644 --- a/app/helpers/projects/project_members_helper.rb +++ b/app/helpers/projects/project_members_helper.rb @@ -27,29 +27,41 @@ module Projects::ProjectMembersHelper project.group.has_owner?(current_user) end - def project_group_links_data_json(group_links) - GroupLink::ProjectGroupLinkSerializer.new.represent(group_links, { current_user: current_user }).to_json + def project_members_list_data_json(project, members, pagination = {}) + project_members_list_data(project, members, pagination).to_json end - def project_members_data_json(project, members) - MemberSerializer.new.represent(members, { current_user: current_user, group: project.group, source: project }).to_json + def project_group_links_list_data_json(project, group_links) + project_group_links_list_data(project, group_links).to_json end - def project_members_list_data_attributes(project, members) + private + + def project_members_serialized(project, members) + MemberSerializer.new.represent(members, { current_user: current_user, group: project.group, source: project }) + end + + def project_group_links_serialized(group_links) + GroupLink::ProjectGroupLinkSerializer.new.represent(group_links, { current_user: current_user }) + end + + def project_members_list_data(project, members, pagination) { - members: project_members_data_json(project, members), + members: project_members_serialized(project, members), + pagination: members_pagination_data(members, pagination), member_path: project_project_member_path(project, ':id'), source_id: project.id, - can_manage_members: can_manage_project_members?(project).to_s + can_manage_members: can_manage_project_members?(project) } end - def project_group_links_list_data_attributes(project, group_links) + def project_group_links_list_data(project, group_links) { - members: project_group_links_data_json(group_links), + members: project_group_links_serialized(group_links), + pagination: members_pagination_data(group_links), member_path: project_group_link_path(project, ':id'), source_id: project.id, - can_manage_members: can_manage_project_members?(project).to_s + can_manage_members: can_manage_project_members?(project) } end end diff --git a/app/helpers/projects/security/configuration_helper.rb b/app/helpers/projects/security/configuration_helper.rb index 265d46cbc41..dee106ab3ae 100644 --- a/app/helpers/projects/security/configuration_helper.rb +++ b/app/helpers/projects/security/configuration_helper.rb @@ -10,4 +10,4 @@ module Projects end end -::Projects::Security::ConfigurationHelper.prepend_if_ee('::EE::Projects::Security::ConfigurationHelper') +::Projects::Security::ConfigurationHelper.prepend_mod_with('Projects::Security::ConfigurationHelper') diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 4be6cd4276b..f2a50ce1325 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -125,34 +125,12 @@ module ProjectsHelper project.fork_source if project.fork_source && can?(current_user, :read_project, project.fork_source) end - def project_nav_tabs - @nav_tabs ||= get_project_nav_tabs(@project, current_user) - end - def project_search_tabs?(tab) abilities = Array(search_tab_ability_map[tab]) abilities.any? { |ability| can?(current_user, ability, @project) } end - def project_nav_tab?(name) - project_nav_tabs.include? name - end - - def any_project_nav_tab?(tabs) - tabs.any? { |tab| project_nav_tab?(tab) } - end - - def project_for_deploy_key(deploy_key) - if deploy_key.has_access_to?(@project) - @project - else - deploy_key.projects.find do |project| - can?(current_user, :read_project, project) - end - end - end - def can_change_visibility_level?(project, current_user) can?(current_user, :change_visibility_level, project) end @@ -285,10 +263,6 @@ module ProjectsHelper !disabled && !compact_mode end - def settings_operations_available? - !@project.archived? && can?(current_user, :admin_operations, @project) - end - def error_tracking_setting_project_json setting = @project.error_tracking_setting @@ -378,89 +352,6 @@ module ProjectsHelper private - def can_read_security_configuration?(project, current_user) - can?(current_user, :access_security_and_compliance, project) && - can?(current_user, :read_security_configuration, project) - end - - def get_project_security_nav_tabs(project, current_user) - if can_read_security_configuration?(project, current_user) - [:security_and_compliance, :security_configuration] - else - [] - end - end - - # rubocop:disable Metrics/CyclomaticComplexity - def get_project_nav_tabs(project, current_user) - nav_tabs = [:home] - - 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) - end - - nav_tabs += get_project_security_nav_tabs(project, current_user) - - if project.repo_exists? && can?(current_user, :read_merge_request, project) - nav_tabs << :merge_requests - end - - if Gitlab.config.registry.enabled && can?(current_user, :read_container_image, project) - nav_tabs << :container_registry - end - - if Feature.enabled?(:infrastructure_registry_page) - nav_tabs << :infrastructure_registry - end - - # Pipelines feature is tied to presence of builds - if can?(current_user, :read_build, project) - nav_tabs << :pipelines - end - - if can_view_operations_tab?(current_user, project) - nav_tabs << :operations - end - - if can_view_product_analytics?(current_user, project) - nav_tabs << :product_analytics - end - - tab_ability_map.each do |tab, ability| - if can?(current_user, ability, project) - nav_tabs << tab - end - end - - apply_external_nav_tabs(nav_tabs, project) - - nav_tabs += package_nav_tabs(project, current_user) - - nav_tabs << :learn_gitlab if learn_gitlab_experiment_enabled?(project) - - nav_tabs - end - # rubocop:enable Metrics/CyclomaticComplexity - - def package_nav_tabs(project, current_user) - [].tap do |tabs| - if ::Gitlab.config.packages.enabled && can?(current_user, :read_package, project) - tabs << :packages - end - end - end - - def apply_external_nav_tabs(nav_tabs, project) - nav_tabs << :external_issue_tracker if project.external_issue_tracker - nav_tabs << :external_wiki if project.external_wiki - - if project.has_confluence? - nav_tabs.delete(:wiki) - nav_tabs << :confluence - end - end - def tab_ability_map { cycle_analytics: :read_cycle_analytics, @@ -485,32 +376,6 @@ module ProjectsHelper } end - def view_operations_tab_ability - [ - :metrics_dashboard, - :read_alert_management_alert, - :read_environment, - :read_issue, - :read_sentry_issue, - :read_cluster, - :read_feature_flag, - :read_terraform_state - ] - end - - def can_view_operations_tab?(current_user, project) - return false unless project.feature_available?(:operations, current_user) - - view_operations_tab_ability.any? do |ability| - can?(current_user, ability, project) - end - end - - def can_view_product_analytics?(current_user, project) - Feature.enabled?(:product_analytics, project) && - can?(current_user, :read_product_analytics, project) - end - def search_tab_ability_map @search_tab_ability_map ||= tab_ability_map.merge( blobs: :download_code, @@ -578,14 +443,6 @@ module ProjectsHelper end end - def sidebar_operations_link_path(project = @project) - if can?(current_user, :read_environment, project) - metrics_project_environments_path(project) - else - project_feature_flags_path(project) - end - end - def project_last_activity(project) if project.last_activity_at time_ago_with_tooltip(project.last_activity_at, placement: 'bottom', html_class: 'last_activity_time_ago') @@ -723,29 +580,6 @@ module ProjectsHelper "#{request.path}?#{options.to_param}" end - def sidebar_security_configuration_paths - %w[ - projects/security/configuration#show - ] - end - - def sidebar_settings_paths - %w[ - projects#edit - integrations#show - services#edit - hooks#index - hooks#edit - access_tokens#index - hook_logs#show - repository#show - ci_cd#show - operations#show - badges#index - pages#show - ] - end - def sidebar_operations_paths %w[ environments @@ -766,10 +600,6 @@ module ProjectsHelper ] end - def sidebar_security_paths - %w[projects/security/configuration#show] - end - def user_can_see_auto_devops_implicitly_enabled_banner?(project, user) Ability.allowed?(user, :admin_project, project) && project.has_auto_devops_implicitly_enabled? && @@ -782,6 +612,16 @@ module ProjectsHelper end def settings_container_registry_expiration_policy_available?(project) + Feature.disabled?(:sidebar_refactor, current_user) && + can_destroy_container_registry_image?(current_user, project) + end + + def settings_packages_and_registries_enabled?(project) + Feature.enabled?(:sidebar_refactor, current_user) && + can_destroy_container_registry_image?(current_user, project) + end + + def can_destroy_container_registry_image?(current_user, project) Gitlab.config.registry.enabled && can?(current_user, :destroy_container_image, project) end @@ -811,4 +651,4 @@ module ProjectsHelper end end -ProjectsHelper.prepend_if_ee('EE::ProjectsHelper') +ProjectsHelper.prepend_mod_with('ProjectsHelper') diff --git a/app/helpers/registrations_helper.rb b/app/helpers/registrations_helper.rb new file mode 100644 index 00000000000..79f0a66f995 --- /dev/null +++ b/app/helpers/registrations_helper.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +module RegistrationsHelper + def social_signin_enabled? + ::Gitlab.dev_env_or_com? && + omniauth_enabled? && + devise_mapping.omniauthable? && + button_based_providers_enabled? + end +end + +RegistrationsHelper.prepend_mod_with('RegistrationsHelper') diff --git a/app/helpers/releases_helper.rb b/app/helpers/releases_helper.rb index d9851564585..de9288121c4 100644 --- a/app/helpers/releases_helper.rb +++ b/app/helpers/releases_helper.rb @@ -72,4 +72,4 @@ module ReleasesHelper end end -ReleasesHelper.prepend_if_ee('EE::ReleasesHelper') +ReleasesHelper.prepend_mod_with('ReleasesHelper') diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb index 2568917bafc..1f4c98d6f28 100644 --- a/app/helpers/search_helper.rb +++ b/app/helpers/search_helper.rb @@ -310,7 +310,7 @@ module SearchHelper link_to search_path(search_params) do concat label concat ' ' - concat content_tag(:span, count, class: ['badge badge-pill', badge_class], data: badge_data) + concat content_tag(:span, count, class: ['badge badge-pill gl-badge badge-muted sm', badge_class], data: badge_data) end end end @@ -431,4 +431,4 @@ module SearchHelper end end -SearchHelper.prepend_if_ee('EE::SearchHelper') +SearchHelper.prepend_mod_with('SearchHelper') diff --git a/app/helpers/selects_helper.rb b/app/helpers/selects_helper.rb index 4d0f9e530fb..88aff31af54 100644 --- a/app/helpers/selects_helper.rb +++ b/app/helpers/selects_helper.rb @@ -91,4 +91,4 @@ module SelectsHelper end end -SelectsHelper.prepend_if_ee('EE::SelectsHelper') +SelectsHelper.prepend_mod_with('SelectsHelper') diff --git a/app/helpers/services_helper.rb b/app/helpers/services_helper.rb index ffa09cb12fb..3d3ab3a6972 100644 --- a/app/helpers/services_helper.rb +++ b/app/helpers/services_helper.rb @@ -153,9 +153,9 @@ module ServicesHelper private def integration_level(integration) - if integration.instance + if integration.instance_level? 'instance' - elsif integration.group_id + elsif integration.group_level? 'group' else 'project' @@ -172,10 +172,14 @@ module ServicesHelper name: integration.to_param } end + + def show_service_templates_nav_link? + Feature.disabled?(:disable_service_templates, type: :development, default_enabled: :yaml) + end end -ServicesHelper.prepend_if_ee('EE::ServicesHelper') +ServicesHelper.prepend_mod_with('ServicesHelper') # The methods in `EE::ServicesHelper` should be available as both instance and # class methods. -ServicesHelper.extend_if_ee('EE::ServicesHelper') +ServicesHelper.extend_mod_with('ServicesHelper') diff --git a/app/helpers/sidebars_helper.rb b/app/helpers/sidebars_helper.rb index 31dfe21671a..0fc306a3f2e 100644 --- a/app/helpers/sidebars_helper.rb +++ b/app/helpers/sidebars_helper.rb @@ -39,7 +39,13 @@ module SidebarsHelper current_user: user, container: project, learn_gitlab_experiment_enabled: learn_gitlab_experiment_enabled?(project), - current_ref: current_ref + learn_gitlab_experiment_tracking_category: learn_gitlab_experiment_tracking_category, + current_ref: current_ref, + jira_issues_integration: project_jira_issues_integration?, + can_view_pipeline_editor: can_view_pipeline_editor?(project), + show_cluster_hint: show_gke_cluster_integration_callout?(project) } end end + +SidebarsHelper.prepend_mod_with('SidebarsHelper') diff --git a/app/helpers/snippets_helper.rb b/app/helpers/snippets_helper.rb index f4af7a5a350..84eb0405c01 100644 --- a/app/helpers/snippets_helper.rb +++ b/app/helpers/snippets_helper.rb @@ -72,4 +72,10 @@ module SnippetsHelper concat(file_count) end end + + def project_snippets_award_api_path(snippet) + if Feature.enabled?(:improved_emoji_picker, snippet.project, default_enabled: :yaml) + api_v4_projects_snippets_award_emoji_path(id: snippet.project.id, snippet_id: snippet.id) + end + end end diff --git a/app/helpers/sorting_helper.rb b/app/helpers/sorting_helper.rb index 974ec046bbb..0bb9e9e9bdd 100644 --- a/app/helpers/sorting_helper.rb +++ b/app/helpers/sorting_helper.rb @@ -301,4 +301,4 @@ module SortingHelper end end -SortingHelper.prepend_if_ee('::EE::SortingHelper') +SortingHelper.prepend_mod_with('SortingHelper') diff --git a/app/helpers/sorting_titles_values_helper.rb b/app/helpers/sorting_titles_values_helper.rb index 651a6437479..28d70f1db45 100644 --- a/app/helpers/sorting_titles_values_helper.rb +++ b/app/helpers/sorting_titles_values_helper.rb @@ -328,4 +328,4 @@ module SortingTitlesValuesHelper end end -SortingHelper.include_if_ee('::EE::SortingTitlesValuesHelper') +SortingHelper.include_mod_with('SortingTitlesValuesHelper') diff --git a/app/helpers/ssh_keys_helper.rb b/app/helpers/ssh_keys_helper.rb index 381db893943..f5a9bea482b 100644 --- a/app/helpers/ssh_keys_helper.rb +++ b/app/helpers/ssh_keys_helper.rb @@ -12,7 +12,10 @@ module SshKeysHelper message: _('This action cannot be undone, and will permanently delete the %{key} SSH key') % { key: key.title }, okVariant: 'danger', okTitle: _('Delete') - } + }, + toggle: 'tooltip', + placement: 'top', + container: 'body' } end end diff --git a/app/helpers/subscribable_banner_helper.rb b/app/helpers/subscribable_banner_helper.rb index c9d4370f8ad..d9251fb3f21 100644 --- a/app/helpers/subscribable_banner_helper.rb +++ b/app/helpers/subscribable_banner_helper.rb @@ -6,4 +6,4 @@ module SubscribableBannerHelper end end -SubscribableBannerHelper.prepend_if_ee('EE::SubscribableBannerHelper') +SubscribableBannerHelper.prepend_mod_with('SubscribableBannerHelper') diff --git a/app/helpers/system_note_helper.rb b/app/helpers/system_note_helper.rb index 85e644967ea..521423fbb94 100644 --- a/app/helpers/system_note_helper.rb +++ b/app/helpers/system_note_helper.rb @@ -54,8 +54,8 @@ module SystemNoteHelper extend self end -SystemNoteHelper.prepend_if_ee('EE::SystemNoteHelper') +SystemNoteHelper.prepend_mod_with('SystemNoteHelper') # The methods in `EE::SystemNoteHelper` should be available as both instance and # class methods. -SystemNoteHelper.extend_if_ee('EE::SystemNoteHelper') +SystemNoteHelper.extend_mod_with('SystemNoteHelper') diff --git a/app/helpers/time_zone_helper.rb b/app/helpers/time_zone_helper.rb index 00f65b72c8e..fe045182c96 100644 --- a/app/helpers/time_zone_helper.rb +++ b/app/helpers/time_zone_helper.rb @@ -18,7 +18,7 @@ module TimeZoneHelper def timezone_data(format: :short) attrs = TIME_ZONE_FORMAT_ATTRS.fetch(format) do valid_formats = TIME_ZONE_FORMAT_ATTRS.keys.map { |k| ":#{k}"}.join(", ") - raise ArgumentError.new("Invalid format :#{format}. Valid formats are #{valid_formats}.") + raise ArgumentError, "Invalid format :#{format}. Valid formats are #{valid_formats}." end ActiveSupport::TimeZone.all.map do |timezone| diff --git a/app/helpers/timeboxes_helper.rb b/app/helpers/timeboxes_helper.rb index e034a985b50..0993e210f42 100644 --- a/app/helpers/timeboxes_helper.rb +++ b/app/helpers/timeboxes_helper.rb @@ -288,4 +288,4 @@ module TimeboxesHelper end end -TimeboxesHelper.prepend_if_ee('EE::TimeboxesHelper') +TimeboxesHelper.prepend_mod_with('TimeboxesHelper') diff --git a/app/helpers/timeboxes_routing_helper.rb b/app/helpers/timeboxes_routing_helper.rb index 6fb5a1a3185..6a5bef74dc9 100644 --- a/app/helpers/timeboxes_routing_helper.rb +++ b/app/helpers/timeboxes_routing_helper.rb @@ -18,4 +18,4 @@ module TimeboxesRoutingHelper end end -TimeboxesRoutingHelper.prepend_if_ee('EE::TimeboxesRoutingHelper') +TimeboxesRoutingHelper.prepend_mod_with('TimeboxesRoutingHelper') diff --git a/app/helpers/todos_helper.rb b/app/helpers/todos_helper.rb index e9a0fef06c4..e9dc271dbdd 100644 --- a/app/helpers/todos_helper.rb +++ b/app/helpers/todos_helper.rb @@ -110,10 +110,8 @@ module TodosHelper 'alert' end - content_tag(:span, nil, class: 'target-status') do - content_tag(:span, nil, class: "status-box status-box-#{type}-#{todo.target.state.to_s.dasherize}") do - todo.target.state.to_s.capitalize - end + tag.span class: "gl-my-0 gl-px-2 status-box status-box-#{type}-#{todo.target.state.to_s.dasherize}" do + todo.target.state.to_s.capitalize end end @@ -232,4 +230,4 @@ module TodosHelper end end -TodosHelper.prepend_if_ee('EE::TodosHelper') +TodosHelper.prepend_mod_with('TodosHelper') diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb index b795851ba30..54c03d3d966 100644 --- a/app/helpers/tree_helper.rb +++ b/app/helpers/tree_helper.rb @@ -205,4 +205,4 @@ module TreeHelper end end -TreeHelper.prepend_if_ee('::EE::TreeHelper') +TreeHelper.prepend_mod_with('TreeHelper') diff --git a/app/helpers/user_callouts_helper.rb b/app/helpers/user_callouts_helper.rb index 7a90984cd77..23db3be631c 100644 --- a/app/helpers/user_callouts_helper.rb +++ b/app/helpers/user_callouts_helper.rb @@ -44,7 +44,7 @@ module UserCalloutsHelper def show_service_templates_deprecated_callout? !Gitlab.com? && current_user&.admin? && - Service.for_template.active.exists? && + Integration.for_template.active.exists? && !user_dismissed?(SERVICE_TEMPLATES_DEPRECATED_CALLOUT) end @@ -80,4 +80,4 @@ module UserCalloutsHelper end end -UserCalloutsHelper.prepend_if_ee('EE::UserCalloutsHelper') +UserCalloutsHelper.prepend_mod_with('UserCalloutsHelper') diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb index 1979426f844..c1d05c2d3cf 100644 --- a/app/helpers/users_helper.rb +++ b/app/helpers/users_helper.rb @@ -100,7 +100,7 @@ module UsersHelper badges << blocked_user_badge(user) if user.blocked? badges << { text: s_('AdminUsers|Admin'), variant: 'success' } if user.admin? badges << { text: s_('AdminUsers|External'), variant: 'secondary' } if user.external? - badges << { text: s_("AdminUsers|It's you!"), variant: nil } if current_user == user + badges << { text: s_("AdminUsers|It's you!"), variant: 'muted' } if current_user == user end end @@ -162,6 +162,49 @@ module UsersHelper header + list end + def user_ban_data(user) + { + path: ban_admin_user_path(user), + method: 'put', + modal_attributes: { + title: s_('AdminUsers|Ban user %{username}?') % { username: sanitize_name(user.name) }, + message: s_('AdminUsers|You can unban their account in the future. Their data remains intact.'), + okVariant: 'warning', + okTitle: s_('AdminUsers|Ban') + }.to_json + } + end + + def user_unban_data(user) + { + path: unban_admin_user_path(user), + method: 'put', + modal_attributes: { + title: s_('AdminUsers|Unban %{username}?') % { username: sanitize_name(user.name) }, + message: s_('AdminUsers|You ban their account in the future if necessary.'), + okVariant: 'info', + okTitle: s_('AdminUsers|Unban') + }.to_json + } + end + + def user_ban_effects + header = tag.p s_('AdminUsers|Banning the user has the following effects:') + + list = tag.ul do + concat tag.li s_('AdminUsers|User will be blocked') + end + + link_start = '<a href="%{url}" target="_blank">'.html_safe % { url: help_page_path("user/admin_area/moderate_users", anchor: "ban-a-user") } + info = tag.p s_('AdminUsers|Learn more about %{link_start}banned users.%{link_end}').html_safe % { link_start: link_start, link_end: '</a>'.html_safe } + + header + list + info + end + + def ban_feature_available? + Feature.enabled?(:ban_user_feature_flag) + end + def user_deactivation_data(user, message) { path: deactivate_admin_user_path(user), @@ -235,6 +278,9 @@ module UsersHelper pending_approval_badge = { text: s_('AdminUsers|Pending approval'), variant: 'info' } return pending_approval_badge if user.blocked_pending_approval? + banned_badge = { text: s_('AdminUsers|Banned'), variant: 'danger' } + return banned_badge if user.banned? + { text: s_('AdminUsers|Blocked'), variant: 'danger' } end @@ -322,4 +368,4 @@ module UsersHelper end end -UsersHelper.prepend_if_ee('EE::UsersHelper') +UsersHelper.prepend_mod_with('UsersHelper') diff --git a/app/helpers/version_check_helper.rb b/app/helpers/version_check_helper.rb index bac3c99e3e5..6f94c241914 100644 --- a/app/helpers/version_check_helper.rb +++ b/app/helpers/version_check_helper.rb @@ -11,16 +11,24 @@ module VersionCheckHelper def link_to_version if Gitlab.pre_release? - commit_link = link_to(Gitlab.revision, Gitlab::COM_URL + namespace_project_commits_path('gitlab-org', source_code_project, Gitlab.revision)) + commit_link = link_to(Gitlab.revision, source_host_url + namespace_project_commits_path(source_code_group, source_code_project, Gitlab.revision)) [Gitlab::VERSION, content_tag(:small, commit_link)].join(' ').html_safe else - link_to Gitlab::VERSION, Gitlab::COM_URL + namespace_project_tag_path('gitlab-org', source_code_project, "v#{Gitlab::VERSION}") + link_to Gitlab::VERSION, source_host_url + namespace_project_tag_path(source_code_group, source_code_project, "v#{Gitlab::VERSION}") end end + def source_host_url + Gitlab::COM_URL + end + + def source_code_group + 'gitlab-org' + end + def source_code_project 'gitlab-foss' end end -VersionCheckHelper.prepend_if_ee('EE::VersionCheckHelper') +VersionCheckHelper.prepend_mod diff --git a/app/helpers/webpack_helper.rb b/app/helpers/webpack_helper.rb index 170e3c45a21..0d27e07f172 100644 --- a/app/helpers/webpack_helper.rb +++ b/app/helpers/webpack_helper.rb @@ -1,10 +1,32 @@ # frozen_string_literal: true module WebpackHelper + def prefetch_link_tag(source) + href = asset_path(source) + + link_tag = tag.link(rel: 'prefetch', href: href) + + early_hints_link = "<#{href}>; rel=prefetch" + + request.send_early_hints("Link" => early_hints_link) + + link_tag + end + def webpack_bundle_tag(bundle) javascript_include_tag(*webpack_entrypoint_paths(bundle)) end + def webpack_preload_asset_tag(asset, options = {}) + path = Gitlab::Webpack::Manifest.asset_paths(asset).first + + if options.delete(:prefetch) + prefetch_link_tag(path) + else + preload_link_tag(path, options) + end + end + def webpack_controller_bundle_tags chunks = [] diff --git a/app/helpers/whats_new_helper.rb b/app/helpers/whats_new_helper.rb index 9362ae1491f..5fca00c5dce 100644 --- a/app/helpers/whats_new_helper.rb +++ b/app/helpers/whats_new_helper.rb @@ -10,6 +10,33 @@ module WhatsNewHelper end def display_whats_new? - Gitlab.dev_env_org_or_com? || user_signed_in? + (Gitlab.dev_env_org_or_com? || user_signed_in?) && + !Gitlab::CurrentSettings.current_application_settings.whats_new_variant_disabled? + end + + def whats_new_variants + ApplicationSetting.whats_new_variants + end + + def whats_new_variants_label(variant) + case variant + when 'all_tiers' + _("Enable What's new: All tiers") + when 'current_tier' + _("Enable What's new: Current tier only") + when 'disabled' + _("Disable What's new") + end + end + + def whats_new_variants_description(variant) + case variant + when 'all_tiers' + _("What's new presents new features from all tiers to help you keep track of all new features.") + when 'current_tier' + _("What's new presents new features for your current subscription tier, while hiding new features not available to your subscription tier.") + when 'disabled' + _("What's new is disabled and can no longer be viewed.") + end end end diff --git a/app/helpers/wiki_helper.rb b/app/helpers/wiki_helper.rb index c2a5ff40852..1b0d1254dc8 100644 --- a/app/helpers/wiki_helper.rb +++ b/app/helpers/wiki_helper.rb @@ -136,4 +136,4 @@ module WikiHelper end end -WikiHelper.prepend_if_ee('EE::WikiHelper') +WikiHelper.prepend_mod_with('WikiHelper') diff --git a/app/helpers/x509_helper.rb b/app/helpers/x509_helper.rb index 009635fb629..4afc5643af4 100644 --- a/app/helpers/x509_helper.rb +++ b/app/helpers/x509_helper.rb @@ -13,7 +13,7 @@ module X509Helper end subjects - rescue + rescue StandardError {} end |