diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-04-20 23:50:22 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-04-20 23:50:22 +0000 |
commit | 9dc93a4519d9d5d7be48ff274127136236a3adb3 (patch) | |
tree | 70467ae3692a0e35e5ea56bcb803eb512a10bedb /app/helpers | |
parent | 4b0f34b6d759d6299322b3a54453e930c6121ff0 (diff) | |
download | gitlab-ce-9dc93a4519d9d5d7be48ff274127136236a3adb3.tar.gz |
Add latest changes from gitlab-org/gitlab@13-11-stable-eev13.11.0-rc43
Diffstat (limited to 'app/helpers')
50 files changed, 513 insertions, 237 deletions
diff --git a/app/helpers/analytics/unique_visits_helper.rb b/app/helpers/analytics/unique_visits_helper.rb index 337a5dc9536..4aa8907f578 100644 --- a/app/helpers/analytics/unique_visits_helper.rb +++ b/app/helpers/analytics/unique_visits_helper.rb @@ -16,7 +16,7 @@ module Analytics def track_visit(target_id) return unless visitor_id - Gitlab::Analytics::UniqueVisits.new.track_visit(visitor_id, target_id) + Gitlab::Analytics::UniqueVisits.new.track_visit(target_id, values: visitor_id) end class_methods do diff --git a/app/helpers/appearances_helper.rb b/app/helpers/appearances_helper.rb index 3ae9f93a27a..65feea4f6e0 100644 --- a/app/helpers/appearances_helper.rb +++ b/app/helpers/appearances_helper.rb @@ -84,3 +84,4 @@ module AppearancesHelper end AppearancesHelper.prepend_if_ee('EE::AppearancesHelper') +AppearancesHelper.prepend_if_jh('JH::AppearancesHelper') diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 9af45daaca4..a2ef2f1207c 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -165,7 +165,7 @@ module ApplicationHelper css_classes = [short_format ? 'js-short-timeago' : 'js-timeago'] css_classes << html_class unless html_class.blank? - element = content_tag :time, l(time, format: "%b %d, %Y"), + content_tag :time, l(time, format: "%b %d, %Y"), class: css_classes.join(' '), title: l(time.to_time.in_time_zone, format: :timeago_tooltip), datetime: time.to_time.getutc.iso8601, @@ -174,8 +174,6 @@ module ApplicationHelper placement: placement, container: 'body' } - - element end def edited_time_ago_with_tooltip(object, placement: 'top', html_class: 'time_ago', exclude_author: false) @@ -194,10 +192,16 @@ module ApplicationHelper end end - def promo_host + # This needs to be used outside of Rails + def self.promo_host 'about.gitlab.com' end + # Convenient method for Rails helper + def promo_host + ApplicationHelper.promo_host + end + def promo_url 'https://' + promo_host end @@ -281,6 +285,7 @@ module ApplicationHelper def page_class class_names = [] class_names << 'issue-boards-page gl-overflow-auto' if current_controller?(:boards) + class_names << 'epic-boards-page' if current_controller?(:epic_boards) class_names << 'environment-logs-page' if current_controller?(:logs) class_names << 'with-performance-bar' if performance_bar_enabled? class_names << system_message_class @@ -405,3 +410,4 @@ module ApplicationHelper end ApplicationHelper.prepend_if_ee('EE::ApplicationHelper') +ApplicationHelper.prepend_if_jh('JH::ApplicationHelper') diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb index 085fbfd08da..504ebb5606e 100644 --- a/app/helpers/application_settings_helper.rb +++ b/app/helpers/application_settings_helper.rb @@ -179,6 +179,7 @@ module ApplicationSettingsHelper def visible_attributes [ :abuse_notification_email, + :admin_mode, :after_sign_out_path, :after_sign_up_text, :akismet_api_key, @@ -228,6 +229,9 @@ module ApplicationSettingsHelper :email_author_in_body, :enabled_git_access_protocol, :enforce_terms, + :external_pipeline_validation_service_timeout, + :external_pipeline_validation_service_token, + :external_pipeline_validation_service_url, :first_day_of_week, :force_pages_access_control, :gitaly_timeout_default, diff --git a/app/helpers/avatars_helper.rb b/app/helpers/avatars_helper.rb index 8d22bda279f..09f91f350bd 100644 --- a/app/helpers/avatars_helper.rb +++ b/app/helpers/avatars_helper.rb @@ -24,11 +24,7 @@ module AvatarsHelper def avatar_icon_for_email(email = nil, size = nil, scale = 2, only_path: true) return gravatar_icon(email, size, scale) if email.nil? - if Feature.enabled?(:avatar_cache_for_email, @current_user, type: :development) - Gitlab::AvatarCache.by_email(email, size, scale, only_path) do - avatar_icon_by_user_email_or_gravatar(email, size, scale, only_path: only_path) - end - else + Gitlab::AvatarCache.by_email(email, size, scale, only_path) do avatar_icon_by_user_email_or_gravatar(email, size, scale, only_path: only_path) end end diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 41bbd0fddd5..3144686bba9 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -41,20 +41,20 @@ module BlobHelper result end - def ide_fork_and_edit_path(project = @project, ref = @ref, path = @path, options = {}) - fork_path_for_current_user(project, ide_edit_path(project, ref, path)) + def ide_fork_and_edit_path(project = @project, ref = @ref, path = @path, with_notice: true) + fork_path_for_current_user(project, ide_edit_path(project, ref, path), with_notice: with_notice) end def fork_and_edit_path(project = @project, ref = @ref, path = @path, options = {}) fork_path_for_current_user(project, edit_blob_path(project, ref, path, options)) end - def fork_path_for_current_user(project, path) + def fork_path_for_current_user(project, path, with_notice: true) return unless current_user project_forks_path(project, namespace_key: current_user.namespace&.id, - continue: edit_blob_fork_params(path)) + continue: edit_blob_fork_params(path, with_notice: with_notice)) end def encode_ide_path(path) @@ -330,12 +330,12 @@ module BlobHelper blob if blob&.readable_text? end - def edit_blob_fork_params(path) + def edit_blob_fork_params(path, with_notice: true) { to: path, - notice: edit_in_new_fork_notice, - notice_now: edit_in_new_fork_notice_now - } + notice: (edit_in_new_fork_notice if with_notice), + notice_now: (edit_in_new_fork_notice_now if with_notice) + }.compact end def edit_modify_file_fork_params(action) diff --git a/app/helpers/boards_helper.rb b/app/helpers/boards_helper.rb index b8c2255ab7e..49963d14934 100644 --- a/app/helpers/boards_helper.rb +++ b/app/helpers/boards_helper.rb @@ -14,7 +14,8 @@ module BoardsHelper root_path: root_path, full_path: full_path, bulk_update_path: @bulk_issues_path, - can_update: (!!can?(current_user, :admin_issue, board)).to_s, + can_update: can_update?.to_s, + can_admin_list: can_admin_list?.to_s, 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, @@ -88,6 +89,14 @@ module BoardsHelper @current_board_parent ||= @group || @project end + def can_update? + can?(current_user, :admin_issue, board) + end + + def can_admin_list? + can?(current_user, :admin_issue_board_list, current_board_parent) + end + def can_admin_issue? can?(current_user, :admin_issue, current_board_parent) end diff --git a/app/helpers/button_helper.rb b/app/helpers/button_helper.rb index ea24f469ffa..1b00f583b55 100644 --- a/app/helpers/button_helper.rb +++ b/app/helpers/button_helper.rb @@ -25,6 +25,7 @@ module ButtonHelper button_text = data[:button_text] || '' hide_tooltip = data[:hide_tooltip] || false hide_button_icon = data[:hide_button_icon] || false + item_prop = data[:itemprop] || nil # This supports code in app/assets/javascripts/copy_to_clipboard.js that # works around ClipboardJS limitations to allow the context-specific copy/pasting of plain text or GFM. @@ -49,7 +50,8 @@ module ButtonHelper data: data, type: :button, title: title, - aria: { label: title } + aria: { label: title }, + itemprop: item_prop } content_tag :button, button_attributes do diff --git a/app/helpers/ci/jobs_helper.rb b/app/helpers/ci/jobs_helper.rb index ec17eccf693..a0d169c1358 100644 --- a/app/helpers/ci/jobs_helper.rb +++ b/app/helpers/ci/jobs_helper.rb @@ -18,6 +18,21 @@ module Ci "retry_outdated_job_docs_url" => help_page_path('ci/pipelines/settings', anchor: 'retry-outdated-jobs') } end + + def job_counts + { + "all" => limited_counter_with_delimiter(@all_builds), + "pending" => limited_counter_with_delimiter(@all_builds.pending), + "running" => limited_counter_with_delimiter(@all_builds.running), + "finished" => limited_counter_with_delimiter(@all_builds.finished) + } + end + + def job_statuses + statuses = Ci::HasStatus::AVAILABLE_STATUSES + + statuses.to_h { |status| [status, status.upcase] } + end end end diff --git a/app/helpers/ci/pipeline_editor_helper.rb b/app/helpers/ci/pipeline_editor_helper.rb index a71b0f4a83a..ceb18d90c92 100644 --- a/app/helpers/ci/pipeline_editor_helper.rb +++ b/app/helpers/ci/pipeline_editor_helper.rb @@ -7,5 +7,23 @@ module Ci def can_view_pipeline_editor?(project) can_collaborate_with_project?(project) end + + def js_pipeline_editor_data(project) + { + "ci-config-path": project.ci_config_path_or_default, + "commit-sha" => project.commit ? project.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'), + "new-merge-request-path" => namespace_project_new_merge_request_path, + "project-path" => project.path, + "project-full-path" => project.full_path, + "project-namespace" => project.namespace.full_path, + "yml-help-page-path" => help_page_path('ci/yaml/README') + } + end end end + +Ci::PipelineEditorHelper.prepend_if_ee('EE::Ci::PipelineEditorHelper') diff --git a/app/helpers/ci/pipelines_helper.rb b/app/helpers/ci/pipelines_helper.rb index 8a6f0821dbb..cabb43f45fd 100644 --- a/app/helpers/ci/pipelines_helper.rb +++ b/app/helpers/ci/pipelines_helper.rb @@ -30,6 +30,46 @@ module Ci project.has_ci? && project.builds_enabled? end + # This list of templates is for the pipeline_empty_state_templates experiment + # and will be cleaned up with https://gitlab.com/gitlab-org/gitlab/-/issues/326299 + def experiment_suggested_ci_templates + [ + { name: 'Android', logo: image_path('illustrations/logos/android.svg') }, + { name: 'Bash', logo: image_path('illustrations/logos/bash.svg') }, + { name: 'C++', logo: image_path('illustrations/logos/c_plus_plus.svg') }, + { name: 'Clojure', logo: image_path('illustrations/logos/clojure.svg') }, + { name: 'Composer', logo: image_path('illustrations/logos/composer.svg') }, + { name: 'Crystal', logo: image_path('illustrations/logos/crystal.svg') }, + { name: 'Dart', logo: image_path('illustrations/logos/dart.svg') }, + { name: 'Django', logo: image_path('illustrations/logos/django.svg') }, + { name: 'Docker', logo: image_path('illustrations/logos/docker.svg') }, + { name: 'Elixir', logo: image_path('illustrations/logos/elixir.svg') }, + { name: 'iOS-Fastlane', logo: image_path('illustrations/logos/fastlane.svg') }, + { name: 'Flutter', logo: image_path('illustrations/logos/flutter.svg') }, + { name: 'Go', logo: image_path('illustrations/logos/go_logo.svg') }, + { 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: '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') }, + { name: 'npm', logo: image_path('illustrations/logos/npm.svg') }, + { name: 'OpenShift', logo: image_path('illustrations/logos/openshift.svg') }, + { name: 'Packer', logo: image_path('illustrations/logos/packer.svg') }, + { name: 'PHP', logo: image_path('illustrations/logos/php.svg') }, + { name: 'Python', logo: image_path('illustrations/logos/python.svg') }, + { name: 'Ruby', logo: image_path('illustrations/logos/ruby.svg') }, + { name: 'Rust', logo: image_path('illustrations/logos/rust.svg') }, + { name: 'Scala', logo: image_path('illustrations/logos/scala.svg') }, + { name: 'Swift', logo: image_path('illustrations/logos/swift.svg') }, + { name: 'Terraform', logo: image_path('illustrations/logos/terraform.svg') } + ] + end + private def warning_markdown(pipeline) diff --git a/app/helpers/ci/runners_helper.rb b/app/helpers/ci/runners_helper.rb index ba5d4e8c65a..82347053d6f 100644 --- a/app/helpers/ci/runners_helper.rb +++ b/app/helpers/ci/runners_helper.rb @@ -4,18 +4,33 @@ module Ci module RunnersHelper include IconsHelper - def runner_status_icon(runner) + def runner_status_icon(runner, size: 16, icon_class: '') status = runner.status + + title = '' + icon = 'warning-solid' + span_class = '' + case status when :not_connected - content_tag(:span, title: _("New runner. Has not connected yet")) do - sprite_icon("warning-solid", size: 24, css_class: "gl-vertical-align-bottom!") - end + title = s_("Runners|New runner, has not connected yet") + icon = 'warning-solid' + when :online + title = s_("Runners|Runner is online, last contact was %{runner_contact} ago") % { runner_contact: time_ago_in_words(runner.contacted_at) } + icon = 'status-active' + span_class = 'gl-text-green-500' + when :offline + title = s_("Runners|Runner is offline, last contact was %{runner_contact} ago") % { runner_contact: time_ago_in_words(runner.contacted_at) } + icon = 'status-failed' + span_class = 'gl-text-red-500' + when :paused + title = s_("Runners|Runner is paused, last contact was %{runner_contact} ago") % { runner_contact: time_ago_in_words(runner.contacted_at) } + icon = 'status-paused' + span_class = 'gl-text-gray-600' + end - when :online, :offline, :paused - content_tag :span, nil, - class: "gl-display-inline-block gl-avatar gl-avatar-s16 gl-avatar-circle runner-status runner-status-#{status}", - title: _("Runner is %{status}, last contact was %{runner_contact} ago") % { status: status, runner_contact: time_ago_in_words(runner.contacted_at) } + content_tag(:span, class: span_class, title: title, data: { toggle: 'tooltip', container: 'body', testid: 'runner_status_icon', qa_selector: "runner_status_#{status}_content" }) do + sprite_icon(icon, size: size, css_class: icon_class) end end diff --git a/app/helpers/clusters_helper.rb b/app/helpers/clusters_helper.rb index cc633df77f9..439628f40c6 100644 --- a/app/helpers/clusters_helper.rb +++ b/app/helpers/clusters_helper.rb @@ -71,6 +71,8 @@ module ClustersHelper render_if_exists 'clusters/clusters/health' when 'apps' render 'applications' + when 'integrations' + render 'integrations' when 'settings' render 'advanced_settings_container' else diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index 42e4844cc8d..e7a81eb5629 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -110,16 +110,18 @@ module CommitsHelper end end - def revert_commit_link - return unless current_user - - tag(:div, data: { display_text: 'Revert' }, class: "js-revert-commit-trigger") - end - - def cherry_pick_commit_link - return unless current_user - - tag(:div, data: { display_text: 'Cherry-pick' }, class: "js-cherry-pick-commit-trigger") + def commit_options_dropdown_data(project, commit) + can_collaborate = current_user && can_collaborate_with_project?(project) + + { + new_project_tag_path: new_project_tag_path(project, ref: commit), + email_patches_path: project_commit_path(project, commit, format: :patch), + plain_diff_path: project_commit_path(project, commit, format: :diff), + can_revert: "#{can_collaborate && !commit.has_been_reverted?(current_user)}", + can_cherry_pick: can_collaborate.to_s, + can_tag: can?(current_user, :push_code, project).to_s, + can_email_patches: (commit.parents.length < 2).to_s + } end def commit_signature_badge_classes(additional_classes) @@ -137,7 +139,7 @@ module CommitsHelper def cherry_pick_projects_data(project) return [] unless Feature.enabled?(:pick_into_project, project, default_enabled: :yaml) - target_projects(project).map do |project| + [project, project.forked_from_project].compact.map do |project| { id: project.id.to_s, name: project.full_path, diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb index 10c7b4032cf..7bf3cb6230b 100644 --- a/app/helpers/diff_helper.rb +++ b/app/helpers/diff_helper.rb @@ -190,12 +190,8 @@ module DiffHelper def render_overflow_warning?(diffs_collection) diff_files = diffs_collection.raw_diff_files - if diff_files.any?(&:too_large?) - Gitlab::Metrics.add_event(:diffs_overflow_single_file_limits) - end - diff_files.overflow?.tap do |overflown| - Gitlab::Metrics.add_event(:diffs_overflow_collection_limits) if overflown + log_overflow_limits(diff_files) end end @@ -286,4 +282,18 @@ module DiffHelper conflicts_service.conflicts.files.index_by(&:our_path) end + + def log_overflow_limits(diff_files) + if diff_files.any?(&:too_large?) + Gitlab::Metrics.add_event(:diffs_overflow_single_file_limits) + end + + Gitlab::Metrics.add_event(:diffs_overflow_collection_limits) if diff_files.overflow? + Gitlab::Metrics.add_event(:diffs_overflow_max_bytes_limits) if diff_files.overflow_max_bytes? + Gitlab::Metrics.add_event(:diffs_overflow_max_files_limits) if diff_files.overflow_max_files? + Gitlab::Metrics.add_event(:diffs_overflow_max_lines_limits) if diff_files.overflow_max_lines? + Gitlab::Metrics.add_event(:diffs_overflow_collapsed_bytes_limits) if diff_files.collapsed_safe_bytes? + Gitlab::Metrics.add_event(:diffs_overflow_collapsed_files_limits) if diff_files.collapsed_safe_files? + Gitlab::Metrics.add_event(:diffs_overflow_collapsed_lines_limits) if diff_files.collapsed_safe_lines? + end end diff --git a/app/helpers/dropdowns_helper.rb b/app/helpers/dropdowns_helper.rb index 45f5281b515..c2f7fa2074c 100644 --- a/app/helpers/dropdowns_helper.rb +++ b/app/helpers/dropdowns_helper.rb @@ -29,7 +29,7 @@ module DropdownsHelper output << dropdown_filter(options[:placeholder]) end - output << content_tag(:div, class: "dropdown-content #{options[:content_class] if options.key?(:content_class)}") do + output << content_tag(:div, data: { qa_selector: "dropdown_list_content" }, class: "dropdown-content #{options[:content_class] if options.key?(:content_class)}") do capture(&block) if block && !options.key?(:footer_content) end @@ -102,7 +102,7 @@ module DropdownsHelper 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 = search_field_tag search_id, nil, data: { qa_selector: "dropdown_input_field" }, class: "dropdown-input-field", placeholder: placeholder, autocomplete: 'off' filter_output << sprite_icon('search', css_class: 'dropdown-input-search') filter_output << sprite_icon('close', size: 16, css_class: 'dropdown-input-clear js-dropdown-input-clear') diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb index 52b8ac915f1..6b3abb4274e 100644 --- a/app/helpers/events_helper.rb +++ b/app/helpers/events_helper.rb @@ -273,7 +273,7 @@ module EventsHelper def event_user_info(event) content_tag(:div, class: "event-user-info") do - concat content_tag(:span, link_to_author(event), class: "author_name") + concat content_tag(:span, link_to_author(event), class: "author-name") concat " ".html_safe concat content_tag(:span, event.author.to_reference, class: "username") end diff --git a/app/helpers/git_helper.rb b/app/helpers/git_helper.rb index 0fb37a69e56..f7c511cdc47 100644 --- a/app/helpers/git_helper.rb +++ b/app/helpers/git_helper.rb @@ -4,8 +4,7 @@ module GitHelper def strip_signature(text) text = text.gsub(/-----BEGIN PGP SIGNATURE-----(.*)-----END PGP SIGNATURE-----/m, "") text = text.gsub(/-----BEGIN PGP MESSAGE-----(.*)-----END PGP MESSAGE-----/m, "") - text = text.gsub(/-----BEGIN SIGNED MESSAGE-----(.*)-----END SIGNED MESSAGE-----/m, "") - text + text.gsub(/-----BEGIN SIGNED MESSAGE-----(.*)-----END SIGNED MESSAGE-----/m, "") end def short_sha(text) diff --git a/app/helpers/gitlab_routing_helper.rb b/app/helpers/gitlab_routing_helper.rb index 6dcdc018a20..48af4793fb0 100644 --- a/app/helpers/gitlab_routing_helper.rb +++ b/app/helpers/gitlab_routing_helper.rb @@ -4,6 +4,8 @@ module GitlabRoutingHelper extend ActiveSupport::Concern + include ::ProjectsHelper + include ::ApplicationSettingsHelper include API::Helpers::RelatedResourcesHelpers included do Gitlab::Routing.includes_helpers(self) diff --git a/app/helpers/graph_helper.rb b/app/helpers/graph_helper.rb index abb3c5a7af8..bcbc67957eb 100644 --- a/app/helpers/graph_helper.rb +++ b/app/helpers/graph_helper.rb @@ -23,7 +23,7 @@ module GraphHelper ratio.to_i end - def should_render_deployment_frequency_charts + def should_render_dora_charts false end end diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb index 62f0c68b0c8..5ce23baa226 100644 --- a/app/helpers/groups_helper.rb +++ b/app/helpers/groups_helper.rb @@ -22,6 +22,9 @@ module GroupsHelper ldap_group_links#index hooks#index pipeline_quota#index + applications#index + applications#show + applications#edit packages_and_registries#index ] end @@ -62,14 +65,6 @@ module GroupsHelper can?(current_user, :set_emails_disabled, group) && !group.parent&.emails_disabled? end - def group_open_issues_count(group) - if Feature.enabled?(:cached_sidebar_open_issues_count, group, default_enabled: :yaml) - cached_open_group_issues_count(group) - else - number_with_delimiter(group_issues_count(state: 'opened')) - end - end - def group_issues_count(state:) IssuesFinder .new(current_user, group_id: @group.id, state: state, non_archived: true, include_subgroups: true) @@ -77,18 +72,11 @@ module GroupsHelper .count end - def cached_open_group_issues_count(group) - count_service = Groups::OpenIssuesCountService - issues_count = count_service.new(group, current_user).count - - if issues_count > count_service::CACHED_COUNT_THRESHOLD - ActiveSupport::NumberHelper - .number_to_human( - issues_count, - units: { thousand: 'k', million: 'm' }, precision: 1, significant: false, format: '%n%u' - ) + def group_open_merge_requests_count(group) + if Feature.enabled?(:cached_sidebar_merge_requests_count, group, default_enabled: :yaml) + cached_issuables_count(@group, type: :merge_requests) else - number_with_delimiter(issues_count) + number_with_delimiter(group_merge_requests_count(state: 'opened')) end end @@ -99,6 +87,14 @@ module GroupsHelper .count end + def cached_issuables_count(group, type: nil) + count_service = issuables_count_service_class(type) + return unless count_service.present? + + issuables_count = count_service.new(group, current_user).count + format_issuables_count(count_service, issuables_count) + end + def group_dependency_proxy_url(group) # The namespace path can include uppercase letters, which # Docker doesn't allow. The proxy expects it to be downcased. @@ -117,7 +113,9 @@ module GroupsHelper @has_group_title = true full_title = [] - group.ancestors.reverse.each_with_index do |parent, index| + ancestors = group.ancestors.with_route + + ancestors.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 @@ -214,6 +212,10 @@ module GroupsHelper !multiple_members?(group) end + def render_setting_to_allow_project_access_token_creation?(group) + group.root? && current_user.can?(:admin_setting_to_allow_project_access_token_creation, group) + end + def show_thanks_for_purchase_banner? params.key?(:purchased_quantity) && params[:purchased_quantity].to_i > 0 end @@ -303,6 +305,26 @@ module GroupsHelper def ancestor_locked_and_has_been_overridden(group) s_("GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup.").html_safe % { ancestor_group: ancestor_group(group) } end + + def issuables_count_service_class(type) + if type == :issues + Groups::OpenIssuesCountService + elsif type == :merge_requests + Groups::MergeRequestsCountService + end + end + + def format_issuables_count(count_service, count) + if count > count_service::CACHED_COUNT_THRESHOLD + ActiveSupport::NumberHelper + .number_to_human( + count, + units: { thousand: 'k', million: 'm' }, precision: 1, significant: false, format: '%n%u' + ) + else + number_with_delimiter(count) + end + end end GroupsHelper.prepend_if_ee('EE::GroupsHelper') diff --git a/app/helpers/ide_helper.rb b/app/helpers/ide_helper.rb index 5f0d513ba35..61d8d0f779d 100644 --- a/app/helpers/ide_helper.rb +++ b/app/helpers/ide_helper.rb @@ -16,7 +16,7 @@ module IdeHelper 'branch-name' => @branch, 'file-path' => @path, 'merge-request' => @merge_request, - 'forked-project' => convert_to_project_entity_json(@forked_project), + 'fork-info' => @fork_info&.to_json, 'project' => convert_to_project_entity_json(@project) } end diff --git a/app/helpers/in_product_marketing_helper.rb b/app/helpers/in_product_marketing_helper.rb index 061404e989d..2574b57a82e 100644 --- a/app/helpers/in_product_marketing_helper.rb +++ b/app/helpers/in_product_marketing_helper.rb @@ -49,7 +49,7 @@ module InProductMarketingHelper 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 Gold free trial') + s_('InProductMarketing|Start with a GitLab Ultimate free trial') ], team: [ s_('InProductMarketing|Invite your colleagues to join in less than one minute'), @@ -97,8 +97,8 @@ module InProductMarketingHelper s_('InProductMarketing|Follow our steps') ], trial: [ - s_('InProductMarketing|...and you can get a free trial of GitLab Gold'), - s_('InProductMarketing|Try GitLab Gold for free'), + 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: [ @@ -142,7 +142,7 @@ module InProductMarketingHelper 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 Gold and your teams will be on it from day one."), + 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([ @@ -172,9 +172,9 @@ module InProductMarketingHelper nil ], trial: [ - s_('InProductMarketing|Start a GitLab Gold trial today in less than one minute, no credit card required.'), - s_('InProductMarketing|Get started today with a 30-day GitLab Gold 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 Gold and enable these features in less than 5 minutes with no credit card required.') + 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.'), @@ -250,7 +250,7 @@ module InProductMarketingHelper trial: [ s_('InProductMarketing|Start a trial'), s_('InProductMarketing|Beef up your security'), - s_('InProductMarketing|Go for the gold!') + s_('InProductMarketing|Start your trial now!') ], team: [ s_('InProductMarketing|Invite your colleagues today'), @@ -313,7 +313,8 @@ module InProductMarketingHelper end def unsubscribe_link(format) - link(s_('InProductMarketing|unsubscribe'), '%tag_unsubscribe_url%', format) + unsubscribe_url = Gitlab.com? ? '%tag_unsubscribe_url%' : profile_notifications_url + link(s_('InProductMarketing|unsubscribe'), unsubscribe_url, format) end def link(text, link, format) diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index 639a54fa9ec..8ebc773bb25 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -20,13 +20,13 @@ module IssuablesHelper end def assignees_label(issuable, include_value: true) - label = 'Assignee'.pluralize(issuable.assignees.count) + assignees = issuable.assignees if include_value sanitized_list = sanitize_name(issuable.assignee_list) - "#{label}: #{sanitized_list}" + ns_('NotificationEmail|Assignee: %{users}', 'NotificationEmail|Assignees: %{users}', assignees.count) % { users: sanitized_list } else - label + ns_('NotificationEmail|Assignee', 'NotificationEmail|Assignees', assignees.count) end end @@ -389,7 +389,8 @@ module IssuablesHelper severity: issuable[:severity], timeTrackingLimitToHours: Gitlab::CurrentSettings.time_tracking_limit_to_hours, createNoteEmail: issuable[:create_note_email], - issuableType: issuable[:type] + issuableType: issuable[:type], + projectMembersPath: project_project_members_path(@project, sort: :access_level_desc) } end diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 0a9965496b8..0a83e707412 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -163,10 +163,41 @@ module IssuesHelper } end + def issues_list_data(project, current_user, finder) + { + 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, + 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, + 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'), + 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), + 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 + } + end + # Overridden in EE def scoped_labels_available?(parent) false end + + def award_emoji_issue_api_path(issue) + if Feature.enabled?(:improved_emoji_picker, issue.project, default_enabled: :yaml) + api_v4_projects_issues_award_emoji_path(id: issue.project.id, issue_iid: issue.iid) + end + end end IssuesHelper.prepend_if_ee('EE::IssuesHelper') diff --git a/app/helpers/jira_connect_helper.rb b/app/helpers/jira_connect_helper.rb index 76a7f785df6..475469a6df9 100644 --- a/app/helpers/jira_connect_helper.rb +++ b/app/helpers/jira_connect_helper.rb @@ -6,8 +6,24 @@ module JiraConnectHelper { groups_path: api_v4_groups_path(params: { min_access_level: Gitlab::Access::MAINTAINER, skip_groups: skip_groups }), + subscriptions: subscriptions.map { |s| serialize_subscription(s) }.to_json, subscriptions_path: jira_connect_subscriptions_path, users_path: current_user ? nil : jira_connect_users_path } end + + private + + def serialize_subscription(subscription) + { + group: { + name: subscription.namespace.name, + avatar_url: subscription.namespace.avatar_url, + full_name: subscription.namespace.full_name, + description: subscription.namespace.description + }, + created_at: subscription.created_at, + unlink_path: jira_connect_subscription_path(subscription) + } + end end diff --git a/app/helpers/learn_gitlab_helper.rb b/app/helpers/learn_gitlab_helper.rb index f50c1e52bed..81896fb9fa4 100644 --- a/app/helpers/learn_gitlab_helper.rb +++ b/app/helpers/learn_gitlab_helper.rb @@ -11,19 +11,20 @@ module LearnGitlabHelper def onboarding_actions_data(project) attributes = onboarding_progress(project).attributes.symbolize_keys - action_urls.map do |action, url| + action_urls.to_h do |action, url| [ action, url: url, completed: attributes[OnboardingProgress.column_name(action)].present?, svg: image_path("learn_gitlab/#{action}.svg") ] - end.to_h + end end private ACTION_ISSUE_IDS = { + issue_created: 4, git_write: 6, pipeline_created: 7, merge_request_created: 9, diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb index 7a798c83b7e..df7fcb0f3da 100644 --- a/app/helpers/merge_requests_helper.rb +++ b/app/helpers/merge_requests_helper.rb @@ -176,13 +176,39 @@ module MergeRequestsHelper def reviewers_label(merge_request, include_value: true) reviewers = merge_request.reviewers - reviewer_label = 'Reviewer'.pluralize(reviewers.count) if include_value sanitized_list = sanitize_name(reviewers.map(&:name).to_sentence) - "#{reviewer_label}: #{sanitized_list}" + ns_('NotificationEmail|Reviewer: %{users}', 'NotificationEmail|Reviewers: %{users}', reviewers.count) % { users: sanitized_list } else - reviewer_label + ns_('NotificationEmail|Reviewer', 'NotificationEmail|Reviewers', reviewers.count) + end + end + + def diffs_tab_pane_data(project, merge_request, params) + { + "is-locked": merge_request.discussion_locked?, + endpoint: diffs_project_merge_request_path(project, merge_request, 'json', params), + endpoint_metadata: @endpoint_metadata_url, + endpoint_batch: diffs_batch_project_json_merge_request_path(project, merge_request, 'json', params), + endpoint_coverage: @coverage_path, + help_page_path: help_page_path('user/discussions/index.md', anchor: 'suggest-changes'), + current_user_data: @current_user_data, + update_current_user_path: @update_current_user_path, + project_path: project_path(merge_request.project), + changes_empty_state_illustration: image_path('illustrations/merge_request_changes_empty.svg'), + is_fluid_layout: fluid_layout.to_s, + dismiss_endpoint: user_callouts_path, + show_suggest_popover: show_suggest_popover?.to_s, + show_whitespace_default: @show_whitespace_default.to_s, + file_by_file_default: @file_by_file_default.to_s, + default_suggestion_commit_message: default_suggestion_commit_message + } + end + + def award_emoji_merge_request_api_path(merge_request) + if Feature.enabled?(:improved_emoji_picker, merge_request.project, default_enabled: :yaml) + api_v4_projects_merge_requests_award_emoji_path(id: merge_request.project.id, merge_request_iid: merge_request.iid) end end diff --git a/app/helpers/namespaces_helper.rb b/app/helpers/namespaces_helper.rb index 8cf5cd49322..a4521541bf9 100644 --- a/app/helpers/namespaces_helper.rb +++ b/app/helpers/namespaces_helper.rb @@ -56,6 +56,33 @@ module NamespacesHelper namespaces_options(selected, **options) end + def cascading_namespace_settings_enabled? + NamespaceSetting.cascading_settings_feature_enabled? + end + + def cascading_namespace_settings_popover_data(attribute, group, settings_path_helper) + locked_by_ancestor = group.namespace_settings.public_send("#{attribute}_locked_by_ancestor?") # rubocop:disable GitlabSecurity/PublicSend + + popover_data = { + locked_by_application_setting: group.namespace_settings.public_send("#{attribute}_locked_by_application_setting?"), # rubocop:disable GitlabSecurity/PublicSend + locked_by_ancestor: locked_by_ancestor + } + + if locked_by_ancestor + ancestor_namespace = group.namespace_settings.public_send("#{attribute}_locked_ancestor").namespace # rubocop:disable GitlabSecurity/PublicSend + + popover_data[:ancestor_namespace] = { + full_name: ancestor_namespace.full_name, + path: settings_path_helper.call(ancestor_namespace) + } + end + + { + popover_data: popover_data.to_json, + testid: 'cascading-settings-lock-icon' + } + end + private # Many importers create a temporary Group, so use the real diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb index c170e58b4ce..db144f63f92 100644 --- a/app/helpers/nav_helper.rb +++ b/app/helpers/nav_helper.rb @@ -92,10 +92,8 @@ module NavHelper links << :admin_impersonation end - if Feature.enabled?(:user_mode_in_session) - if current_user_mode.admin_mode? - links << :admin_mode - end + if Gitlab::CurrentSettings.admin_mode && current_user_mode.admin_mode? + links << :admin_mode end links diff --git a/app/helpers/packages_helper.rb b/app/helpers/packages_helper.rb index 8f365fd0786..04465f7798c 100644 --- a/app/helpers/packages_helper.rb +++ b/app/helpers/packages_helper.rb @@ -50,6 +50,7 @@ module PackagesHelper def track_package_event(event_name, scope, **args) ::Packages::CreateEventService.new(nil, current_user, event_name: event_name, scope: scope).execute - track_event(event_name, **args) + category = args.delete(:category) || self.class.name + ::Gitlab::Tracking.event(category, event_name.to_s, **args) end end diff --git a/app/helpers/page_layout_helper.rb b/app/helpers/page_layout_helper.rb index 8920133734c..6997c8cffda 100644 --- a/app/helpers/page_layout_helper.rb +++ b/app/helpers/page_layout_helper.rb @@ -159,13 +159,20 @@ module PageLayoutHelper end def user_status_properties(user) - 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 } + 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 + } + return default_properties unless user&.status default_properties.merge({ current_emoji: user.status.emoji.to_s, current_message: user.status.message.to_s, - current_availability: user.status.availability.to_s + current_availability: user.status.availability.to_s, + current_clear_status_after: user.status.clear_status_at.to_s }) end diff --git a/app/helpers/preferences_helper.rb b/app/helpers/preferences_helper.rb index 12bc509466e..add6e1eaf6f 100644 --- a/app/helpers/preferences_helper.rb +++ b/app/helpers/preferences_helper.rb @@ -33,7 +33,7 @@ module PreferencesHelper groups: _("Your Groups"), todos: _("Your To-Do List"), issues: _("Assigned Issues"), - merge_requests: _("Assigned Merge Requests"), + merge_requests: _("Assigned merge requests"), operations: _("Operations Dashboard") }.with_indifferent_access.freeze end diff --git a/app/helpers/profiles_helper.rb b/app/helpers/profiles_helper.rb index 87187e97df4..3219620de71 100644 --- a/app/helpers/profiles_helper.rb +++ b/app/helpers/profiles_helper.rb @@ -37,4 +37,18 @@ module ProfilesHelper def user_status_set_to_busy?(status) status&.availability == availability_values[:busy] end + + # Overridden in EE::ProfilesHelper#ssh_key_expiration_tooltip + def ssh_key_expiration_tooltip(key) + return key.errors.full_messages.join(', ') if key.errors.full_messages.any? + + s_('Profiles|Key usable beyond expiration date.') if key.expired? + end + + # Overridden in EE::ProfilesHelper#ssh_key_expires_field_description + def ssh_key_expires_field_description + s_('Profiles|Key can still be used after expiration.') + end end + +ProfilesHelper.prepend_ee_mod diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 6c17039a5d9..4be6cd4276b 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -410,6 +410,10 @@ module ProjectsHelper 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 @@ -725,14 +729,6 @@ module ProjectsHelper ] end - def sidebar_projects_paths - %w[ - projects#show - projects#activity - releases#index - ] - end - def sidebar_settings_paths %w[ projects#edit @@ -750,25 +746,6 @@ module ProjectsHelper ] end - def sidebar_repository_paths - %w[ - tree - blob - blame - edit_tree - new_tree - find_file - commit - commits - compare - projects/repositories - tags - branches - graphs - network - ] - end - def sidebar_operations_paths %w[ environments @@ -809,10 +786,6 @@ module ProjectsHelper can?(current_user, :destroy_container_image, project) end - def project_access_token_available?(project) - can?(current_user, :admin_resource_access_tokens, project) - end - def build_project_breadcrumb_link(project) project_name = simple_sanitize(project.name) diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb index 86012352c8b..2568917bafc 100644 --- a/app/helpers/search_helper.rb +++ b/app/helpers/search_helper.rb @@ -193,7 +193,7 @@ module SearchHelper { category: "In this project", label: _("Network"), url: project_network_path(@project, ref) }, { category: "In this project", label: _("Graph"), url: project_graph_path(@project, ref) }, { category: "In this project", label: _("Issues"), url: project_issues_path(@project) }, - { category: "In this project", label: _("Merge Requests"), url: project_merge_requests_path(@project) }, + { category: "In this project", label: _("Merge requests"), url: project_merge_requests_path(@project) }, { category: "In this project", label: _("Milestones"), url: project_milestones_path(@project) }, { category: "In this project", label: _("Snippets"), url: project_snippets_path(@project) }, { category: "In this project", label: _("Members"), url: project_project_members_path(@project) }, diff --git a/app/helpers/services_helper.rb b/app/helpers/services_helper.rb index 14d20e7c622..ffa09cb12fb 100644 --- a/app/helpers/services_helper.rb +++ b/app/helpers/services_helper.rb @@ -4,29 +4,29 @@ module ServicesHelper def service_event_description(event) case event when "push", "push_events" - s_("ProjectService|Event will be triggered by a push to the repository") + s_("ProjectService|Trigger event for pushes to the repository.") when "tag_push", "tag_push_events" - s_("ProjectService|Event will be triggered when a new tag is pushed to the repository") + s_("ProjectService|Trigger event for new tags pushed to the repository.") when "note", "note_events" - s_("ProjectService|Event will be triggered when someone adds a comment") + s_("ProjectService|Trigger event for new comments.") when "confidential_note", "confidential_note_events" - s_("ProjectService|Event will be triggered when someone adds a comment on a confidential issue") + s_("ProjectService|Trigger event for new comments on confidential issues.") when "issue", "issue_events" - s_("ProjectService|Event will be triggered when an issue is created/updated/closed") + s_("ProjectService|Trigger event when an issue is created, updated, or closed.") when "confidential_issue", "confidential_issue_events" - s_("ProjectService|Event will be triggered when a confidential issue is created/updated/closed") + s_("ProjectService|Trigger event when a confidential issue is created, updated, or closed.") when "merge_request", "merge_request_events" - s_("ProjectService|Event will be triggered when a merge request is created/updated/merged") + s_("ProjectService|Trigger event when a merge request is created, updated, or merged.") when "pipeline", "pipeline_events" - s_("ProjectService|Event will be triggered when a pipeline status changes") + s_("ProjectService|Trigger event when a pipeline status changes.") when "wiki_page", "wiki_page_events" - s_("ProjectService|Event will be triggered when a wiki page is created/updated") + s_("ProjectService|Trigger event when a wiki page is created or updated.") when "commit", "commit_events" - s_("ProjectService|Event will be triggered when a commit is created/updated") + s_("ProjectService|Trigger event when a commit is created or updated.") when "deployment" - s_("ProjectService|Event will be triggered when a deployment starts or finishes") + s_("ProjectService|Trigger event when a deployment starts or finishes.") when "alert" - s_("ProjectService|Event will be triggered when a new, unique alert is recorded") + s_("ProjectService|Trigger event when a new, unique alert is recorded.") end end @@ -86,7 +86,7 @@ module ServicesHelper end def integration_form_data(integration, group: nil) - { + form_data = { id: integration.id, show_active: integration.show_active_box?.to_s, activated: (integration.active || integration.new_record?).to_s, @@ -106,6 +106,19 @@ module ServicesHelper test_path: scoped_test_integration_path(integration), reset_path: scoped_reset_integration_path(integration, group: group) } + + if integration.is_a?(JiraService) + form_data[:jira_issue_transition_automatic] = integration.jira_issue_transition_automatic + form_data[:jira_issue_transition_id] = integration.jira_issue_transition_id + end + + form_data + end + + def integration_list_data(integrations) + { + integrations: integrations.map { |i| serialize_integration(i) }.to_json + } end def trigger_events_for_service(integration) @@ -148,6 +161,17 @@ module ServicesHelper 'project' end end + + def serialize_integration(integration) + { + active: integration.operating?, + title: integration.title, + description: integration.description, + updated_at: integration.updated_at, + edit_path: scoped_edit_integration_path(integration), + name: integration.to_param + } + end end ServicesHelper.prepend_if_ee('EE::ServicesHelper') diff --git a/app/helpers/sidebars_helper.rb b/app/helpers/sidebars_helper.rb new file mode 100644 index 00000000000..31dfe21671a --- /dev/null +++ b/app/helpers/sidebars_helper.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +module SidebarsHelper + def sidebar_tracking_attributes_by_object(object) + case object + when Project + sidebar_project_tracking_attrs + when Group + sidebar_group_tracking_attrs + when User + sidebar_user_profile_tracking_attrs + else + {} + end + end + + def project_sidebar_context(project, user, current_ref) + context_data = project_sidebar_context_data(project, user, current_ref) + + Sidebars::Projects::Context.new(**context_data) + end + + private + + def sidebar_project_tracking_attrs + tracking_attrs('projects_side_navigation', 'render', 'projects_side_navigation') + end + + def sidebar_group_tracking_attrs + tracking_attrs('groups_side_navigation', 'render', 'groups_side_navigation') + end + + def sidebar_user_profile_tracking_attrs + tracking_attrs('user_side_navigation', 'render', 'user_side_navigation') + end + + def project_sidebar_context_data(project, user, current_ref) + { + current_user: user, + container: project, + learn_gitlab_experiment_enabled: learn_gitlab_experiment_enabled?(project), + current_ref: current_ref + } + end +end diff --git a/app/helpers/snippets_helper.rb b/app/helpers/snippets_helper.rb index 36f4fced147..f4af7a5a350 100644 --- a/app/helpers/snippets_helper.rb +++ b/app/helpers/snippets_helper.rb @@ -11,16 +11,6 @@ module SnippetsHelper end end - def download_raw_snippet_button(snippet) - link_to(sprite_icon('download'), - gitlab_raw_snippet_path(snippet, inline: false), - target: '_blank', - rel: 'noopener noreferrer', - class: "btn btn-sm has-tooltip", - title: 'Download', - data: { container: 'body' }) - end - # Return the path of a snippets index for a user or for a project # # @returns String, path to snippet index @@ -54,7 +44,7 @@ module SnippetsHelper link_to(external_snippet_icon('doc-code'), gitlab_raw_snippet_blob_url(snippet, blob.path), - class: 'btn', + class: 'gl-button btn btn-default', target: '_blank', rel: 'noopener noreferrer', title: 'Open raw') @@ -63,7 +53,7 @@ module SnippetsHelper def embedded_snippet_download_button(snippet, blob) link_to(external_snippet_icon('download'), gitlab_raw_snippet_blob_url(snippet, blob.path, nil, inline: false), - class: 'btn', + class: 'gl-button btn btn-default', target: '_blank', title: 'Download', rel: 'noopener noreferrer') diff --git a/app/helpers/sorting_helper.rb b/app/helpers/sorting_helper.rb index 35c8b140bfe..974ec046bbb 100644 --- a/app/helpers/sorting_helper.rb +++ b/app/helpers/sorting_helper.rb @@ -231,7 +231,7 @@ module SortingHelper end def sort_direction_button(reverse_url, reverse_sort, sort_value) - link_class = 'btn btn-default has-tooltip reverse-sort-btn qa-reverse-sort rspec-reverse-sort' + link_class = 'gl-button btn btn-default btn-icon has-tooltip reverse-sort-btn qa-reverse-sort rspec-reverse-sort' icon = sort_direction_icon(sort_value) url = reverse_url diff --git a/app/helpers/submodule_helper.rb b/app/helpers/submodule_helper.rb index 959178c47d7..f1e0be3a622 100644 --- a/app/helpers/submodule_helper.rb +++ b/app/helpers/submodule_helper.rb @@ -18,7 +18,8 @@ module SubmoduleHelper end if url =~ %r{([^/:]+)/([^/]+(?:\.git)?)\Z} - namespace, project = Regexp.last_match(1), Regexp.last_match(2) + namespace = Regexp.last_match(1) + project = Regexp.last_match(2) gitlab_hosts = [Gitlab.config.gitlab.url, Gitlab.config.gitlab_shell.ssh_path_prefix] diff --git a/app/helpers/tab_helper.rb b/app/helpers/tab_helper.rb index e81986d4453..1d3242ca44a 100644 --- a/app/helpers/tab_helper.rb +++ b/app/helpers/tab_helper.rb @@ -65,6 +65,13 @@ module TabHelper # # When `TreeController#index` is requested # # => '<li>Hello</li>' # + # # Paths, controller and actions can be used at the same time + # nav_link(path: 'tree#show', controller: 'admin/appearances') { "Hello" } + # + # nav_link(path: 'foo#bar', controller: 'tree') { "Hello" } + # nav_link(path: 'foo#bar', controller: 'tree', action: 'show') { "Hello" } + # nav_link(path: 'foo#bar', action: 'show') { "Hello" } + # # Returns a list item element String def nav_link(options = {}, &block) klass = active_nav_link?(options) ? 'active' : '' @@ -85,34 +92,12 @@ module TabHelper def active_nav_link?(options) return false if options[:unless]&.call - if path = options.delete(:path) - unless path.respond_to?(:each) - path = [path] - end - - path.any? do |single_path| - current_path?(single_path) - end - elsif page = options.delete(:page) - unless page.respond_to?(:each) - page = [page] - end - - page.any? do |single_page| - current_page?(single_page) - end - else - c = options.delete(:controller) - a = options.delete(:action) + controller = options.delete(:controller) + action = options.delete(:action) - if c && a - # When given both options, make sure BOTH are true - current_controller?(*c) && current_action?(*a) - else - # Otherwise check EITHER option - current_controller?(*c) || current_action?(*a) - end - end + route_matches_paths?(options.delete(:path)) || + route_matches_pages?(options.delete(:page)) || + route_matches_controllers_and_or_actions?(controller, action) end def current_path?(path) @@ -127,4 +112,26 @@ module TabHelper 'active' end end + + private + + def route_matches_paths?(paths) + Array(paths).compact.any? do |single_path| + current_path?(single_path) + end + end + + def route_matches_pages?(pages) + Array(pages).compact.any? do |single_page| + current_page?(single_page) + end + end + + def route_matches_controllers_and_or_actions?(controller, action) + if controller && action + current_controller?(*controller) && current_action?(*action) + else + current_controller?(*controller) || current_action?(*action) + end + end end diff --git a/app/helpers/timeboxes_helper.rb b/app/helpers/timeboxes_helper.rb index bbf8cf7dac3..e034a985b50 100644 --- a/app/helpers/timeboxes_helper.rb +++ b/app/helpers/timeboxes_helper.rb @@ -115,17 +115,6 @@ module TimeboxesHelper end end - def milestones_filter_dropdown_path - project = @target_project || @project - if project - project_milestones_path(project, :json) - elsif @group - group_milestones_path(@group, :json) - else - dashboard_milestones_path(:json) - end - end - def milestone_time_for(date, date_type) title = date_type == :start ? "Start date" : "End date" diff --git a/app/helpers/todos_helper.rb b/app/helpers/todos_helper.rb index 7b0e0df8998..e9a0fef06c4 100644 --- a/app/helpers/todos_helper.rb +++ b/app/helpers/todos_helper.rb @@ -157,21 +157,11 @@ module TodosHelper ] end - def todo_projects_options - projects = current_user.authorized_projects.sorted_by_activity.non_archived.with_route - - projects = projects.map do |project| - { id: project.id, text: project.full_name } - end - - projects.unshift({ id: '', text: 'Any Project' }).to_json - end - def todo_types_options [ { id: '', text: 'Any Type' }, { id: 'Issue', text: 'Issue' }, - { id: 'MergeRequest', text: 'Merge Request' }, + { id: 'MergeRequest', text: 'Merge request' }, { id: 'DesignManagement::Design', text: 'Design' }, { id: 'AlertManagement::Alert', text: 'Alert' } ] @@ -240,14 +230,6 @@ module TodosHelper false end end - - def todo_group_options - groups = current_user.authorized_groups.with_route.map do |group| - { id: group.id, text: group.full_name } - end - - groups.unshift({ id: '', text: 'Any Group' }).to_json - end end TodosHelper.prepend_if_ee('EE::TodosHelper') diff --git a/app/helpers/tracking_helper.rb b/app/helpers/tracking_helper.rb index 221d9692661..7957038c21e 100644 --- a/app/helpers/tracking_helper.rb +++ b/app/helpers/tracking_helper.rb @@ -1,13 +1,13 @@ # frozen_string_literal: true module TrackingHelper - def tracking_attrs(label, event, property) + def tracking_attrs(label, action, property) return {} unless tracking_enabled? { data: { track_label: label, - track_event: event, + track_action: action, track_property: property } } diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb index b050f533d77..b795851ba30 100644 --- a/app/helpers/tree_helper.rb +++ b/app/helpers/tree_helper.rb @@ -131,6 +131,8 @@ module TreeHelper def breadcrumb_data_attributes attrs = { + selected_branch: selected_branch, + can_push_code: can?(current_user, :push_code, @project).to_s, can_collaborate: can_collaborate_with_project?(@project).to_s, new_blob_path: project_new_blob_path(@project, @ref), upload_path: project_create_blob_path(@project, @ref), diff --git a/app/helpers/user_callouts_helper.rb b/app/helpers/user_callouts_helper.rb index 6a242d000ae..7a90984cd77 100644 --- a/app/helpers/user_callouts_helper.rb +++ b/app/helpers/user_callouts_helper.rb @@ -5,7 +5,7 @@ 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' + SERVICE_TEMPLATES_DEPRECATED_CALLOUT = 'service_templates_deprecated_callout' TABS_POSITION_HIGHLIGHT = 'tabs_position_highlight' WEBHOOKS_MOVED = 'webhooks_moved' CUSTOMIZE_HOMEPAGE = 'customize_homepage' @@ -41,16 +41,19 @@ module UserCalloutsHelper !user_dismissed?(SUGGEST_POPOVER_DISMISSED) end - def show_service_templates_deprecated? - !user_dismissed?(SERVICE_TEMPLATES_DEPRECATED) + def show_service_templates_deprecated_callout? + !Gitlab.com? && + current_user&.admin? && + Service.for_template.active.exists? && + !user_dismissed?(SERVICE_TEMPLATES_DEPRECATED_CALLOUT) end def show_webhooks_moved_alert? !user_dismissed?(WEBHOOKS_MOVED) end - def show_customize_homepage_banner?(customize_homepage) - customize_homepage && !user_dismissed?(CUSTOMIZE_HOMEPAGE) + def show_customize_homepage_banner? + !user_dismissed?(CUSTOMIZE_HOMEPAGE) end def show_feature_flags_new_version? diff --git a/app/helpers/whats_new_helper.rb b/app/helpers/whats_new_helper.rb index bbf5bde5904..9362ae1491f 100644 --- a/app/helpers/whats_new_helper.rb +++ b/app/helpers/whats_new_helper.rb @@ -5,15 +5,11 @@ module WhatsNewHelper ReleaseHighlight.most_recent_item_count end - def whats_new_storage_key - most_recent_version = ReleaseHighlight.versions&.first - - return unless most_recent_version - - ['display-whats-new-notification', most_recent_version].join('-') + def whats_new_version_digest + ReleaseHighlight.most_recent_version_digest end - def whats_new_versions - ReleaseHighlight.versions + def display_whats_new? + Gitlab.dev_env_org_or_com? || user_signed_in? end end diff --git a/app/helpers/wiki_helper.rb b/app/helpers/wiki_helper.rb index 3f82cf893a0..c2a5ff40852 100644 --- a/app/helpers/wiki_helper.rb +++ b/app/helpers/wiki_helper.rb @@ -22,7 +22,7 @@ module WikiHelper end def wiki_sidebar_toggle_button - content_tag :button, class: 'btn btn-default sidebar-toggle js-sidebar-wiki-toggle', role: 'button', type: 'button' do + content_tag :button, class: 'gl-button btn btn-default btn-icon sidebar-toggle js-sidebar-wiki-toggle', role: 'button', type: 'button' do sprite_icon('chevron-double-lg-left') end end @@ -61,7 +61,7 @@ module WikiHelper def wiki_sort_controls(wiki, sort, direction) sort ||= Wiki::TITLE_ORDER - link_class = 'btn btn-default has-tooltip reverse-sort-btn qa-reverse-sort rspec-reverse-sort' + link_class = 'gl-button btn btn-default btn-icon has-tooltip reverse-sort-btn qa-reverse-sort rspec-reverse-sort' reversed_direction = direction == 'desc' ? 'asc' : 'desc' icon_class = direction == 'desc' ? 'highest' : 'lowest' diff --git a/app/helpers/workhorse_helper.rb b/app/helpers/workhorse_helper.rb index 28dd1b00292..8785c4cdcbb 100644 --- a/app/helpers/workhorse_helper.rb +++ b/app/helpers/workhorse_helper.rb @@ -7,7 +7,7 @@ module WorkhorseHelper def send_git_blob(repository, blob, inline: true) headers.store(*Gitlab::Workhorse.send_git_blob(repository, blob)) - headers['Content-Disposition'] = inline ? 'inline' : content_disposition_attachment(repository.project, blob.name) + headers['Content-Disposition'] = content_disposition_for_blob(blob, inline) # If enabled, this will override the values set above workhorse_set_content_type! @@ -49,11 +49,9 @@ module WorkhorseHelper headers[Gitlab::Workhorse::DETECT_HEADER] = "true" end - def content_disposition_attachment(project, filename) - if Feature.enabled?(:attachment_with_filename, project, default_enabled: :yaml) - ActionDispatch::Http::ContentDisposition.format(disposition: 'attachment', filename: filename) - else - 'attachment' - end + def content_disposition_for_blob(blob, inline) + return 'inline' if inline + + ActionDispatch::Http::ContentDisposition.format(disposition: 'attachment', filename: blob.name) end end |