From 85dc423f7090da0a52c73eb66faf22ddb20efff9 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Sat, 19 Sep 2020 01:45:44 +0000 Subject: Add latest changes from gitlab-org/gitlab@13-4-stable-ee --- app/helpers/application_helper.rb | 8 ++- app/helpers/application_settings_helper.rb | 7 +-- app/helpers/auth_helper.rb | 4 +- app/helpers/blob_helper.rb | 7 ++- app/helpers/boards_helper.rb | 4 +- app/helpers/ci/jobs_helper.rb | 1 + app/helpers/ci/pipelines_helper.rb | 33 ++++++++++--- app/helpers/clusters_helper.rb | 6 +++ app/helpers/container_registry_helper.rb | 8 +++ app/helpers/dashboard_helper.rb | 4 -- app/helpers/deploy_tokens_helper.rb | 9 +++- app/helpers/dev_ops_report_helper.rb | 18 +++++++ app/helpers/dev_ops_score_helper.rb | 18 ------- app/helpers/diff_helper.rb | 37 +++++++++++--- app/helpers/dropdowns_helper.rb | 44 ++++++++++------- app/helpers/emails_helper.rb | 45 +++++++++++++++++ app/helpers/environments_helper.rb | 78 ++++++++++++++--------------- app/helpers/form_helper.rb | 23 +++++++++ app/helpers/groups/group_members_helper.rb | 79 ++++++++++++++++++++++++++++++ app/helpers/groups_helper.rb | 15 ++++++ app/helpers/icons_helper.rb | 2 +- app/helpers/issuables_helper.rb | 15 ++++-- app/helpers/issues_helper.rb | 2 +- app/helpers/lazy_image_tag_helper.rb | 2 +- app/helpers/nav_helper.rb | 6 ++- app/helpers/notes_helper.rb | 6 ++- app/helpers/notifications_helper.rb | 10 ++-- app/helpers/operations_helper.rb | 1 + app/helpers/preferences_helper.rb | 11 +++++ app/helpers/projects_helper.rb | 6 +-- app/helpers/releases_helper.rb | 1 + app/helpers/search_helper.rb | 41 ++++++++++++++-- app/helpers/services_helper.rb | 28 ++++++++++- app/helpers/snippets_helper.rb | 2 +- app/helpers/startup_css_helper.rb | 7 +++ app/helpers/submodule_helper.rb | 54 ++++++++++++-------- app/helpers/system_note_helper.rb | 5 +- app/helpers/tab_helper.rb | 2 - app/helpers/tree_helper.rb | 34 ++++++++++++- app/helpers/user_callouts_helper.rb | 10 ++++ app/helpers/whats_new_helper.rb | 24 +++++++++ app/helpers/wiki_helper.rb | 9 +++- 42 files changed, 568 insertions(+), 158 deletions(-) create mode 100644 app/helpers/container_registry_helper.rb create mode 100644 app/helpers/dev_ops_report_helper.rb delete mode 100644 app/helpers/dev_ops_score_helper.rb create mode 100644 app/helpers/startup_css_helper.rb create mode 100644 app/helpers/whats_new_helper.rb (limited to 'app/helpers') diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 41b20a1d9a0..a81225c8954 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -4,6 +4,8 @@ require 'digest/md5' require 'uri' module ApplicationHelper + include StartupCssHelper + # See https://docs.gitlab.com/ee/development/ee_features.html#code-in-app-views # rubocop: disable CodeReuse/ActiveRecord def render_if_exists(partial, locals = {}) @@ -235,13 +237,9 @@ module ApplicationHelper "#{request.path}?#{options.compact.to_param}" end - def use_startup_css? - Feature.enabled?(:startup_css) && !Rails.env.test? - end - def stylesheet_link_tag_defer(path) if use_startup_css? - stylesheet_link_tag(path, media: "print") + stylesheet_link_tag(path, media: "print", crossorigin: ActionController::Base.asset_host ? 'anonymous' : nil) else stylesheet_link_tag(path, media: "all") end diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb index 404700bb25e..9245cc1cb1c 100644 --- a/app/helpers/application_settings_helper.rb +++ b/app/helpers/application_settings_helper.rb @@ -222,6 +222,8 @@ module ApplicationSettingsHelper :gitaly_timeout_default, :gitaly_timeout_medium, :gitaly_timeout_fast, + :gitpod_enabled, + :gitpod_url, :grafana_enabled, :grafana_url, :gravatar_enabled, @@ -298,7 +300,6 @@ module ApplicationSettingsHelper :unique_ips_limit_per_user, :unique_ips_limit_time_window, :usage_ping_enabled, - :instance_statistics_visibility_private, :user_default_external, :user_show_add_ssh_key_message, :user_default_internal_regex, @@ -313,7 +314,6 @@ module ApplicationSettingsHelper :snowplow_cookie_domain, :snowplow_enabled, :snowplow_app_id, - :snowplow_iglu_registry_url, :push_event_hooks_limit, :push_event_activities_limit, :custom_http_clone_url_root, @@ -328,7 +328,8 @@ module ApplicationSettingsHelper :group_import_limit, :group_export_limit, :group_download_export_limit, - :wiki_page_max_content_bytes + :wiki_page_max_content_bytes, + :container_registry_delete_tags_service_timeout ] end diff --git a/app/helpers/auth_helper.rb b/app/helpers/auth_helper.rb index a57e27d23c8..7f8cb66a84f 100644 --- a/app/helpers/auth_helper.rb +++ b/app/helpers/auth_helper.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module AuthHelper - PROVIDERS_WITH_ICONS = %w(twitter github gitlab bitbucket google_oauth2 facebook azure_oauth2 authentiq salesforce).freeze + PROVIDERS_WITH_ICONS = %w(twitter github gitlab bitbucket google_oauth2 facebook azure_oauth2 authentiq salesforce atlassian_oauth2).freeze LDAP_PROVIDER = /\Aldap/.freeze def ldap_enabled? @@ -133,6 +133,8 @@ module AuthHelper # rubocop: disable CodeReuse/ActiveRecord def auth_active?(provider) + return current_user.atlassian_identity.present? if provider == :atlassian_oauth2 + current_user.identities.exists?(provider: provider.to_s) end # rubocop: enable CodeReuse/ActiveRecord diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 615c834c529..2eff87ae0ec 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -41,7 +41,7 @@ module BlobHelper end def encode_ide_path(path) - url_encode(path).gsub('%2F', '/') + ERB::Util.url_encode(path).gsub('%2F', '/') end def edit_blob_button(project = @project, ref = @ref, path = @path, options = {}) @@ -375,4 +375,9 @@ module BlobHelper def human_access @project.team.human_max_access(current_user&.id).try(:downcase) end + + def editing_ci_config? + @path.to_s.end_with?(Ci::Pipeline::CONFIG_EXTENSION) || + @path.to_s == @project.ci_config_path_or_default + end end diff --git a/app/helpers/boards_helper.rb b/app/helpers/boards_helper.rb index f8c00f3a4cd..6a4a7a8dfb2 100644 --- a/app/helpers/boards_helper.rb +++ b/app/helpers/boards_helper.rb @@ -11,13 +11,13 @@ module BoardsHelper lists_endpoint: board_lists_path(board), board_id: board.id, disabled: (!can?(current_user, :create_non_backlog_issues, board)).to_s, - issue_link_base: build_issue_link_base, root_path: root_path, full_path: full_path, bulk_update_path: @bulk_issues_path, time_tracking_limit_to_hours: Gitlab::CurrentSettings.time_tracking_limit_to_hours.to_s, recent_boards_endpoint: recent_boards_path, - parent: current_board_parent.model_name.param_key + parent: current_board_parent.model_name.param_key, + group_id: @group&.id } end diff --git a/app/helpers/ci/jobs_helper.rb b/app/helpers/ci/jobs_helper.rb index 0344413b849..e876eb64029 100644 --- a/app/helpers/ci/jobs_helper.rb +++ b/app/helpers/ci/jobs_helper.rb @@ -6,6 +6,7 @@ module Ci { "endpoint" => project_job_path(@project, @build, format: :json), "project_path" => @project.full_path, + "artifact_help_url" => help_page_path('user/gitlab_com/index.html', anchor: 'gitlab-cicd'), "deployment_help_url" => help_page_path('user/project/clusters/index.html', anchor: 'troubleshooting'), "runner_help_url" => help_page_path('ci/runners/README.html', anchor: 'set-maximum-job-timeout-for-a-runner'), "runner_settings_url" => project_runners_path(@build.project, anchor: 'js-runners-settings'), diff --git a/app/helpers/ci/pipelines_helper.rb b/app/helpers/ci/pipelines_helper.rb index 749726e0e33..309aa477108 100644 --- a/app/helpers/ci/pipelines_helper.rb +++ b/app/helpers/ci/pipelines_helper.rb @@ -2,16 +2,35 @@ module Ci module PipelinesHelper + include Gitlab::Ci::Warnings + def pipeline_warnings(pipeline) return unless pipeline.warning_messages.any? - content_tag(:div, class: 'alert alert-warning') do - content_tag(:h4, 'Warning:') << - content_tag(:div) do - pipeline.warning_messages.each do |warning| - concat(markdown(warning.content)) - end - end + total_warnings = pipeline.warning_messages.length + message = warning_header(total_warnings) + + content_tag(:div, class: 'bs-callout bs-callout-warning') do + content_tag(:details) do + concat content_tag(:summary, message, class: 'gl-mb-2') + warning_markdown(pipeline) { |markdown| concat markdown } + end + end + end + + def warning_header(count) + message = _("%{total_warnings} warning(s) found:") % { total_warnings: count } + + return message unless count > MAX_LIMIT + + _("%{message} showing first %{warnings_displayed}") % { message: message, warnings_displayed: MAX_LIMIT } + end + + private + + def warning_markdown(pipeline) + pipeline.warning_messages(limit: MAX_LIMIT).each do |warning| + yield markdown(warning.content) end end end diff --git a/app/helpers/clusters_helper.rb b/app/helpers/clusters_helper.rb index b97e847c397..caad215e996 100644 --- a/app/helpers/clusters_helper.rb +++ b/app/helpers/clusters_helper.rb @@ -36,6 +36,12 @@ module ClustersHelper } end + def js_cluster_new + { + cluster_connect_help_path: help_page_path('user/project/clusters/add_remove_clusters', anchor: 'add-existing-cluster') + } + end + # This method is depreciated and will be removed when associated HAML files are moved to JavaScript def provider_icon(provider = nil) img_data = js_clusters_list_data.dig(:img_tags, provider&.to_sym) || diff --git a/app/helpers/container_registry_helper.rb b/app/helpers/container_registry_helper.rb new file mode 100644 index 00000000000..9a5d84a90dd --- /dev/null +++ b/app/helpers/container_registry_helper.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module ContainerRegistryHelper + def limit_delete_tags_service? + Feature.enabled?(:container_registry_expiration_policies_throttling) && + ContainerRegistry::Client.supports_tag_delete? + end +end diff --git a/app/helpers/dashboard_helper.rb b/app/helpers/dashboard_helper.rb index 0ba03cd90ea..195b3162039 100644 --- a/app/helpers/dashboard_helper.rb +++ b/app/helpers/dashboard_helper.rb @@ -58,10 +58,6 @@ module DashboardHelper links += [:activity, :milestones] end - if can?(current_user, :read_instance_statistics) - links << :analytics - end - links end end diff --git a/app/helpers/deploy_tokens_helper.rb b/app/helpers/deploy_tokens_helper.rb index 80a5bb44c69..d6fbe0b6b45 100644 --- a/app/helpers/deploy_tokens_helper.rb +++ b/app/helpers/deploy_tokens_helper.rb @@ -7,8 +7,13 @@ module DeployTokensHelper Rails.env.test? end - def container_registry_enabled?(project) + def container_registry_enabled?(group_or_project) Gitlab.config.registry.enabled && - can?(current_user, :read_container_image, project) + can?(current_user, :read_container_image, group_or_project) + end + + def packages_registry_enabled?(group_or_project) + Gitlab.config.packages.enabled && + can?(current_user, :read_package, group_or_project) end end diff --git a/app/helpers/dev_ops_report_helper.rb b/app/helpers/dev_ops_report_helper.rb new file mode 100644 index 00000000000..ab7e56fc1a2 --- /dev/null +++ b/app/helpers/dev_ops_report_helper.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module DevOpsReportHelper + def score_level(score) + if score < 33.33 + 'low' + elsif score < 66.66 + 'average' + else + 'high' + end + end + + def format_score(score) + precision = score < 1 ? 2 : 1 + number_with_precision(score, precision: precision) + end +end diff --git a/app/helpers/dev_ops_score_helper.rb b/app/helpers/dev_ops_score_helper.rb deleted file mode 100644 index 9a673998149..00000000000 --- a/app/helpers/dev_ops_score_helper.rb +++ /dev/null @@ -1,18 +0,0 @@ -# frozen_string_literal: true - -module DevOpsScoreHelper - def score_level(score) - if score < 33.33 - 'low' - elsif score < 66.66 - 'average' - else - 'high' - end - end - - def format_score(score) - precision = score < 1 ? 2 : 1 - number_with_precision(score, precision: precision) - end -end diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb index 3b25de521d0..7c254e069f6 100644 --- a/app/helpers/diff_helper.rb +++ b/app/helpers/diff_helper.rb @@ -100,20 +100,43 @@ module DiffHelper end def submodule_link(blob, ref, repository = @repository) - project_url, tree_url = submodule_links(blob, ref, repository) - commit_id = if tree_url.nil? - Commit.truncate_sha(blob.id) - else - link_to Commit.truncate_sha(blob.id), tree_url - end + urls = submodule_links(blob, ref, repository) + + folder_name = truncate(blob.name, length: 40) + folder_name = link_to(folder_name, urls.web) if urls&.web + + commit_id = Commit.truncate_sha(blob.id) + commit_id = link_to(commit_id, urls.tree) if urls&.tree [ - content_tag(:span, link_to(truncate(blob.name, length: 40), project_url)), + content_tag(:span, folder_name), '@', content_tag(:span, commit_id, class: 'commit-sha') ].join(' ').html_safe end + def submodule_diff_compare_link(diff_file) + compare_url = submodule_links(diff_file.blob, diff_file.content_sha, diff_file.repository, diff_file)&.compare + + link = "" + + if compare_url + + link_text = [ + _('Compare'), + ' ', + content_tag(:span, Commit.truncate_sha(diff_file.old_blob.id), class: 'commit-sha'), + '...', + content_tag(:span, Commit.truncate_sha(diff_file.blob.id), class: 'commit-sha') + ].join('').html_safe + + tooltip = _('Compare submodule commit revisions') + link = content_tag(:span, link_to(link_text, compare_url, class: 'btn has-tooltip', title: tooltip), class: 'submodule-compare') + end + + link + end + def diff_file_blob_raw_url(diff_file, only_path: false) project_raw_url(@project, tree_join(diff_file.content_sha, diff_file.file_path), only_path: only_path) end diff --git a/app/helpers/dropdowns_helper.rb b/app/helpers/dropdowns_helper.rb index 772a5f79a4d..84aa08281f6 100644 --- a/app/helpers/dropdowns_helper.rb +++ b/app/helpers/dropdowns_helper.rb @@ -62,20 +62,37 @@ module DropdownsHelper end def dropdown_title(title, options: {}) - content_tag :div, class: "dropdown-title" do + has_back = options.fetch(:back, false) + has_close = options.fetch(:close, true) + + container_class = %w[dropdown-title gl-display-flex] + margin_class = [] + + if has_back && has_close + container_class << 'gl-justify-content-space-between' + elsif has_back + margin_class << 'gl-mr-auto' + elsif has_close + margin_class << 'gl-ml-auto' + end + + container_class = container_class.join(' ') + margin_class = margin_class.join(' ') + + content_tag :div, class: container_class do title_output = [] - if options.fetch(:back, false) - title_output << content_tag(:button, class: "dropdown-title-button dropdown-menu-back", aria: { label: "Go back" }, type: "button") do - icon('arrow-left') + if has_back + title_output << content_tag(:button, class: "dropdown-title-button dropdown-menu-back " + margin_class, aria: { label: "Go back" }, type: "button") do + sprite_icon('arrow-left') end end - title_output << content_tag(:span, title) + title_output << content_tag(:span, title, class: margin_class) - if options.fetch(:close, true) - title_output << content_tag(:button, class: "dropdown-title-button dropdown-menu-close", aria: { label: "Close" }, type: "button") do - icon('times', class: 'dropdown-menu-close-icon') + if has_close + title_output << content_tag(:button, class: "dropdown-title-button dropdown-menu-close " + margin_class, aria: { label: "Close" }, type: "button") do + sprite_icon('close', size: 16, css_class: 'dropdown-menu-close-icon') end end @@ -83,20 +100,11 @@ module DropdownsHelper end end - def dropdown_input(placeholder, input_id: nil) - content_tag :div, class: "dropdown-input" do - filter_output = text_field_tag input_id, nil, class: "dropdown-input-field dropdown-no-filter", placeholder: placeholder, autocomplete: 'off' - filter_output << icon('times', class: "dropdown-input-clear js-dropdown-input-clear", role: "button") - - filter_output.html_safe - end - end - def dropdown_filter(placeholder, search_id: nil) content_tag :div, class: "dropdown-input" do filter_output = search_field_tag search_id, nil, class: "dropdown-input-field qa-dropdown-input-field", placeholder: placeholder, autocomplete: 'off' filter_output << icon('search', class: "dropdown-input-search") - filter_output << icon('times', class: "dropdown-input-clear js-dropdown-input-clear", role: "button") + filter_output << sprite_icon('close', size: 16, css_class: 'dropdown-input-clear js-dropdown-input-clear') filter_output.html_safe end diff --git a/app/helpers/emails_helper.rb b/app/helpers/emails_helper.rb index ba2330dfc9a..d5c22927991 100644 --- a/app/helpers/emails_helper.rb +++ b/app/helpers/emails_helper.rb @@ -177,8 +177,53 @@ module EmailsHelper strip_tags(render_message(:footer_message, style: '')) end + def say_hi(user) + _('Hi %{username}!') % { username: sanitize_name(user.name) } + end + + def say_hello(user) + _('Hello, %{username}!') % { username: sanitize_name(user.name) } + end + + def two_factor_authentication_disabled_text + _('Two-factor authentication has been disabled for your GitLab account.') + end + + def re_enable_two_factor_authentication_text(format: nil) + url = profile_two_factor_auth_url + + case format + when :html + settings_link_to = generate_link(_('two-factor authentication settings'), url).html_safe + _("If you want to re-enable two-factor authentication, visit the %{settings_link_to} page.").html_safe % { settings_link_to: settings_link_to } + else + _('If you want to re-enable two-factor authentication, visit %{two_factor_link}') % + { two_factor_link: url } + end + end + + def admin_changed_password_text(format: nil) + url = Gitlab.config.gitlab.url + + case format + when :html + link_to = generate_link(url, url).html_safe + _('An administrator changed the password for your GitLab account on %{link_to}.').html_safe % { link_to: link_to } + else + _('An administrator changed the password for your GitLab account on %{link_to}.') % { link_to: url } + end + end + + def contact_your_administrator_text + _('Please contact your administrator with any questions.') + end + private + def generate_link(text, url) + link_to(text, url, target: :_blank, rel: 'noopener noreferrer') + end + def show_footer? email_header_and_footer_enabled? && current_appearance&.show_footer? end diff --git a/app/helpers/environments_helper.rb b/app/helpers/environments_helper.rb index 39be8ae9f60..7f0c59f65a0 100644 --- a/app/helpers/environments_helper.rb +++ b/app/helpers/environments_helper.rb @@ -12,8 +12,8 @@ module EnvironmentsHelper def environments_folder_list_view_data { "endpoint" => folder_project_environments_path(@project, @folder, format: :json), - "folder-name" => @folder, - "can-read-environment" => can?(current_user, :read_environment, @project).to_s + "folder_name" => @folder, + "can_read_environment" => can?(current_user, :read_environment, @project).to_s } end @@ -33,11 +33,11 @@ module EnvironmentsHelper def environment_logs_data(project, environment) { - "environment-name": environment.name, - "environments-path": project_environments_path(project, format: :json), - "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) + "environment_name": environment.name, + "environments_path": project_environments_path(project, format: :json), + "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) } end @@ -51,18 +51,18 @@ module EnvironmentsHelper return {} unless project { - 'settings-path' => edit_project_service_path(project, 'prometheus'), - 'clusters-path' => project_clusters_path(project), - 'dashboards-endpoint' => project_performance_monitoring_dashboards_path(project, format: :json), - 'default-branch' => project.default_branch, - 'project-path' => project_path(project), - 'tags-path' => project_tags_path(project), - 'external-dashboard-url' => project.metrics_setting_external_dashboard_url, - 'custom-metrics-path' => project_prometheus_metrics_path(project), - '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 + 'settings_path' => edit_project_service_path(project, 'prometheus'), + 'clusters_path' => project_clusters_path(project), + 'dashboards_endpoint' => project_performance_monitoring_dashboards_path(project, format: :json), + 'default_branch' => project.default_branch, + 'project_path' => project_path(project), + 'tags_path' => project_tags_path(project), + 'external_dashboard_url' => project.metrics_setting_external_dashboard_url, + 'custom_metrics_path' => project_prometheus_metrics_path(project), + '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 } end @@ -70,11 +70,11 @@ module EnvironmentsHelper return {} unless environment { - 'metrics-dashboard-base-path' => metrics_dashboard_base_path(environment, project), - 'current-environment-name' => environment.name, - 'has-metrics' => "#{environment.has_metrics?}", - 'prometheus-status' => "#{environment.prometheus_status}", - 'environment-state' => "#{environment.state}" + 'metrics_dashboard_base_path' => metrics_dashboard_base_path(environment, project), + 'current_environment_name' => environment.name, + 'has_metrics' => "#{environment.has_metrics?}", + 'prometheus_status' => "#{environment.prometheus_status}", + 'environment_state' => "#{environment.state}" } end @@ -93,26 +93,26 @@ module EnvironmentsHelper return {} unless project && environment { - 'metrics-endpoint' => additional_metrics_project_environment_path(project, environment, format: :json), - 'dashboard-endpoint' => metrics_dashboard_project_environment_path(project, environment, format: :json), - 'deployments-endpoint' => project_environment_deployments_path(project, environment, format: :json), - 'alerts-endpoint' => project_prometheus_alerts_path(project, environment_id: environment.id, format: :json), - 'operations-settings-path' => project_settings_operations_path(project), - 'can-access-operations-settings' => can?(current_user, :admin_operations, project).to_s, - 'panel-preview-endpoint' => project_metrics_dashboards_builder_path(project, format: :json) + 'metrics_endpoint' => additional_metrics_project_environment_path(project, environment, format: :json), + 'dashboard_endpoint' => metrics_dashboard_project_environment_path(project, environment, format: :json), + 'deployments_endpoint' => project_environment_deployments_path(project, environment, format: :json), + 'alerts_endpoint' => project_prometheus_alerts_path(project, environment_id: environment.id, format: :json), + 'operations_settings_path' => project_settings_operations_path(project), + 'can_access_operations_settings' => can?(current_user, :admin_operations, project).to_s, + 'panel_preview_endpoint' => project_metrics_dashboards_builder_path(project, format: :json) } end def static_metrics_data { - 'documentation-path' => help_page_path('administration/monitoring/prometheus/index.md'), - 'add-dashboard-documentation-path' => help_page_path('operations/metrics/dashboards/index.md', anchor: 'add-a-new-dashboard-to-your-project'), - 'empty-getting-started-svg-path' => image_path('illustrations/monitoring/getting_started.svg'), - 'empty-loading-svg-path' => image_path('illustrations/monitoring/loading.svg'), - 'empty-no-data-svg-path' => image_path('illustrations/monitoring/no_data.svg'), - 'empty-no-data-small-svg-path' => image_path('illustrations/chart-empty-state-small.svg'), - 'empty-unable-to-connect-svg-path' => image_path('illustrations/monitoring/unable_to_connect.svg'), - 'custom-dashboard-base-path' => Gitlab::Metrics::Dashboard::RepoDashboardFinder::DASHBOARD_ROOT + 'documentation_path' => help_page_path('administration/monitoring/prometheus/index.md'), + 'add_dashboard_documentation_path' => help_page_path('operations/metrics/dashboards/index.md', anchor: 'add-a-new-dashboard-to-your-project'), + 'empty_getting_started_svg_path' => image_path('illustrations/monitoring/getting_started.svg'), + 'empty_loading_svg_path' => image_path('illustrations/monitoring/loading.svg'), + 'empty_no_data_svg_path' => image_path('illustrations/monitoring/no_data.svg'), + 'empty_no_data_small_svg_path' => image_path('illustrations/chart-empty-state-small.svg'), + 'empty_unable_to_connect_svg_path' => image_path('illustrations/monitoring/unable_to_connect.svg'), + 'custom_dashboard_base_path' => Gitlab::Metrics::Dashboard::RepoDashboardFinder::DASHBOARD_ROOT } end end diff --git a/app/helpers/form_helper.rb b/app/helpers/form_helper.rb index 67bfeb22d92..3dde5afcb92 100644 --- a/app/helpers/form_helper.rb +++ b/app/helpers/form_helper.rb @@ -55,6 +55,29 @@ module FormHelper dropdown_data end + def reviewers_dropdown_options(issuable_type) + { + toggle_class: 'js-reviewer-search js-multiselect js-save-user-data', + title: 'Request review from', + filter: true, + dropdown_class: 'dropdown-menu-user dropdown-menu-selectable dropdown-menu-reviewer', + placeholder: _('Search users'), + data: { + first_user: current_user&.username, + null_user: true, + current_user: true, + project_id: (@target_project || @project)&.id, + field_name: "#{issuable_type}[reviewer_ids][]", + default_label: 'Unassigned', + 'dropdown-header': 'Reviewer(s)', + multi_select: true, + 'input-meta': 'name', + 'always-show-selectbox': true, + current_user_info: UserSerializer.new.represent(current_user) + } + } + end + # Overwritten def issue_supports_multiple_assignees? false diff --git a/app/helpers/groups/group_members_helper.rb b/app/helpers/groups/group_members_helper.rb index 1952325c504..dcff2be34da 100644 --- a/app/helpers/groups/group_members_helper.rb +++ b/app/helpers/groups/group_members_helper.rb @@ -1,6 +1,10 @@ # frozen_string_literal: true module Groups::GroupMembersHelper + include AvatarsHelper + + AVATAR_SIZE = 40 + def group_member_select_options { multiple: true, class: 'input-clamp qa-member-select-field ', scope: :all, email_user: true } end @@ -8,6 +12,81 @@ module Groups::GroupMembersHelper def render_invite_member_for_group(group, default_access_level) render 'shared/members/invite_member', submit_url: group_group_members_path(group), access_levels: GroupMember.access_level_roles, default_access_level: default_access_level end + + def linked_groups_data_json(group_links) + GroupGroupLinkSerializer.new.represent(group_links).to_json + end + + def members_data_json(group, members) + members_data(group, members).to_json + end + + private + + def members_data(group, members) + members.map do |member| + user = member.user + source = member.source + + data = { + id: member.id, + created_at: member.created_at, + expires_at: member.expires_at&.to_time, + requested_at: member.requested_at, + can_update: member.can_update?, + can_remove: member.can_remove?, + can_override: member.can_override?, + access_level: { + string_value: member.human_access, + integer_value: member.access_level + }, + source: { + id: source.id, + name: source.full_name, + web_url: Gitlab::UrlBuilder.build(source) + } + }.merge(member_created_by_data(member.created_by)) + + if user.present? + data[:user] = member_user_data(user) + else + data[:invite] = member_invite_data(member) + end + + data + end + end + + def member_created_by_data(created_by) + return {} unless created_by.present? + + { + created_by: { + name: created_by.name, + web_url: Gitlab::UrlBuilder.build(created_by) + } + } + end + + def member_user_data(user) + { + id: user.id, + name: user.name, + username: user.username, + web_url: Gitlab::UrlBuilder.build(user), + avatar_url: avatar_icon_for_user(user, AVATAR_SIZE), + blocked: user.blocked?, + two_factor_enabled: user.two_factor_enabled? + } + end + + def member_invite_data(member) + { + email: member.invite_email, + avatar_url: avatar_icon_for_email(member.invite_email, AVATAR_SIZE), + can_resend: member.can_resend_invite? + } + end end Groups::GroupMembersHelper.prepend_if_ee('EE::Groups::GroupMembersHelper') diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb index eb80acd869f..06a52457fd6 100644 --- a/app/helpers/groups_helper.rb +++ b/app/helpers/groups_helper.rb @@ -167,8 +167,23 @@ module GroupsHelper @group.packages_feature_enabled? 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) + end + private + def just_created? + flash[:notice] =~ /successfully created/ + end + + def multiple_members?(group) + group.member_count > 1 + end + def get_group_sidebar_links links = [:overview, :group_members] diff --git a/app/helpers/icons_helper.rb b/app/helpers/icons_helper.rb index 9957d5c6330..0352b0ddf28 100644 --- a/app/helpers/icons_helper.rb +++ b/app/helpers/icons_helper.rb @@ -100,7 +100,7 @@ module IconsHelper def boolean_to_icon(value) if value - icon('circle', class: 'cgreen') + sprite_icon('check', css_class: 'cgreen') else sprite_icon('power', css_class: 'clgray') end diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index 0b859a39c4f..b255597b18d 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -205,6 +205,12 @@ module IssuablesHelper author_output end + if access = project.team.human_max_access(issuable.author_id) + output << content_tag(:span, access, class: "user-access-role has-tooltip d-none d-xl-inline-block gl-ml-3 ", title: _("This user is a %{access} of the %{name} project.") % { access: access.downcase, name: project.name }) + elsif project.team.contributor?(issuable.author_id) + output << content_tag(:span, _("Contributor"), class: "user-access-role has-tooltip d-none d-xl-inline-block gl-ml-3", title: _("This user has previously committed to the %{name} project.") % { name: project.name }) + end + output << content_tag(:span, (sprite_icon('first-contribution', css_class: 'gl-icon gl-vertical-align-middle') if issuable.first_contribution?), class: 'has-tooltip gl-ml-2', title: _('1st contribution!')) output << content_tag(:span, (issuable.task_status if issuable.tasks?), id: "task_status", class: "d-none d-sm-none d-md-inline-block gl-ml-3") @@ -292,8 +298,10 @@ module IssuablesHelper { hasClosingMergeRequest: issuable.merge_requests_count(current_user) != 0, + issueType: issuable.issue_type, zoomMeetingUrl: ZoomMeeting.canonical_meeting_url(issuable), - sentryIssueIdentifier: SentryIssue.find_by(issue: issuable)&.sentry_issue_identifier # rubocop:disable CodeReuse/ActiveRecord + sentryIssueIdentifier: SentryIssue.find_by(issue: issuable)&.sentry_issue_identifier, # rubocop:disable CodeReuse/ActiveRecord + iid: issuable.iid.to_s } end @@ -301,8 +309,8 @@ module IssuablesHelper return { groupPath: parent.path } if parent.is_a?(Group) { - projectPath: ref_project.path, - projectNamespace: ref_project.namespace.full_path + projectPath: ref_project.path, + projectNamespace: ref_project.namespace.full_path } end @@ -464,6 +472,7 @@ module IssuablesHelper rootPath: root_path, fullPath: issuable[:project_full_path], iid: issuable[:iid], + severity: issuable[:severity], timeTrackingLimitToHours: Gitlab::CurrentSettings.time_tracking_limit_to_hours } end diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 55170cbfa6b..e8ea39d7ffc 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -4,7 +4,7 @@ module IssuesHelper def issue_css_classes(issue) classes = ["issue"] classes << "closed" if issue.closed? - classes << "today" if issue.today? + classes << "today" if issue.new? classes << "user-can-drag" if @sort == 'relative_position' classes.join(' ') end diff --git a/app/helpers/lazy_image_tag_helper.rb b/app/helpers/lazy_image_tag_helper.rb index ac987a04895..0c5744b46ae 100644 --- a/app/helpers/lazy_image_tag_helper.rb +++ b/app/helpers/lazy_image_tag_helper.rb @@ -25,5 +25,5 @@ module LazyImageTagHelper end # Required for Banzai::Filter::ImageLazyLoadFilter - module_function :placeholder_image + module_function :placeholder_image # rubocop: disable Style/AccessModifierDeclarations end diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb index d849ed9d076..578c7ae7923 100644 --- a/app/helpers/nav_helper.rb +++ b/app/helpers/nav_helper.rb @@ -47,7 +47,7 @@ module NavHelper end def has_extra_nav_icons? - Gitlab::Sherlock.enabled? || can?(current_user, :read_instance_statistics) || current_user.admin? + Gitlab::Sherlock.enabled? || current_user.admin? end def page_has_markdown? @@ -62,6 +62,10 @@ module NavHelper %w(system_info background_jobs health_check requests_profiles) end + def admin_analytics_nav_links + %w(dev_ops_report cohorts) + end + def group_issues_sub_menu_items %w(groups#issues labels#index milestones#index boards#index boards#show) end diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb index 2ba1d841c2e..c02adfcf4c6 100644 --- a/app/helpers/notes_helper.rb +++ b/app/helpers/notes_helper.rb @@ -85,6 +85,10 @@ module NotesHelper note.project.team.max_member_access(note.author_id) end + def note_human_max_access(note) + note.project.team.human_max_access(note.author_id) + end + def discussion_path(discussion) if discussion.for_merge_request? return unless discussion.diff_discussion? @@ -181,7 +185,7 @@ module NotesHelper reopenPath: reopen_issuable_path(issuable), notesPath: notes_url, prerenderedNotesCount: issuable.capped_notes_count(MAX_PRERENDERED_NOTES), - lastFetchedAt: Time.now.to_i + lastFetchedAt: Time.now.to_i * ::Gitlab::UpdatedNotesPaginator::MICROSECOND } if issuable.is_a?(MergeRequest) diff --git a/app/helpers/notifications_helper.rb b/app/helpers/notifications_helper.rb index 9a64fe98f86..784b242e2b5 100644 --- a/app/helpers/notifications_helper.rb +++ b/app/helpers/notifications_helper.rb @@ -6,15 +6,15 @@ module NotificationsHelper def notification_icon_class(level) case level.to_sym when :disabled, :owner_disabled - 'microphone-slash' + 'notifications-off' when :participating - 'volume-up' + 'notifications' when :watch 'eye' when :mention 'at' when :global - 'globe' + 'earth' end end @@ -28,8 +28,8 @@ module NotificationsHelper end end - def notification_icon(level, text = nil) - icon("#{notification_icon_class(level)} fw", text: text) + def notification_icon(level) + sprite_icon("#{notification_icon_class(level)}") end def notification_title(level) diff --git a/app/helpers/operations_helper.rb b/app/helpers/operations_helper.rb index 37e91153710..521f394a920 100644 --- a/app/helpers/operations_helper.rb +++ b/app/helpers/operations_helper.rb @@ -43,6 +43,7 @@ module OperationsHelper create_issue: setting.create_issue.to_s, issue_template_key: setting.issue_template_key.to_s, send_email: setting.send_email.to_s, + auto_close_incident: setting.auto_close_incident.to_s, pagerduty_active: setting.pagerduty_active.to_s, pagerduty_token: setting.pagerduty_token.to_s, pagerduty_webhook_url: project_incidents_integrations_pagerduty_url(@project, token: setting.pagerduty_token), diff --git a/app/helpers/preferences_helper.rb b/app/helpers/preferences_helper.rb index 5a917a02d51..2c406641882 100644 --- a/app/helpers/preferences_helper.rb +++ b/app/helpers/preferences_helper.rb @@ -61,6 +61,10 @@ module PreferencesHelper @user_application_theme ||= Gitlab::Themes.for_user(current_user).css_class end + def user_application_theme_name + @user_application_theme_name ||= Gitlab::Themes.for_user(current_user).name.downcase.tr(' ', '_') + end + def user_color_scheme Gitlab::ColorSchemes.for_user(current_user).css_class end @@ -76,6 +80,13 @@ module PreferencesHelper ) end + def integration_views + [].tap do |views| + views << 'gitpod' if Gitlab::Gitpod.feature_and_settings_enabled? + views << 'sourcegraph' if Gitlab::Sourcegraph.feature_available? && Gitlab::CurrentSettings.sourcegraph_enabled + end + end + private # Ensure that anyone adding new options updates `DASHBOARD_CHOICES` too diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 1ce4903f8df..72cc07b13a5 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -7,7 +7,7 @@ module ProjectsHelper end def link_to_project(project) - link_to namespace_project_path(namespace_id: project.namespace, id: project), title: h(project.name) do + link_to namespace_project_path(namespace_id: project.namespace, id: project), title: h(project.name), class: 'gl-link' do title = content_tag(:span, project.name, class: 'project-name') if project.namespace @@ -109,7 +109,7 @@ module ProjectsHelper end def transfer_project_message(project) - _("You are going to transfer %{project_full_name} to another owner. Are you ABSOLUTELY sure?") % + _("You are going to transfer %{project_full_name} to another namespace. Are you ABSOLUTELY sure?") % { project_full_name: project.full_name } end @@ -640,7 +640,7 @@ module ProjectsHelper pagesAvailable: Gitlab.config.pages.enabled, pagesAccessControlEnabled: Gitlab.config.pages.access_control, pagesAccessControlForced: ::Gitlab::Pages.access_control_is_forced?, - pagesHelpPath: help_page_path('user/project/pages/introduction', anchor: 'gitlab-pages-access-control-core') + pagesHelpPath: help_page_path('user/project/pages/introduction', anchor: 'gitlab-pages-access-control') } end diff --git a/app/helpers/releases_helper.rb b/app/helpers/releases_helper.rb index f1dff18523f..979a68ecb7b 100644 --- a/app/helpers/releases_helper.rb +++ b/app/helpers/releases_helper.rb @@ -15,6 +15,7 @@ module ReleasesHelper def data_for_releases_page { project_id: @project.id, + project_path: @project.full_path, illustration_path: illustration, documentation_path: help_page }.tap do |data| diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb index 377aee1ae9e..d55ad878b92 100644 --- a/app/helpers/search_helper.rb +++ b/app/helpers/search_helper.rb @@ -1,12 +1,14 @@ # frozen_string_literal: true module SearchHelper - SEARCH_PERMITTED_PARAMS = [:search, :scope, :project_id, :group_id, :repository_ref, :snippets].freeze + SEARCH_PERMITTED_PARAMS = [:search, :scope, :project_id, :group_id, :repository_ref, :snippets, :state].freeze def search_autocomplete_opts(term) return unless current_user resources_results = [ + recent_merge_requests_autocomplete(term), + recent_issues_autocomplete(term), groups_autocomplete(term), projects_autocomplete(term) ].flatten @@ -178,6 +180,34 @@ module SearchHelper } end end + + def recent_merge_requests_autocomplete(term, limit = 5) + return [] unless current_user + + ::Gitlab::Search::RecentMergeRequests.new(user: current_user).search(term).limit(limit).map do |mr| + { + category: "Recent merge requests", + id: mr.id, + label: search_result_sanitize(mr.title), + url: merge_request_path(mr), + avatar_url: mr.project.avatar_url || '' + } + end + end + + def recent_issues_autocomplete(term, limit = 5) + return [] unless current_user + + ::Gitlab::Search::RecentIssues.new(user: current_user).search(term).limit(limit).map do |i| + { + category: "Recent issues", + id: i.id, + label: search_result_sanitize(i.title), + url: issue_path(i), + avatar_url: i.project.avatar_url || '' + } + end + end # rubocop: enable CodeReuse/ActiveRecord def search_result_sanitize(str) @@ -250,15 +280,16 @@ module SearchHelper # Sanitize a HTML field for search display. Most tags are stripped out and the # maximum length is set to 200 characters. - def search_md_sanitize(object, field) - html = markdown_field(object, field) - html = Truncato.truncate( - html, + def search_md_sanitize(source) + source = Truncato.truncate( + source, count_tags: false, count_tail: false, max_length: 200 ) + html = markdown(source) + # Truncato's filtered_tags and filtered_attributes are not quite the same sanitize(html, tags: %w(a p ol ul li pre code)) end diff --git a/app/helpers/services_helper.rb b/app/helpers/services_helper.rb index e9d39cc8175..6b5de73a831 100644 --- a/app/helpers/services_helper.rb +++ b/app/helpers/services_helper.rb @@ -92,9 +92,15 @@ module ServicesHelper commit_events: integration.commit_events.to_s, enable_comments: integration.comment_on_event_enabled.to_s, comment_detail: integration.comment_detail, + learn_more_path: integrations_help_page_path, trigger_events: trigger_events_for_service(integration), fields: fields_for_service(integration), - inherit_from_id: integration.inherit_from_id + inherit_from_id: integration.inherit_from_id, + integration_level: integration_level(integration), + editable: integration.editable?.to_s, + cancel_path: scoped_integrations_path, + can_test: integration.can_test?.to_s, + test_path: scoped_test_integration_path(integration) } end @@ -106,11 +112,31 @@ module ServicesHelper ServiceFieldSerializer.new(service: integration).represent(integration.global_fields).to_json end + def integrations_help_page_path + help_page_path('user/admin_area/settings/project_integration_management') + end + def project_jira_issues_integration? false end + def group_level_integrations? + @group.present? && Feature.enabled?(:group_level_integrations, @group) + end + extend self + + private + + def integration_level(integration) + if integration.instance + 'instance' + elsif integration.group_id + 'group' + else + 'project' + end + end end ServicesHelper.prepend_if_ee('EE::ServicesHelper') diff --git a/app/helpers/snippets_helper.rb b/app/helpers/snippets_helper.rb index 10c95da394f..94c46feb8ae 100644 --- a/app/helpers/snippets_helper.rb +++ b/app/helpers/snippets_helper.rb @@ -12,7 +12,7 @@ module SnippetsHelper end def download_raw_snippet_button(snippet) - link_to(icon('download'), + link_to(sprite_icon('download'), gitlab_raw_snippet_path(snippet, inline: false), target: '_blank', rel: 'noopener noreferrer', diff --git a/app/helpers/startup_css_helper.rb b/app/helpers/startup_css_helper.rb new file mode 100644 index 00000000000..b54e19bfc0d --- /dev/null +++ b/app/helpers/startup_css_helper.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +module StartupCssHelper + def use_startup_css? + (Feature.enabled?(:startup_css) || params[:startup_css] == 'true' || cookies['startup_css'] == 'true') && !Rails.env.test? + end +end diff --git a/app/helpers/submodule_helper.rb b/app/helpers/submodule_helper.rb index 06ea3dd8a81..959178c47d7 100644 --- a/app/helpers/submodule_helper.rb +++ b/app/helpers/submodule_helper.rb @@ -6,19 +6,19 @@ module SubmoduleHelper VALID_SUBMODULE_PROTOCOLS = %w[http https git ssh].freeze # links to files listing for submodule if submodule is a project on this server - def submodule_links(submodule_item, ref = nil, repository = @repository) - repository.submodule_links.for(submodule_item, ref) + def submodule_links(submodule_item, ref = nil, repository = @repository, diff_file = nil) + repository.submodule_links.for(submodule_item, ref, diff_file) end - def submodule_links_for_url(submodule_item_id, url, repository) - return [nil, nil] unless url + def submodule_links_for_url(submodule_item_id, url, repository, old_submodule_item_id = nil) + return [nil, nil, nil] unless url if url == '.' || url == './' url = File.join(Gitlab.config.gitlab.url, repository.project.full_path) end if url =~ %r{([^/:]+)/([^/]+(?:\.git)?)\Z} - namespace, project = $1, $2 + namespace, project = Regexp.last_match(1), Regexp.last_match(2) gitlab_hosts = [Gitlab.config.gitlab.url, Gitlab.config.gitlab_shell.ssh_path_prefix] @@ -34,21 +34,24 @@ module SubmoduleHelper project.sub!(/\.git\z/, '') if self_url?(url, namespace, project) - [url_helpers.namespace_project_path(namespace, project), - url_helpers.namespace_project_tree_path(namespace, project, submodule_item_id)] + [ + url_helpers.namespace_project_path(namespace, project), + url_helpers.namespace_project_tree_path(namespace, project, submodule_item_id), + (url_helpers.namespace_project_compare_path(namespace, project, to: submodule_item_id, from: old_submodule_item_id) if old_submodule_item_id) + ] elsif relative_self_url?(url) - relative_self_links(url, submodule_item_id, repository.project) + relative_self_links(url, submodule_item_id, old_submodule_item_id, repository.project) elsif gist_github_dot_com_url?(url) gist_github_com_tree_links(namespace, project, submodule_item_id) elsif github_dot_com_url?(url) - github_com_tree_links(namespace, project, submodule_item_id) + github_com_tree_links(namespace, project, submodule_item_id, old_submodule_item_id) elsif gitlab_dot_com_url?(url) - gitlab_com_tree_links(namespace, project, submodule_item_id) + gitlab_com_tree_links(namespace, project, submodule_item_id, old_submodule_item_id) else - [sanitize_submodule_url(url), nil] + [sanitize_submodule_url(url), nil, nil] end else - [sanitize_submodule_url(url), nil] + [sanitize_submodule_url(url), nil, nil] end end @@ -79,22 +82,30 @@ module SubmoduleHelper url.start_with?('../', './') end - def gitlab_com_tree_links(namespace, project, commit) + def gitlab_com_tree_links(namespace, project, commit, old_commit) base = ['https://gitlab.com/', namespace, '/', project].join('') - [base, [base, '/-/tree/', commit].join('')] + [ + base, + [base, '/-/tree/', commit].join(''), + ([base, '/-/compare/', old_commit, '...', commit].join('') if old_commit) + ] end def gist_github_com_tree_links(namespace, project, commit) base = ['https://gist.github.com/', namespace, '/', project].join('') - [base, [base, commit].join('/')] + [base, [base, commit].join('/'), nil] end - def github_com_tree_links(namespace, project, commit) + def github_com_tree_links(namespace, project, commit, old_commit) base = ['https://github.com/', namespace, '/', project].join('') - [base, [base, '/tree/', commit].join('')] + [ + base, + [base, '/tree/', commit].join(''), + ([base, '/compare/', old_commit, '...', commit].join('') if old_commit) + ] end - def relative_self_links(relative_path, commit, project) + def relative_self_links(relative_path, commit, old_commit, project) relative_path = relative_path.rstrip absolute_project_path = "/" + project.full_path @@ -107,7 +118,7 @@ module SubmoduleHelper target_namespace_path = File.dirname(submodule_project_path) if target_namespace_path == '/' || target_namespace_path.start_with?(absolute_project_path) - return [nil, nil] + return [nil, nil, nil] end target_namespace_path.sub!(%r{^/}, '') @@ -116,10 +127,11 @@ module SubmoduleHelper begin [ url_helpers.namespace_project_path(target_namespace_path, submodule_base), - url_helpers.namespace_project_tree_path(target_namespace_path, submodule_base, commit) + url_helpers.namespace_project_tree_path(target_namespace_path, submodule_base, commit), + (url_helpers.namespace_project_compare_path(target_namespace_path, submodule_base, to: commit, from: old_commit) if old_commit) ] rescue ActionController::UrlGenerationError - [nil, nil] + [nil, nil, nil] end end diff --git a/app/helpers/system_note_helper.rb b/app/helpers/system_note_helper.rb index 6ea6a33ba5e..0227ad1092d 100644 --- a/app/helpers/system_note_helper.rb +++ b/app/helpers/system_note_helper.rb @@ -7,7 +7,7 @@ module SystemNoteHelper 'description' => 'pencil-square', 'merge' => 'git-merge', 'merged' => 'git-merge', - 'opened' => 'issue-open', + 'opened' => 'issues', 'closed' => 'issue-close', 'time_tracking' => 'timer', 'assignee' => 'user', @@ -33,7 +33,8 @@ module SystemNoteHelper 'designs_removed' => 'doc-image', 'designs_discussion_added' => 'doc-image', 'status' => 'status', - 'alert_issue_added' => 'issues' + 'alert_issue_added' => 'issues', + 'new_alert_added' => 'warning' }.freeze def system_note_icon_name(note) diff --git a/app/helpers/tab_helper.rb b/app/helpers/tab_helper.rb index 0f156003a01..6fbe2642056 100644 --- a/app/helpers/tab_helper.rb +++ b/app/helpers/tab_helper.rb @@ -127,5 +127,3 @@ module TabHelper end end end - -TabHelper.prepend_if_ee('EE::TabHelper') diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb index 90a5b6da4c7..7644ed783eb 100644 --- a/app/helpers/tree_helper.rb +++ b/app/helpers/tree_helper.rb @@ -191,16 +191,46 @@ module TreeHelper def vue_file_list_data(project, ref) { - can_push_code: current_user&.can?(:push_code, project) && "true", project_path: project.full_path, project_short_path: project.path, - fork_path: current_user&.fork_of(project)&.full_path, ref: ref, escaped_ref: ActionDispatch::Journey::Router::Utils.escape_path(ref), full_name: project.name_with_namespace } end + def ide_base_path(project) + can_push_code = current_user&.can?(:push_code, project) + fork_path = current_user&.fork_of(project)&.full_path + + if can_push_code + project.full_path + else + fork_path || project.full_path + end + end + + def vue_ide_link_data(project, ref) + can_collaborate = can_collaborate_with_project?(project) + can_create_mr_from_fork = can?(current_user, :fork_project, project) && can?(current_user, :create_merge_request_in, project) + show_web_ide_button = (can_collaborate || current_user&.already_forked?(project) || can_create_mr_from_fork) + + { + ide_base_path: ide_base_path(project), + needs_to_fork: !can_collaborate && !current_user&.already_forked?(project), + show_web_ide_button: show_web_ide_button, + show_gitpod_button: show_web_ide_button && Gitlab::Gitpod.feature_and_settings_enabled?(project), + gitpod_url: full_gitpod_url(project, ref), + gitpod_enabled: current_user&.gitpod_enabled + } + end + + def full_gitpod_url(project, ref) + return "" unless Gitlab::Gitpod.feature_and_settings_enabled?(project) + + "#{Gitlab::CurrentSettings.gitpod_url}##{project_tree_url(project, tree_join(ref, @path || ''))}" + end + def directory_download_links(project, ref, archive_prefix) Gitlab::Workhorse::ARCHIVE_FORMATS.map do |fmt| { diff --git a/app/helpers/user_callouts_helper.rb b/app/helpers/user_callouts_helper.rb index 98159369fb1..967271a8431 100644 --- a/app/helpers/user_callouts_helper.rb +++ b/app/helpers/user_callouts_helper.rb @@ -5,9 +5,11 @@ module UserCalloutsHelper GKE_CLUSTER_INTEGRATION = 'gke_cluster_integration' GCP_SIGNUP_OFFER = 'gcp_signup_offer' SUGGEST_POPOVER_DISMISSED = 'suggest_popover_dismissed' + SERVICE_TEMPLATES_DEPRECATED = 'service_templates_deprecated' TABS_POSITION_HIGHLIGHT = 'tabs_position_highlight' WEBHOOKS_MOVED = 'webhooks_moved' CUSTOMIZE_HOMEPAGE = 'customize_homepage' + WEB_IDE_ALERT_DISMISSED = 'web_ide_alert_dismissed' def show_admin_integrations_moved? !user_dismissed?(ADMIN_INTEGRATIONS_MOVED) @@ -37,6 +39,10 @@ module UserCalloutsHelper !user_dismissed?(SUGGEST_POPOVER_DISMISSED) end + def show_service_templates_deprecated? + !user_dismissed?(SERVICE_TEMPLATES_DEPRECATED) + end + def show_webhooks_moved_alert? !user_dismissed?(WEBHOOKS_MOVED) end @@ -45,6 +51,10 @@ module UserCalloutsHelper customize_homepage && !user_dismissed?(CUSTOMIZE_HOMEPAGE) end + def show_web_ide_alert? + !user_dismissed?(WEB_IDE_ALERT_DISMISSED) + end + private def user_dismissed?(feature_name, ignore_dismissal_earlier_than = nil) diff --git a/app/helpers/whats_new_helper.rb b/app/helpers/whats_new_helper.rb new file mode 100644 index 00000000000..f0044daa645 --- /dev/null +++ b/app/helpers/whats_new_helper.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module WhatsNewHelper + EMPTY_JSON = ''.to_json + + def whats_new_most_recent_release_items + YAML.load_file(most_recent_release_file_path).to_json + + rescue => e + Gitlab::ErrorTracking.track_exception(e, yaml_file_path: most_recent_release_file_path) + + EMPTY_JSON + end + + private + + def most_recent_release_file_path + Dir.glob(files_path).max + end + + def files_path + Rails.root.join('data', 'whats_new', '*.yml') + end +end diff --git a/app/helpers/wiki_helper.rb b/app/helpers/wiki_helper.rb index ad33ac66f38..8c756b9370b 100644 --- a/app/helpers/wiki_helper.rb +++ b/app/helpers/wiki_helper.rb @@ -69,7 +69,12 @@ module WikiHelper end def wiki_attachment_upload_url - expose_url(api_v4_projects_wikis_attachments_path(id: @wiki.container.id)) + case @wiki.container + when Project + expose_url(api_v4_projects_wikis_attachments_path(id: @wiki.container.id)) + else + raise TypeError, "Unsupported wiki container #{@wiki.container.class}" + end end def wiki_sort_controls(wiki, sort, direction) @@ -147,3 +152,5 @@ module WikiHelper !container.has_confluence? end end + +WikiHelper.prepend_if_ee('EE::WikiHelper') -- cgit v1.2.3