From e8d2c2579383897a1dd7f9debd359abe8ae8373d Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Tue, 20 Jul 2021 09:55:51 +0000 Subject: Add latest changes from gitlab-org/gitlab@14-1-stable-ee --- app/helpers/admin/user_actions_helper.rb | 17 +- app/helpers/analytics/unique_visits_helper.rb | 30 -- app/helpers/application_helper.rb | 2 +- app/helpers/application_settings_helper.rb | 7 + app/helpers/auto_devops_helper.rb | 2 +- app/helpers/blob_helper.rb | 4 +- app/helpers/ci/jobs_helper.rb | 1 - app/helpers/ci/pipeline_editor_helper.rb | 16 +- app/helpers/ci/pipelines_helper.rb | 68 ++-- app/helpers/ci/variables_helper.rb | 2 +- app/helpers/clusters_helper.rb | 6 +- app/helpers/commits_helper.rb | 8 + app/helpers/custom_metrics_helper.rb | 2 +- app/helpers/diff_helper.rb | 10 +- app/helpers/environments_helper.rb | 2 +- app/helpers/events_helper.rb | 3 +- app/helpers/gitlab_routing_helper.rb | 381 +---------------------- app/helpers/groups_helper.rb | 18 +- app/helpers/integrations_helper.rb | 210 +++++++++++++ app/helpers/issuables_helper.rb | 9 + app/helpers/issues_helper.rb | 3 - app/helpers/namespaces_helper.rb | 4 - app/helpers/nav/new_dropdown_helper.rb | 15 +- app/helpers/nav/top_nav_helper.rb | 4 +- app/helpers/nav_helper.rb | 6 +- app/helpers/operations_helper.rb | 13 +- app/helpers/packages_helper.rb | 27 +- app/helpers/personal_access_tokens_helper.rb | 7 + app/helpers/projects/alert_management_helper.rb | 4 +- app/helpers/projects_helper.rb | 22 +- app/helpers/registrations_helper.rb | 7 - app/helpers/releases_helper.rb | 23 +- app/helpers/routing/artifacts_helper.rb | 49 +++ app/helpers/routing/graphql_helper.rb | 13 + app/helpers/routing/groups/members_helper.rb | 31 ++ app/helpers/routing/members_helper.rb | 14 + app/helpers/routing/pipeline_schedules_helper.rb | 29 ++ app/helpers/routing/projects/members_helper.rb | 31 ++ app/helpers/routing/projects_helper.rb | 85 +++++ app/helpers/routing/snippets_helper.rb | 144 +++++++++ app/helpers/routing/wiki_helper.rb | 13 + app/helpers/search_helper.rb | 21 +- app/helpers/services_helper.rb | 185 ----------- app/helpers/sessions_helper.rb | 9 + app/helpers/sidebars_helper.rb | 74 ++++- app/helpers/sorting_helper.rb | 21 +- app/helpers/sorting_titles_values_helper.rb | 56 ++-- app/helpers/tracking_helper.rb | 2 +- app/helpers/user_callouts_helper.rb | 2 +- app/helpers/users_helper.rb | 147 ++------- app/helpers/whats_new_helper.rb | 6 +- 51 files changed, 960 insertions(+), 905 deletions(-) delete mode 100644 app/helpers/analytics/unique_visits_helper.rb create mode 100644 app/helpers/integrations_helper.rb create mode 100644 app/helpers/personal_access_tokens_helper.rb create mode 100644 app/helpers/routing/artifacts_helper.rb create mode 100644 app/helpers/routing/graphql_helper.rb create mode 100644 app/helpers/routing/groups/members_helper.rb create mode 100644 app/helpers/routing/members_helper.rb create mode 100644 app/helpers/routing/pipeline_schedules_helper.rb create mode 100644 app/helpers/routing/projects/members_helper.rb create mode 100644 app/helpers/routing/projects_helper.rb create mode 100644 app/helpers/routing/snippets_helper.rb create mode 100644 app/helpers/routing/wiki_helper.rb delete mode 100644 app/helpers/services_helper.rb (limited to 'app/helpers') diff --git a/app/helpers/admin/user_actions_helper.rb b/app/helpers/admin/user_actions_helper.rb index cd520a75b44..5719d8f5ffd 100644 --- a/app/helpers/admin/user_actions_helper.rb +++ b/app/helpers/admin/user_actions_helper.rb @@ -15,6 +15,7 @@ module Admin deactivate_actions unlock_actions delete_actions + ban_actions @actions end @@ -28,7 +29,7 @@ module Admin @actions << 'approve' @actions << 'reject' elsif @user.blocked? - @actions << 'unblock' + @actions << 'unblock' unless @user.banned? else @actions << 'block' end @@ -52,5 +53,19 @@ module Admin @actions << 'delete' @actions << 'delete_with_contributions' end + + def ban_actions + return unless ban_feature_available? + return if @user.internal? + + if @user.banned? + @actions << 'unban' + return + end + + unless @user.blocked? + @actions << 'ban' + end + end end end diff --git a/app/helpers/analytics/unique_visits_helper.rb b/app/helpers/analytics/unique_visits_helper.rb deleted file mode 100644 index 4aa8907f578..00000000000 --- a/app/helpers/analytics/unique_visits_helper.rb +++ /dev/null @@ -1,30 +0,0 @@ -# frozen_string_literal: true - -module Analytics - module UniqueVisitsHelper - extend ActiveSupport::Concern - - def visitor_id - return cookies[:visitor_id] if cookies[:visitor_id].present? - return unless current_user - - uuid = SecureRandom.uuid - cookies[:visitor_id] = { value: uuid, expires: 24.months } - uuid - end - - def track_visit(target_id) - return unless visitor_id - - Gitlab::Analytics::UniqueVisits.new.track_visit(target_id, values: visitor_id) - end - - class_methods do - def track_unique_visits(controller_actions, target_id:) - after_action only: controller_actions, if: -> { request.format.html? && request.headers['DNT'] != '1' } do - track_visit(target_id) - end - end - end - end -end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 2e15b3f22c2..1304bcb1c7e 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -285,7 +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 << 'epic-boards-page gl-overflow-auto' 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 diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb index efdad22fa54..a3df566e4b3 100644 --- a/app/helpers/application_settings_helper.rb +++ b/app/helpers/application_settings_helper.rb @@ -331,6 +331,7 @@ module ApplicationSettingsHelper :unique_ips_limit_per_user, :unique_ips_limit_time_window, :usage_ping_enabled, + :usage_ping_features_enabled, :user_default_external, :user_show_add_ssh_key_message, :user_default_internal_regex, @@ -343,6 +344,8 @@ module ApplicationSettingsHelper :commit_email_hostname, :protected_ci_variables, :local_markdown_version, + :mailgun_signing_key, + :mailgun_events_enabled, :snowplow_collector_hostname, :snowplow_cookie_domain, :snowplow_enabled, @@ -437,6 +440,10 @@ module ApplicationSettingsHelper Feature.enabled?(:help_page_documentation_redirect) end + def valid_runner_registrars + Gitlab::CurrentSettings.valid_runner_registrars + end + def signup_enabled? !!Gitlab::CurrentSettings.signup_enabled end diff --git a/app/helpers/auto_devops_helper.rb b/app/helpers/auto_devops_helper.rb index c27f5d4ebce..91a335cd504 100644 --- a/app/helpers/auto_devops_helper.rb +++ b/app/helpers/auto_devops_helper.rb @@ -7,7 +7,7 @@ module AutoDevopsHelper can?(current_user, :admin_pipeline, project) && project.has_auto_devops_implicitly_disabled? && !project.repository.gitlab_ci_yml && - !project.ci_service + !project.ci_integration end def badge_for_auto_devops_scope(auto_devops_receiver) diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index dfd6de3f1d5..eccd0e7a34c 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -65,7 +65,7 @@ module BlobHelper return unless blob = readable_blob(options, path, project, ref) common_classes = "btn gl-button btn-confirm js-edit-blob gl-ml-3 #{options[:extra_class]}" - data = { track_event: 'click_edit', track_label: 'Edit' } + data = { track_action: 'click_edit', track_label: 'edit' } if Feature.enabled?(:web_ide_primary_edit, project.group) common_classes += " btn-inverted" @@ -85,7 +85,7 @@ module BlobHelper return unless blob common_classes = 'btn gl-button btn-confirm ide-edit-button gl-ml-3' - data = { track_event: 'click_edit_ide', track_label: 'Web IDE' } + data = { track_action: 'click_edit_ide', track_label: 'web_ide' } unless Feature.enabled?(:web_ide_primary_edit, project.group) common_classes += " btn-inverted" diff --git a/app/helpers/ci/jobs_helper.rb b/app/helpers/ci/jobs_helper.rb index 23f2a082a68..882302f05ad 100644 --- a/app/helpers/ci/jobs_helper.rb +++ b/app/helpers/ci/jobs_helper.rb @@ -9,7 +9,6 @@ module Ci "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_settings_url" => project_runners_path(@build.project, anchor: 'js-runners-settings'), - "variables_settings_url" => project_variables_path(@build.project, anchor: 'js-cicd-variables-settings'), "page_path" => project_job_path(@project, @build), "build_status" => @build.status, "build_stage" => @build.stage, diff --git a/app/helpers/ci/pipeline_editor_helper.rb b/app/helpers/ci/pipeline_editor_helper.rb index 8c8ee2d4d0f..d441ffbb853 100644 --- a/app/helpers/ci/pipeline_editor_helper.rb +++ b/app/helpers/ci/pipeline_editor_helper.rb @@ -9,24 +9,26 @@ module Ci end def js_pipeline_editor_data(project) - commit_sha = project.commit ? project.commit.sha : '' + initial_branch = params[:branch_name] + latest_commit = project.repository.commit(initial_branch) || project.commit + commit_sha = latest_commit ? latest_commit.sha : '' { "ci-config-path": project.ci_config_path_or_default, - "ci-examples-help-page-path" => help_page_path('ci/examples/README'), - "ci-help-page-path" => help_page_path('ci/README'), + "ci-examples-help-page-path" => help_page_path('ci/examples/index'), + "ci-help-page-path" => help_page_path('ci/index'), "commit-sha" => commit_sha, - "default-branch" => project.default_branch, + "default-branch" => project.default_branch_or_main, "empty-state-illustration-path" => image_path('illustrations/empty-state/empty-dag-md.svg'), - "initial-branch-name": params[:branch_name], + "initial-branch-name" => initial_branch, "lint-help-page-path" => help_page_path('ci/lint', anchor: 'validate-basic-logic-and-syntax'), "needs-help-page-path" => help_page_path('ci/yaml/README', anchor: 'needs'), "new-merge-request-path" => namespace_project_new_merge_request_path, - "pipeline_etag" => project.commit ? graphql_etag_pipeline_sha_path(commit_sha) : '', + "pipeline_etag" => latest_commit ? graphql_etag_pipeline_sha_path(commit_sha) : '', "pipeline-page-path" => project_pipelines_path(project), "project-path" => project.path, "project-full-path" => project.full_path, "project-namespace" => project.namespace.full_path, - "runner-help-page-path" => help_page_path('ci/runners/README'), + "runner-help-page-path" => help_page_path('ci/runners/index'), "total-branches" => project.repository.branches.length, "yml-help-page-path" => help_page_path('ci/yaml/README') } diff --git a/app/helpers/ci/pipelines_helper.rb b/app/helpers/ci/pipelines_helper.rb index f42cd53ae3a..6be46b40023 100644 --- a/app/helpers/ci/pipelines_helper.rb +++ b/app/helpers/ci/pipelines_helper.rb @@ -30,42 +30,40 @@ 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 + def 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: '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') } + { name: 'Android', logo: image_path('illustrations/third-party-logos/ci_cd-template-logos/android.svg') }, + { name: 'Bash', logo: image_path('illustrations/third-party-logos/ci_cd-template-logos/bash.svg') }, + { name: 'C++', logo: image_path('illustrations/third-party-logos/ci_cd-template-logos/c_plus_plus.svg') }, + { name: 'Clojure', logo: image_path('illustrations/third-party-logos/ci_cd-template-logos/clojure.svg') }, + { name: 'Composer', logo: image_path('illustrations/third-party-logos/ci_cd-template-logos/composer.svg') }, + { name: 'Crystal', logo: image_path('illustrations/third-party-logos/ci_cd-template-logos/crystal.svg') }, + { name: 'Dart', logo: image_path('illustrations/third-party-logos/ci_cd-template-logos/dart.svg') }, + { name: 'Django', logo: image_path('illustrations/third-party-logos/ci_cd-template-logos/django.svg') }, + { name: 'Docker', logo: image_path('illustrations/third-party-logos/ci_cd-template-logos/docker.svg') }, + { name: 'Elixir', logo: image_path('illustrations/third-party-logos/ci_cd-template-logos/elixir.svg') }, + { name: 'iOS-Fastlane', logo: image_path('illustrations/third-party-logos/ci_cd-template-logos/fastlane.svg') }, + { name: 'Flutter', logo: image_path('illustrations/third-party-logos/ci_cd-template-logos/flutter.svg') }, + { name: 'Go', logo: image_path('illustrations/third-party-logos/ci_cd-template-logos/go_logo.svg') }, + { name: 'Gradle', logo: image_path('illustrations/third-party-logos/ci_cd-template-logos/gradle.svg') }, + { name: 'Grails', logo: image_path('illustrations/third-party-logos/ci_cd-template-logos/grails.svg') }, + { name: 'dotNET', logo: image_path('illustrations/third-party-logos/ci_cd-template-logos/dotnet.svg') }, + { name: 'Julia', logo: image_path('illustrations/third-party-logos/ci_cd-template-logos/julia.svg') }, + { name: 'Laravel', logo: image_path('illustrations/third-party-logos/ci_cd-template-logos/laravel.svg') }, + { name: 'LaTeX', logo: image_path('illustrations/third-party-logos/ci_cd-template-logos/latex.svg') }, + { name: 'Maven', logo: image_path('illustrations/third-party-logos/ci_cd-template-logos/maven.svg') }, + { name: 'Mono', logo: image_path('illustrations/third-party-logos/ci_cd-template-logos/mono.svg') }, + { name: 'Nodejs', logo: image_path('illustrations/third-party-logos/ci_cd-template-logos/node_js.svg') }, + { name: 'npm', logo: image_path('illustrations/third-party-logos/ci_cd-template-logos/npm.svg') }, + { name: 'OpenShift', logo: image_path('illustrations/third-party-logos/ci_cd-template-logos/openshift.svg') }, + { name: 'Packer', logo: image_path('illustrations/third-party-logos/ci_cd-template-logos/packer.svg') }, + { name: 'PHP', logo: image_path('illustrations/third-party-logos/ci_cd-template-logos/php.svg') }, + { name: 'Python', logo: image_path('illustrations/third-party-logos/ci_cd-template-logos/python.svg') }, + { name: 'Ruby', logo: image_path('illustrations/third-party-logos/ci_cd-template-logos/ruby.svg') }, + { name: 'Rust', logo: image_path('illustrations/third-party-logos/ci_cd-template-logos/rust.svg') }, + { name: 'Scala', logo: image_path('illustrations/third-party-logos/ci_cd-template-logos/scala.svg') }, + { name: 'Swift', logo: image_path('illustrations/third-party-logos/ci_cd-template-logos/swift.svg') }, + { name: 'Terraform', logo: image_path('illustrations/third-party-logos/ci_cd-template-logos/terraform.svg') } ] end diff --git a/app/helpers/ci/variables_helper.rb b/app/helpers/ci/variables_helper.rb index b20390d58e9..84572363a8d 100644 --- a/app/helpers/ci/variables_helper.rb +++ b/app/helpers/ci/variables_helper.rb @@ -48,7 +48,7 @@ module Ci end def ci_variable_maskable_regex - Ci::Maskable::REGEX.inspect.sub('\\A', '^').sub('\\z', '$').sub(/^\//, '').sub(/\/[a-z]*$/, '').gsub('\/', '/') + Ci::Maskable::REGEX.inspect.sub('\\A', '^').sub('\\z', '$').sub(%r{^/}, '').sub(%r{/[a-z]*$}, '').gsub('\/', '/') end end end diff --git a/app/helpers/clusters_helper.rb b/app/helpers/clusters_helper.rb index 14783882f5e..e9a75babb97 100644 --- a/app/helpers/clusters_helper.rb +++ b/app/helpers/clusters_helper.rb @@ -20,7 +20,11 @@ module ClustersHelper { default_branch_name: clusterable_project.default_branch, empty_state_image: image_path('illustrations/clusters_empty.svg'), - project_path: clusterable_project.full_path + project_path: clusterable_project.full_path, + agent_docs_url: help_page_path('user/clusters/agent/index'), + install_docs_url: help_page_path('administration/clusters/kas'), + get_started_docs_url: help_page_path('user/clusters/agent/index', anchor: 'define-a-configuration-repository'), + integration_docs_url: help_page_path('user/clusters/agent/index', anchor: 'get-started-with-gitops-and-the-gitlab-agent') } end diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index a7696ba4ea7..d2ac1e8f985 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -167,6 +167,14 @@ module CommitsHelper ] end + DEFAULT_SHA = '0000000' + + # Returns the template path for commit resources + # to be utilized by the client applications. + def commit_path_template(project) + project_commit_path(project, DEFAULT_SHA).sub("/#{DEFAULT_SHA}", '/$COMMIT_SHA') + end + protected # Private: Returns a link to a person. If the person has a matching user and diff --git a/app/helpers/custom_metrics_helper.rb b/app/helpers/custom_metrics_helper.rb index 5ea386e268d..9fbfe377c61 100644 --- a/app/helpers/custom_metrics_helper.rb +++ b/app/helpers/custom_metrics_helper.rb @@ -5,7 +5,7 @@ module CustomMetricsHelper { 'custom-metrics-path' => url_for([project, metric]), 'metric-persisted' => metric.persisted?.to_s, - 'edit-project-service-path' => edit_project_service_path(project, PrometheusService), + 'edit-project-service-path' => edit_project_service_path(project, ::Integrations::Prometheus), 'validate-query-path' => validate_query_project_prometheus_metrics_path(project), 'title' => metric.title.to_s, 'query' => metric.query.to_s, diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb index e430b0f402b..3aa54e3afe9 100644 --- a/app/helpers/diff_helper.rb +++ b/app/helpers/diff_helper.rb @@ -190,10 +190,8 @@ module DiffHelper end def render_overflow_warning?(diffs_collection) - diff_files = diffs_collection.raw_diff_files - - diff_files.overflow?.tap do |overflown| - log_overflow_limits(diff_files) + diffs_collection.overflow?.tap do |overflown| + log_overflow_limits(diff_files: diffs_collection.raw_diff_files, collection_overflow: overflown) end end @@ -285,12 +283,12 @@ module DiffHelper conflicts_service.conflicts.files.index_by(&:our_path) end - def log_overflow_limits(diff_files) + def log_overflow_limits(diff_files:, collection_overflow:) 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_collection_limits) if collection_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? diff --git a/app/helpers/environments_helper.rb b/app/helpers/environments_helper.rb index 5927c82abe9..62060200698 100644 --- a/app/helpers/environments_helper.rb +++ b/app/helpers/environments_helper.rb @@ -79,7 +79,7 @@ module EnvironmentsHelper end def has_managed_prometheus?(project) - project.prometheus_service&.prometheus_available? == true + project.prometheus_integration&.prometheus_available? == true end def metrics_dashboard_base_path(environment, project) diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb index cd70c6f3962..4ee3acd32d2 100644 --- a/app/helpers/events_helper.rb +++ b/app/helpers/events_helper.rb @@ -240,8 +240,7 @@ module EventsHelper DESIGN_ICONS = { 'created' => 'upload', 'updated' => 'pencil', - 'destroyed' => ICON_NAMES_BY_EVENT_TYPE['destroyed'], - 'archived' => 'archive' + 'destroyed' => ICON_NAMES_BY_EVENT_TYPE['destroyed'] }.freeze def design_event_icon(action, size: 24) diff --git a/app/helpers/gitlab_routing_helper.rb b/app/helpers/gitlab_routing_helper.rb index 0a684d92eb1..0f835e6881e 100644 --- a/app/helpers/gitlab_routing_helper.rb +++ b/app/helpers/gitlab_routing_helper.rb @@ -7,381 +7,18 @@ module GitlabRoutingHelper include ::ProjectsHelper include ::ApplicationSettingsHelper include API::Helpers::RelatedResourcesHelpers + include ::Routing::ProjectsHelper + include ::Routing::Projects::MembersHelper + include ::Routing::Groups::MembersHelper + include ::Routing::MembersHelper + include ::Routing::ArtifactsHelper + include ::Routing::PipelineSchedulesHelper + include ::Routing::SnippetsHelper + include ::Routing::WikiHelper + include ::Routing::GraphqlHelper included do Gitlab::Routing.includes_helpers(self) end - - # Project - def project_tree_path(project, ref = nil, *args) - namespace_project_tree_path(project.namespace, project, ref || @ref || project.repository.root_ref, *args) # rubocop:disable Cop/ProjectPathHelper - end - - def project_commits_path(project, ref = nil, *args) - namespace_project_commits_path(project.namespace, project, ref || @ref || project.repository.root_ref, *args) # rubocop:disable Cop/ProjectPathHelper - end - - def project_ref_path(project, ref_name, *args) - project_commits_path(project, ref_name, *args) - end - - def environment_path(environment, *args) - project_environment_path(environment.project, environment, *args) - end - - def environment_metrics_path(environment, *args) - metrics_project_environment_path(environment.project, environment, *args) - end - - def environment_delete_path(environment, *args) - expose_path(api_v4_projects_environments_path(id: environment.project.id, environment_id: environment.id)) - end - - def issue_path(entity, *args) - project_issue_path(entity.project, entity, *args) - end - - def merge_request_path(entity, *args) - project_merge_request_path(entity.project, entity, *args) - end - - def pipeline_path(pipeline, *args) - project_pipeline_path(pipeline.project, pipeline.id, *args) - end - - def issue_url(entity, *args) - project_issue_url(entity.project, entity, *args) - end - - def merge_request_url(entity, *args) - project_merge_request_url(entity.project, entity, *args) - end - - def pipeline_url(pipeline, *args) - project_pipeline_url(pipeline.project, pipeline.id, *args) - end - - def pipeline_job_url(pipeline, build, *args) - project_job_url(pipeline.project, build.id, *args) - end - - def commits_url(entity, *args) - project_commits_url(entity.project, entity.source_ref, *args) - end - - def commit_url(entity, *args) - project_commit_url(entity.project, entity.sha, *args) - end - - def release_url(entity, *args) - project_release_url(entity.project, entity, *args) - end - - def preview_markdown_path(parent, *args) - return group_preview_markdown_path(parent, *args) if parent.is_a?(Group) - - if @snippet.is_a?(PersonalSnippet) - preview_markdown_snippets_path - else - preview_markdown_project_path(parent, *args) - end - end - - def edit_milestone_path(entity, *args) - if entity.resource_parent.is_a?(Group) - edit_group_milestone_path(entity.resource_parent, entity, *args) - else - edit_project_milestone_path(entity.resource_parent, entity, *args) - end - end - - def toggle_subscription_path(entity, *args) - if entity.is_a?(Issue) - toggle_subscription_project_issue_path(entity.project, entity) - else - toggle_subscription_project_merge_request_path(entity.project, entity) - end - end - - def toggle_award_emoji_personal_snippet_path(*args) - toggle_award_emoji_snippet_path(*args) - end - - def toggle_award_emoji_project_project_snippet_path(*args) - toggle_award_emoji_project_snippet_path(*args) - end - - def toggle_award_emoji_project_project_snippet_url(*args) - toggle_award_emoji_project_snippet_url(*args) - end - - ## Members - def project_members_url(project, *args) - project_project_members_url(project, *args) - end - - def project_member_path(project_member, *args) - project_project_member_path(project_member.source, project_member) - end - - def request_access_project_members_path(project, *args) - request_access_project_project_members_path(project) - end - - def leave_project_members_path(project, *args) - leave_project_project_members_path(project) - end - - def approve_access_request_project_member_path(project_member, *args) - approve_access_request_project_project_member_path(project_member.source, project_member) - end - - def resend_invite_project_member_path(project_member, *args) - resend_invite_project_project_member_path(project_member.source, project_member) - end - - # Groups - - ## Members - def group_members_url(group, *args) - group_group_members_url(group, *args) - end - - def group_member_path(group_member, *args) - group_group_member_path(group_member.source, group_member) - end - - def request_access_group_members_path(group, *args) - request_access_group_group_members_path(group) - end - - def leave_group_members_path(group, *args) - leave_group_group_members_path(group) - end - - def approve_access_request_group_member_path(group_member, *args) - approve_access_request_group_group_member_path(group_member.source, group_member) - end - - def resend_invite_group_member_path(group_member, *args) - resend_invite_group_group_member_path(group_member.source, group_member) - end - - # Members - def source_members_url(member) - case member.source_type - when 'Namespace' - group_group_members_url(member.source) - when 'Project' - project_project_members_url(member.source) - end - end - - # Artifacts - - # Rails path generators are slow because they need to do large regex comparisons - # against the arguments. We can speed this up 10x by generating the strings directly. - - # /*namespace_id/:project_id/-/jobs/:job_id/artifacts/download(.:format) - def fast_download_project_job_artifacts_path(project, job, params = {}) - expose_fast_artifacts_path(project, job, :download, params) - end - - # /*namespace_id/:project_id/-/jobs/:job_id/artifacts/keep(.:format) - def fast_keep_project_job_artifacts_path(project, job) - expose_fast_artifacts_path(project, job, :keep) - end - - # /*namespace_id/:project_id/-/jobs/:job_id/artifacts/browse(/*path) - def fast_browse_project_job_artifacts_path(project, job) - expose_fast_artifacts_path(project, job, :browse) - end - - def expose_fast_artifacts_path(project, job, action, params = {}) - path = "#{project.full_path}/-/jobs/#{job.id}/artifacts/#{action}" - - unless params.empty? - path += "?#{params.to_query}" - end - - Gitlab::Utils.append_path(Gitlab.config.gitlab.relative_url_root, path) - end - - def artifacts_action_path(path, project, build) - action, path_params = path.split('/', 2) - args = [project, build, path_params] - - case action - when 'download' - download_project_job_artifacts_path(*args) - when 'browse' - browse_project_job_artifacts_path(*args) - when 'file' - file_project_job_artifacts_path(*args) - when 'raw' - raw_project_job_artifacts_path(*args) - end - end - - # Pipeline Schedules - def pipeline_schedules_path(project, *args) - project_pipeline_schedules_path(project, *args) - end - - def pipeline_schedule_path(schedule, *args) - project = schedule.project - project_pipeline_schedule_path(project, schedule, *args) - end - - def edit_pipeline_schedule_path(schedule) - project = schedule.project - edit_project_pipeline_schedule_path(project, schedule) - end - - def play_pipeline_schedule_path(schedule, *args) - project = schedule.project - play_project_pipeline_schedule_path(project, schedule, *args) - end - - def take_ownership_pipeline_schedule_path(schedule, *args) - project = schedule.project - take_ownership_project_pipeline_schedule_path(project, schedule, *args) - end - - def gitlab_snippet_path(snippet, *args) - if snippet.is_a?(ProjectSnippet) - project_snippet_path(snippet.project, snippet, *args) - else - new_args = snippet_query_params(snippet, *args) - snippet_path(snippet, *new_args) - end - end - - def gitlab_snippet_url(snippet, *args) - if snippet.is_a?(ProjectSnippet) - project_snippet_url(snippet.project, snippet, *args) - else - new_args = snippet_query_params(snippet, *args) - snippet_url(snippet, *new_args) - end - end - - def gitlab_dashboard_snippets_path(snippet, *args) - if snippet.is_a?(ProjectSnippet) - project_snippets_path(snippet.project, *args) - else - dashboard_snippets_path - end - end - - def gitlab_raw_snippet_path(snippet, *args) - if snippet.is_a?(ProjectSnippet) - raw_project_snippet_path(snippet.project, snippet, *args) - else - new_args = snippet_query_params(snippet, *args) - raw_snippet_path(snippet, *new_args) - end - end - - def gitlab_raw_snippet_url(snippet, *args) - if snippet.is_a?(ProjectSnippet) - raw_project_snippet_url(snippet.project, snippet, *args) - else - new_args = snippet_query_params(snippet, *args) - raw_snippet_url(snippet, *new_args) - end - end - - def gitlab_raw_snippet_blob_url(snippet, path, ref = nil, **options) - params = { - snippet_id: snippet, - ref: ref || snippet.repository.root_ref, - path: path - } - - if snippet.is_a?(ProjectSnippet) - project_snippet_blob_raw_url(snippet.project, **params, **options) - else - snippet_blob_raw_url(**params, **options) - end - end - - def gitlab_raw_snippet_blob_path(snippet, path, ref = nil, **options) - gitlab_raw_snippet_blob_url(snippet, path, ref, only_path: true, **options) - end - - def gitlab_snippet_notes_path(snippet, *args) - new_args = snippet_query_params(snippet, *args) - snippet_notes_path(snippet, *new_args) - end - - def gitlab_snippet_notes_url(snippet, *args) - new_args = snippet_query_params(snippet, *args) - snippet_notes_url(snippet, *new_args) - end - - def gitlab_snippet_note_path(snippet, note, *args) - new_args = snippet_query_params(snippet, *args) - snippet_note_path(snippet, note, *new_args) - end - - def gitlab_snippet_note_url(snippet, note, *args) - new_args = snippet_query_params(snippet, *args) - snippet_note_url(snippet, note, *new_args) - end - - def gitlab_toggle_award_emoji_snippet_note_path(snippet, note, *args) - new_args = snippet_query_params(snippet, *args) - toggle_award_emoji_snippet_note_path(snippet, note, *new_args) - end - - def gitlab_toggle_award_emoji_snippet_note_url(snippet, note, *args) - new_args = snippet_query_params(snippet, *args) - toggle_award_emoji_snippet_note_url(snippet, note, *new_args) - end - - def gitlab_toggle_award_emoji_snippet_path(snippet, *args) - new_args = snippet_query_params(snippet, *args) - toggle_award_emoji_snippet_path(snippet, *new_args) - end - - def gitlab_toggle_award_emoji_snippet_url(snippet, *args) - new_args = snippet_query_params(snippet, *args) - toggle_award_emoji_snippet_url(snippet, *new_args) - end - - # Wikis - - def wiki_path(wiki, **options) - Gitlab::UrlBuilder.wiki_url(wiki, only_path: true, **options) - end - - def wiki_page_path(wiki, page, **options) - Gitlab::UrlBuilder.wiki_page_url(wiki, page, only_path: true, **options) - end - - # GraphQL ETag routes - def graphql_etag_pipeline_path(pipeline) - [api_graphql_path, "pipelines/id/#{pipeline.id}"].join(':') - end - - def graphql_etag_pipeline_sha_path(sha) - [api_graphql_path, "pipelines/sha/#{sha}"].join(':') - end - - private - - def snippet_query_params(snippet, *args) - opts = case args.last - when Hash - args.pop - when ActionController::Parameters - args.pop.to_h - else - {} - end - - args << opts - end end GitlabRoutingHelper.include_mod_with('GitlabRoutingHelper') diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb index 26a5df321cd..400ad721b06 100644 --- a/app/helpers/groups_helper.rb +++ b/app/helpers/groups_helper.rb @@ -5,15 +5,9 @@ module GroupsHelper %w[ groups#activity groups#subgroups - ].tap do |paths| - extra_routes = if sidebar_refactor_disabled? - ['groups#show', 'groups#details'] - else - ['labels#index', 'group_members#index'] - end - - paths.concat(extra_routes) - end + labels#index + group_members#index + ] end def group_settings_nav_link_paths @@ -45,11 +39,7 @@ module GroupsHelper end def group_information_title(group) - if Feature.enabled?(:sidebar_refactor, current_user, default_enabled: :yaml) - group.subgroup? ? _('Subgroup information') : _('Group information') - else - group.subgroup? ? _('Subgroup overview') : _('Group overview') - end + group.subgroup? ? _('Subgroup information') : _('Group information') end def group_container_registry_nav? diff --git a/app/helpers/integrations_helper.rb b/app/helpers/integrations_helper.rb new file mode 100644 index 00000000000..ab305d822e8 --- /dev/null +++ b/app/helpers/integrations_helper.rb @@ -0,0 +1,210 @@ +# frozen_string_literal: true + +module IntegrationsHelper + def integration_event_description(integration, event) + case integration + when Integrations::Jira + jira_integration_event_description(event) + when Integrations::Teamcity + teamcity_integration_event_description(event) + else + default_integration_event_description(event) + end + end + + def integration_event_field_name(event) + event = event.pluralize if %w[merge_request issue confidential_issue].include?(event) + "#{event}_events" + end + + def scoped_integrations_path + if @project.present? + project_settings_integrations_path(@project) + elsif @group.present? + group_settings_integrations_path(@group) + else + integrations_admin_application_settings_path + end + end + + def scoped_integration_path(integration) + if @project.present? + project_service_path(@project, integration) + elsif @group.present? + group_settings_integration_path(@group, integration) + else + admin_application_settings_integration_path(integration) + end + end + + def scoped_edit_integration_path(integration) + if @project.present? + edit_project_service_path(@project, integration) + elsif @group.present? + edit_group_settings_integration_path(@group, integration) + else + edit_admin_application_settings_integration_path(integration) + end + end + + def scoped_test_integration_path(integration) + if @project.present? + test_project_service_path(@project, integration) + elsif @group.present? + test_group_settings_integration_path(@group, integration) + else + test_admin_application_settings_integration_path(integration) + end + end + + def scoped_reset_integration_path(integration, group: nil) + return '' unless integration.persisted? + + if group.present? + reset_group_settings_integration_path(group, integration) + else + reset_admin_application_settings_integration_path(integration) + end + 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, + type: integration.to_param, + merge_request_events: integration.merge_requests_events.to_s, + 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_integration(integration), + fields: fields_for_integration(integration), + 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.testable?.to_s, + test_path: scoped_test_integration_path(integration), + reset_path: scoped_reset_integration_path(integration, group: group) + } + + if integration.is_a?(Integrations::Jira) + 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 integrations_help_page_path + help_page_path('user/admin_area/settings/project_integration_management') + end + + def project_jira_issues_integration? + false + end + + def instance_level_integrations? + !Gitlab.com? + end + + def jira_issue_breadcrumb_link(issue_reference) + link_to '', { class: 'gl-display-flex gl-align-items-center gl-white-space-nowrap' } do + icon = image_tag image_path('illustrations/logos/jira.svg'), width: 15, height: 15, class: 'gl-mr-2' + [icon, issue_reference].join.html_safe + end + end + + extend self + + private + + def jira_integration_event_description(event) + case event + when "merge_request", "merge_request_events" + s_("JiraService|Jira comments are created when an issue is referenced in a merge request.") + when "commit", "commit_events" + s_("JiraService|Jira comments are created when an issue is referenced in a commit.") + end + end + + def teamcity_integration_event_description(event) + case event + when 'push', 'push_events' + s_('TeamcityIntegration|Trigger TeamCity CI after every push to the repository, except branch delete') + when 'merge_request', 'merge_request_events' + s_('TeamcityIntegration|Trigger TeamCity CI after a merge request has been created or updated') + end + end + + def default_integration_event_description(event) + case event + when "push", "push_events" + s_("ProjectService|Trigger event for pushes to the repository.") + when "tag_push", "tag_push_events" + s_("ProjectService|Trigger event for new tags pushed to the repository.") + when "note", "note_events" + s_("ProjectService|Trigger event for new comments.") + when "confidential_note", "confidential_note_events" + s_("ProjectService|Trigger event for new comments on confidential issues.") + when "issue", "issue_events" + s_("ProjectService|Trigger event when an issue is created, updated, or closed.") + when "confidential_issue", "confidential_issue_events" + s_("ProjectService|Trigger event when a confidential issue is created, updated, or closed.") + when "merge_request", "merge_request_events" + s_("ProjectService|Trigger event when a merge request is created, updated, or merged.") + when "pipeline", "pipeline_events" + s_("ProjectService|Trigger event when a pipeline status changes.") + when "wiki_page", "wiki_page_events" + s_("ProjectService|Trigger event when a wiki page is created or updated.") + when "commit", "commit_events" + s_("ProjectService|Trigger event when a commit is created or updated.") + when "deployment" + s_("ProjectService|Trigger event when a deployment starts or finishes.") + when "alert" + s_("ProjectService|Trigger event when a new, unique alert is recorded.") + end + end + + def trigger_events_for_integration(integration) + ServiceEventSerializer.new(service: integration).represent(integration.configurable_events).to_json + end + + def fields_for_integration(integration) + ServiceFieldSerializer.new(service: integration).represent(integration.global_fields).to_json + end + + def integration_level(integration) + if integration.instance_level? + 'instance' + elsif integration.group_level? + 'group' + else + '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 + +IntegrationsHelper.prepend_mod_with('IntegrationsHelper') + +# The methods in `EE::IntegrationsHelper` should be available as both instance and +# class methods. +IntegrationsHelper.extend_mod_with('IntegrationsHelper') diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index c40feb42eea..d8ba530f3f6 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -425,6 +425,15 @@ module IssuablesHelper } end + def sidebar_status_data(issuable_sidebar, project) + { + iid: issuable_sidebar[:iid], + issuable_type: issuable_sidebar[:type], + full_path: project.full_path, + can_edit: issuable_sidebar.dig(:current_user, :can_edit).to_s + } + end + def parent @project || @group end diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 7690773354f..5bedfc61d46 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -181,7 +181,6 @@ module IssuesHelper def issues_list_data(project, current_user, finder) { - autocomplete_users_path: autocomplete_users_path(active: true, current_user: true, project_id: project.id, format: :json), autocomplete_award_emojis_path: autocomplete_award_emojis_path, calendar_path: url_for(safe_params.merge(calendar_url_options)), can_bulk_update: can?(current_user, :admin_issue, project).to_s, @@ -201,8 +200,6 @@ module IssuesHelper max_attachment_size: number_to_human_size(Gitlab::CurrentSettings.max_attachment_size.megabytes), new_issue_path: new_project_issue_path(project, issue: { milestone_id: finder.milestones.first.try(:id) }), project_import_jira_path: project_import_jira_path(project), - project_labels_path: project_labels_path(project, include_ancestor_groups: true, format: :json), - project_milestones_path: project_milestones_path(project, format: :json), project_path: project.full_path, quick_actions_help_path: help_page_path('user/project/quick_actions'), reset_path: new_issuable_address_project_path(project, issuable_type: 'issue'), diff --git a/app/helpers/namespaces_helper.rb b/app/helpers/namespaces_helper.rb index 39a8f506ba2..106df168080 100644 --- a/app/helpers/namespaces_helper.rb +++ b/app/helpers/namespaces_helper.rb @@ -56,10 +56,6 @@ 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 diff --git a/app/helpers/nav/new_dropdown_helper.rb b/app/helpers/nav/new_dropdown_helper.rb index b952aeacb13..ff8839d68fd 100644 --- a/app/helpers/nav/new_dropdown_helper.rb +++ b/app/helpers/nav/new_dropdown_helper.rb @@ -21,13 +21,6 @@ module Nav } end - def new_repo_experiment_text - experiment(:new_repo, user: current_user) do |e| - e.use { _('New project') } - e.try { _('New project/repository') } - end.run - end - private def group_menu_section(group) @@ -37,9 +30,9 @@ module Nav menu_items.push( ::Gitlab::Nav::TopNavMenuItem.build( id: 'new_project', - title: new_repo_experiment_text, + title: _('New project/repository'), href: new_project_path(namespace_id: group.id), - data: { track_experiment: 'new_repo', track_event: 'click_link_new_project_group', track_label: 'plus_menu_dropdown' } + data: { track_event: 'click_link_new_project_group', track_label: 'plus_menu_dropdown' } ) ) end @@ -129,9 +122,9 @@ module Nav menu_items.push( ::Gitlab::Nav::TopNavMenuItem.build( id: 'general_new_project', - title: new_repo_experiment_text, + title: _('New project/repository'), href: new_project_path, - data: { track_experiment: 'new_repo', track_event: 'click_link_new_project', track_label: 'plus_menu_dropdown', qa_selector: 'global_new_project_link' } + data: { track_event: 'click_link_new_project', track_label: 'plus_menu_dropdown', qa_selector: 'global_new_project_link' } ) ) end diff --git a/app/helpers/nav/top_nav_helper.rb b/app/helpers/nav/top_nav_helper.rb index b8ddb932b73..052b8339ebd 100644 --- a/app/helpers/nav/top_nav_helper.rb +++ b/app/helpers/nav/top_nav_helper.rb @@ -22,7 +22,7 @@ module Nav new_view_model = new_dropdown_view_model(project: project, group: group) - if new_view_model + if new_view_model && new_view_model.fetch(:menu_sections)&.any? builder.add_view(NEW_VIEW, new_view_model) end @@ -98,7 +98,7 @@ module Nav builder.add_primary_menu_item_with_shortcut( active: nav == 'project' || active_nav_link?(path: %w[root#index projects#trending projects#starred dashboard/projects#index]), css_class: 'qa-projects-dropdown', - data: { track_label: "projects_dropdown", track_event: "click_dropdown", track_experiment: "new_repo" }, + data: { track_label: "projects_dropdown", track_event: "click_dropdown" }, view: PROJECTS_VIEW, shortcut_href: dashboard_projects_path, **projects_menu_item_attrs diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb index b5171dfbebd..6c57a31f3db 100644 --- a/app/helpers/nav_helper.rb +++ b/app/helpers/nav_helper.rb @@ -64,7 +64,7 @@ module NavHelper end def admin_analytics_nav_links - %w(dev_ops_report) + %w(dev_ops_report usage_trends) end def group_issues_sub_menu_items @@ -73,9 +73,7 @@ module NavHelper milestones#index boards#index boards#show - ].tap do |paths| - paths << 'labels#index' if Feature.disabled?(:sidebar_refactor, current_user, default_enabled: :yaml) - end + ] end private diff --git a/app/helpers/operations_helper.rb b/app/helpers/operations_helper.rb index fb410c46128..5d2f225edcf 100644 --- a/app/helpers/operations_helper.rb +++ b/app/helpers/operations_helper.rb @@ -2,10 +2,11 @@ module OperationsHelper include Gitlab::Utils::StrongMemoize + include IntegrationsHelper - def prometheus_service - strong_memoize(:prometheus_service) do - @project.find_or_initialize_service(::PrometheusService.to_param) + def prometheus_integration + strong_memoize(:prometheus_integration) do + @project.find_or_initialize_integration(::Integrations::Prometheus.to_param) end end @@ -14,11 +15,11 @@ module OperationsHelper templates = setting.available_issue_templates.map { |t| { key: t.key, name: t.name } } { - 'prometheus_activated' => prometheus_service.manual_configuration?.to_s, - 'prometheus_form_path' => scoped_integration_path(prometheus_service), + 'prometheus_activated' => prometheus_integration.manual_configuration?.to_s, + 'prometheus_form_path' => scoped_integration_path(prometheus_integration), 'prometheus_reset_key_path' => reset_alerting_token_project_settings_operations_path(@project), 'prometheus_authorization_key' => @project.alerting_setting&.token, - 'prometheus_api_url' => prometheus_service.api_url, + 'prometheus_api_url' => prometheus_integration.api_url, 'prometheus_url' => notify_project_prometheus_alerts_url(@project, format: :json), 'alerts_setup_url' => help_page_path('operations/incident_management/integrations.md', anchor: 'configuration'), 'alerts_usage_url' => project_alert_management_index_path(@project), diff --git a/app/helpers/packages_helper.rb b/app/helpers/packages_helper.rb index fe41c041b4f..50984415aa5 100644 --- a/app/helpers/packages_helper.rb +++ b/app/helpers/packages_helper.rb @@ -57,10 +57,35 @@ module PackagesHelper def show_cleanup_policy_on_alert(project) Gitlab.com? && Gitlab.config.registry.enabled && - project.container_registry_enabled && + project.feature_available?(:container_registry, current_user) && !Gitlab::CurrentSettings.container_expiration_policies_enable_historic_entries && Feature.enabled?(:container_expiration_policies_historic_entry, project) && project.container_expiration_policy.nil? && project.container_repositories.exists? end + + def package_details_data(project, package = nil) + { + package: package ? package_from_presenter(package) : nil, + can_delete: can?(current_user, :destroy_package, project).to_s, + svg_path: image_path('illustrations/no-packages.svg'), + npm_path: package_registry_instance_url(:npm), + npm_help_path: help_page_path('user/packages/npm_registry/index'), + maven_path: package_registry_project_url(project.id, :maven), + maven_help_path: help_page_path('user/packages/maven_repository/index'), + conan_path: package_registry_project_url(project.id, :conan), + conan_help_path: help_page_path('user/packages/conan_repository/index'), + nuget_path: nuget_package_registry_url(project.id), + nuget_help_path: help_page_path('user/packages/nuget_repository/index'), + pypi_path: pypi_registry_url(project.id), + pypi_setup_path: package_registry_project_url(project.id, :pypi), + pypi_help_path: help_page_path('user/packages/pypi_repository/index'), + composer_path: composer_registry_url(project&.group&.id), + composer_help_path: help_page_path('user/packages/composer_repository/index'), + project_name: project.name, + project_list_url: project_packages_path(project), + group_list_url: project.group ? group_packages_path(project.group) : '', + composer_config_repository_name: composer_config_repository_name(project.group&.id) + } + end end diff --git a/app/helpers/personal_access_tokens_helper.rb b/app/helpers/personal_access_tokens_helper.rb new file mode 100644 index 00000000000..5cc8d21096f --- /dev/null +++ b/app/helpers/personal_access_tokens_helper.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +module PersonalAccessTokensHelper + def personal_access_token_expiration_enforced? + false + end +end diff --git a/app/helpers/projects/alert_management_helper.rb b/app/helpers/projects/alert_management_helper.rb index b46e3eb3bc3..b50e287a509 100644 --- a/app/helpers/projects/alert_management_helper.rb +++ b/app/helpers/projects/alert_management_helper.rb @@ -29,13 +29,13 @@ module Projects::AlertManagementHelper private def has_managed_prometheus?(project) - project.prometheus_service&.prometheus_available? == true + project.prometheus_integration&.prometheus_available? == true end def alert_management_enabled?(project) !!( project.alert_management_alerts.any? || - project.prometheus_service_active? || + project.prometheus_integration_active? || AlertManagement::HttpIntegrationsFinder.new(project, active: true).execute.any? ) end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 8800bd0643c..752e91df9c4 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -350,6 +350,10 @@ module ProjectsHelper nil end + def show_terraform_banner?(project) + project.repository_languages.with_programming_language('HCL').exists? && project.terraform_states.empty? + end + private def tab_ability_map @@ -530,7 +534,8 @@ 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') + pagesHelpPath: help_page_path('user/project/pages/introduction', anchor: 'gitlab-pages-access-control'), + issuesHelpPath: help_page_path('user/project/issues/index') } end @@ -611,21 +616,6 @@ module ProjectsHelper project.unlink_forks_upon_visibility_decrease_enabled? && project.visibility_level > Gitlab::VisibilityLevel::PRIVATE && project.forks_count > 0 end - def settings_container_registry_expiration_policy_available?(project) - Feature.disabled?(:sidebar_refactor, current_user, default_enabled: :yaml) && - can_destroy_container_registry_image?(current_user, project) - end - - def settings_packages_and_registries_enabled?(project) - Feature.enabled?(:sidebar_refactor, current_user, default_enabled: :yaml) && - can_destroy_container_registry_image?(current_user, project) - end - - def can_destroy_container_registry_image?(current_user, project) - Gitlab.config.registry.enabled && - can?(current_user, :destroy_container_image, project) - end - def build_project_breadcrumb_link(project) project_name = simple_sanitize(project.name) diff --git a/app/helpers/registrations_helper.rb b/app/helpers/registrations_helper.rb index 24131e32c6c..91adc36749b 100644 --- a/app/helpers/registrations_helper.rb +++ b/app/helpers/registrations_helper.rb @@ -1,13 +1,6 @@ # frozen_string_literal: true module RegistrationsHelper - def social_signin_enabled? - ::Gitlab.dev_env_or_com? && - omniauth_enabled? && - devise_mapping.omniauthable? && - button_based_providers_enabled? - end - def signup_username_data_attributes { min_length: User::MIN_USERNAME_LENGTH, diff --git a/app/helpers/releases_helper.rb b/app/helpers/releases_helper.rb index de9288121c4..4fa61191ba5 100644 --- a/app/helpers/releases_helper.rb +++ b/app/helpers/releases_helper.rb @@ -4,6 +4,10 @@ module ReleasesHelper IMAGE_PATH = 'illustrations/releases.svg' DOCUMENTATION_PATH = 'user/project/releases/index' + # This needs to be kept in sync with the constant in + # app/assets/javascripts/releases/constants.js + DEFAULT_SORT = 'RELEASED_AT_DESC' + def illustration image_path(IMAGE_PATH) end @@ -20,15 +24,24 @@ module ReleasesHelper documentation_path: help_page }.tap do |data| if can?(current_user, :create_release, @project) - data[:new_release_path] = if Feature.enabled?(:new_release_page, @project, default_enabled: true) - new_project_release_path(@project) - else - new_project_tag_path(@project) - end + data[:new_release_path] = new_project_release_path(@project) end end end + # For simplicity, only optimize non-paginated requests + def use_startup_query_for_index_page? + params[:before].nil? && params[:after].nil? + end + + def index_page_startup_query_variables + { + fullPath: @project.full_path, + sort: DEFAULT_SORT, + first: 1 + } + end + def data_for_show_page { project_id: @project.id, diff --git a/app/helpers/routing/artifacts_helper.rb b/app/helpers/routing/artifacts_helper.rb new file mode 100644 index 00000000000..32df9098e48 --- /dev/null +++ b/app/helpers/routing/artifacts_helper.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +module Routing + module ArtifactsHelper + # Rails path generators are slow because they need to do large regex comparisons + # against the arguments. We can speed this up 10x by generating the strings directly. + + # /*namespace_id/:project_id/-/jobs/:job_id/artifacts/download(.:format) + def fast_download_project_job_artifacts_path(project, job, params = {}) + expose_fast_artifacts_path(project, job, :download, params) + end + + # /*namespace_id/:project_id/-/jobs/:job_id/artifacts/keep(.:format) + def fast_keep_project_job_artifacts_path(project, job) + expose_fast_artifacts_path(project, job, :keep) + end + + # /*namespace_id/:project_id/-/jobs/:job_id/artifacts/browse(/*path) + def fast_browse_project_job_artifacts_path(project, job) + expose_fast_artifacts_path(project, job, :browse) + end + + def expose_fast_artifacts_path(project, job, action, params = {}) + path = "#{project.full_path}/-/jobs/#{job.id}/artifacts/#{action}" + + unless params.empty? + path += "?#{params.to_query}" + end + + Gitlab::Utils.append_path(Gitlab.config.gitlab.relative_url_root, path) + end + + def artifacts_action_path(path, project, build) + action, path_params = path.split('/', 2) + args = [project, build, path_params] + + case action + when 'download' + download_project_job_artifacts_path(*args) + when 'browse' + browse_project_job_artifacts_path(*args) + when 'file' + file_project_job_artifacts_path(*args) + when 'raw' + raw_project_job_artifacts_path(*args) + end + end + end +end diff --git a/app/helpers/routing/graphql_helper.rb b/app/helpers/routing/graphql_helper.rb new file mode 100644 index 00000000000..beefbb9b387 --- /dev/null +++ b/app/helpers/routing/graphql_helper.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Routing + module GraphqlHelper + def graphql_etag_pipeline_path(pipeline) + [api_graphql_path, "pipelines/id/#{pipeline.id}"].join(':') + end + + def graphql_etag_pipeline_sha_path(sha) + [api_graphql_path, "pipelines/sha/#{sha}"].join(':') + end + end +end diff --git a/app/helpers/routing/groups/members_helper.rb b/app/helpers/routing/groups/members_helper.rb new file mode 100644 index 00000000000..eabeacff1d7 --- /dev/null +++ b/app/helpers/routing/groups/members_helper.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Routing + module Groups + module MembersHelper + def group_members_url(group, *args) + group_group_members_url(group, *args) + end + + def group_member_path(group_member, *args) + group_group_member_path(group_member.source, group_member) + end + + def request_access_group_members_path(group, *args) + request_access_group_group_members_path(group) + end + + def leave_group_members_path(group, *args) + leave_group_group_members_path(group) + end + + def approve_access_request_group_member_path(group_member, *args) + approve_access_request_group_group_member_path(group_member.source, group_member) + end + + def resend_invite_group_member_path(group_member, *args) + resend_invite_group_group_member_path(group_member.source, group_member) + end + end + end +end diff --git a/app/helpers/routing/members_helper.rb b/app/helpers/routing/members_helper.rb new file mode 100644 index 00000000000..18f6d06ab3b --- /dev/null +++ b/app/helpers/routing/members_helper.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module Routing + module MembersHelper + def source_members_url(member) + case member.source_type + when 'Namespace' + group_group_members_url(member.source) + when 'Project' + project_project_members_url(member.source) + end + end + end +end diff --git a/app/helpers/routing/pipeline_schedules_helper.rb b/app/helpers/routing/pipeline_schedules_helper.rb new file mode 100644 index 00000000000..6501018a365 --- /dev/null +++ b/app/helpers/routing/pipeline_schedules_helper.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module Routing + module PipelineSchedulesHelper + def pipeline_schedules_path(project, *args) + project_pipeline_schedules_path(project, *args) + end + + def pipeline_schedule_path(schedule, *args) + project = schedule.project + project_pipeline_schedule_path(project, schedule, *args) + end + + def edit_pipeline_schedule_path(schedule) + project = schedule.project + edit_project_pipeline_schedule_path(project, schedule) + end + + def play_pipeline_schedule_path(schedule, *args) + project = schedule.project + play_project_pipeline_schedule_path(project, schedule, *args) + end + + def take_ownership_pipeline_schedule_path(schedule, *args) + project = schedule.project + take_ownership_project_pipeline_schedule_path(project, schedule, *args) + end + end +end diff --git a/app/helpers/routing/projects/members_helper.rb b/app/helpers/routing/projects/members_helper.rb new file mode 100644 index 00000000000..72f88a1408b --- /dev/null +++ b/app/helpers/routing/projects/members_helper.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Routing + module Projects + module MembersHelper + def project_members_url(project, *args) + project_project_members_url(project, *args) + end + + def project_member_path(project_member, *args) + project_project_member_path(project_member.source, project_member) + end + + def request_access_project_members_path(project, *args) + request_access_project_project_members_path(project) + end + + def leave_project_members_path(project, *args) + leave_project_project_members_path(project) + end + + def approve_access_request_project_member_path(project_member, *args) + approve_access_request_project_project_member_path(project_member.source, project_member) + end + + def resend_invite_project_member_path(project_member, *args) + resend_invite_project_project_member_path(project_member.source, project_member) + end + end + end +end diff --git a/app/helpers/routing/projects_helper.rb b/app/helpers/routing/projects_helper.rb new file mode 100644 index 00000000000..fb000b29739 --- /dev/null +++ b/app/helpers/routing/projects_helper.rb @@ -0,0 +1,85 @@ +# frozen_string_literal: true + +module Routing + module ProjectsHelper + def project_tree_path(project, ref = nil, *args) + namespace_project_tree_path(project.namespace, project, ref || @ref || project.repository.root_ref, *args) # rubocop:disable Cop/ProjectPathHelper + end + + def project_commits_path(project, ref = nil, *args) + namespace_project_commits_path(project.namespace, project, ref || @ref || project.repository.root_ref, *args) # rubocop:disable Cop/ProjectPathHelper + end + + def project_ref_path(project, ref_name, *args) + project_commits_path(project, ref_name, *args) + end + + def environment_path(environment, *args) + project_environment_path(environment.project, environment, *args) + end + + def environment_metrics_path(environment, *args) + metrics_project_environment_path(environment.project, environment, *args) + end + + def environment_delete_path(environment, *args) + expose_path(api_v4_projects_environments_path(id: environment.project.id, environment_id: environment.id)) + end + + def issue_path(entity, *args) + project_issue_path(entity.project, entity, *args) + end + + def merge_request_path(entity, *args) + project_merge_request_path(entity.project, entity, *args) + end + + def pipeline_path(pipeline, *args) + project_pipeline_path(pipeline.project, pipeline.id, *args) + end + + def issue_url(entity, *args) + project_issue_url(entity.project, entity, *args) + end + + def merge_request_url(entity, *args) + project_merge_request_url(entity.project, entity, *args) + end + + def pipeline_url(pipeline, *args) + project_pipeline_url(pipeline.project, pipeline.id, *args) + end + + def pipeline_job_url(pipeline, build, *args) + project_job_url(pipeline.project, build.id, *args) + end + + def commits_url(entity, *args) + project_commits_url(entity.project, entity.source_ref, *args) + end + + def commit_url(entity, *args) + project_commit_url(entity.project, entity.sha, *args) + end + + def release_url(entity, *args) + project_release_url(entity.project, entity, *args) + end + + def edit_milestone_path(entity, *args) + if entity.resource_parent.is_a?(Group) + edit_group_milestone_path(entity.resource_parent, entity, *args) + else + edit_project_milestone_path(entity.resource_parent, entity, *args) + end + end + + def toggle_subscription_path(entity, *args) + if entity.is_a?(Issue) + toggle_subscription_project_issue_path(entity.project, entity) + else + toggle_subscription_project_merge_request_path(entity.project, entity) + end + end + end +end diff --git a/app/helpers/routing/snippets_helper.rb b/app/helpers/routing/snippets_helper.rb new file mode 100644 index 00000000000..19450c1d033 --- /dev/null +++ b/app/helpers/routing/snippets_helper.rb @@ -0,0 +1,144 @@ +# frozen_string_literal: true + +module Routing + module SnippetsHelper + def gitlab_snippet_path(snippet, *args) + if snippet.is_a?(ProjectSnippet) + project_snippet_path(snippet.project, snippet, *args) + else + new_args = snippet_query_params(snippet, *args) + snippet_path(snippet, *new_args) + end + end + + def gitlab_snippet_url(snippet, *args) + if snippet.is_a?(ProjectSnippet) + project_snippet_url(snippet.project, snippet, *args) + else + new_args = snippet_query_params(snippet, *args) + snippet_url(snippet, *new_args) + end + end + + def gitlab_dashboard_snippets_path(snippet, *args) + if snippet.is_a?(ProjectSnippet) + project_snippets_path(snippet.project, *args) + else + dashboard_snippets_path + end + end + + def gitlab_raw_snippet_path(snippet, *args) + if snippet.is_a?(ProjectSnippet) + raw_project_snippet_path(snippet.project, snippet, *args) + else + new_args = snippet_query_params(snippet, *args) + raw_snippet_path(snippet, *new_args) + end + end + + def gitlab_raw_snippet_url(snippet, *args) + if snippet.is_a?(ProjectSnippet) + raw_project_snippet_url(snippet.project, snippet, *args) + else + new_args = snippet_query_params(snippet, *args) + raw_snippet_url(snippet, *new_args) + end + end + + def gitlab_raw_snippet_blob_url(snippet, path, ref = nil, **options) + params = { + snippet_id: snippet, + ref: ref || snippet.default_branch, + path: path + } + + if snippet.is_a?(ProjectSnippet) + project_snippet_blob_raw_url(snippet.project, **params, **options) + else + snippet_blob_raw_url(**params, **options) + end + end + + def gitlab_raw_snippet_blob_path(snippet, path, ref = nil, **options) + gitlab_raw_snippet_blob_url(snippet, path, ref, only_path: true, **options) + end + + def gitlab_snippet_notes_path(snippet, *args) + new_args = snippet_query_params(snippet, *args) + snippet_notes_path(snippet, *new_args) + end + + def gitlab_snippet_notes_url(snippet, *args) + new_args = snippet_query_params(snippet, *args) + snippet_notes_url(snippet, *new_args) + end + + def gitlab_snippet_note_path(snippet, note, *args) + new_args = snippet_query_params(snippet, *args) + snippet_note_path(snippet, note, *new_args) + end + + def gitlab_snippet_note_url(snippet, note, *args) + new_args = snippet_query_params(snippet, *args) + snippet_note_url(snippet, note, *new_args) + end + + def gitlab_toggle_award_emoji_snippet_note_path(snippet, note, *args) + new_args = snippet_query_params(snippet, *args) + toggle_award_emoji_snippet_note_path(snippet, note, *new_args) + end + + def gitlab_toggle_award_emoji_snippet_note_url(snippet, note, *args) + new_args = snippet_query_params(snippet, *args) + toggle_award_emoji_snippet_note_url(snippet, note, *new_args) + end + + def gitlab_toggle_award_emoji_snippet_path(snippet, *args) + new_args = snippet_query_params(snippet, *args) + toggle_award_emoji_snippet_path(snippet, *new_args) + end + + def gitlab_toggle_award_emoji_snippet_url(snippet, *args) + new_args = snippet_query_params(snippet, *args) + toggle_award_emoji_snippet_url(snippet, *new_args) + end + + def preview_markdown_path(parent, *args) + return group_preview_markdown_path(parent, *args) if parent.is_a?(Group) + + if @snippet.is_a?(PersonalSnippet) + preview_markdown_snippets_path + else + preview_markdown_project_path(parent, *args) + end + end + + def toggle_award_emoji_personal_snippet_path(*args) + toggle_award_emoji_snippet_path(*args) + end + + def toggle_award_emoji_project_project_snippet_path(*args) + toggle_award_emoji_project_snippet_path(*args) + end + + def toggle_award_emoji_project_project_snippet_url(*args) + toggle_award_emoji_project_snippet_url(*args) + end + + private + + def snippet_query_params(snippet, *args) + opts = case args.last + when Hash + args.pop + when ActionController::Parameters + args.pop.to_h + else + {} + end + + args << opts + end + end +end diff --git a/app/helpers/routing/wiki_helper.rb b/app/helpers/routing/wiki_helper.rb new file mode 100644 index 00000000000..95f9e87de36 --- /dev/null +++ b/app/helpers/routing/wiki_helper.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Routing + module WikiHelper + def wiki_path(wiki, **options) + Gitlab::UrlBuilder.wiki_url(wiki, only_path: true, **options) + end + + def wiki_page_path(wiki, page, **options) + Gitlab::UrlBuilder.wiki_page_url(wiki, page, only_path: true, **options) + end + end +end diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb index e07ee22339a..ec8ed3d6e7f 100644 --- a/app/helpers/search_helper.rb +++ b/app/helpers/search_helper.rb @@ -131,7 +131,7 @@ module SearchHelper end def search_sort_options - [ + options = [ { title: _('Created date'), sortable: true, @@ -149,6 +149,19 @@ module SearchHelper } } ] + + if search_service.scope == 'issues' && Feature.enabled?(:search_sort_issues_by_popularity) + options << { + title: _('Popularity'), + sortable: true, + sortParam: { + asc: 'popularity_asc', + desc: 'popularity_desc' + } + } + end + + options end private @@ -172,12 +185,12 @@ module SearchHelper # Autocomplete results for internal help pages def help_autocomplete [ - { category: "Help", label: _("API Help"), url: help_page_path("api/README") }, + { category: "Help", label: _("API Help"), url: help_page_path("api/index") }, { category: "Help", label: _("Markdown Help"), url: help_page_path("user/markdown") }, { category: "Help", label: _("Permissions Help"), url: help_page_path("user/permissions") }, { category: "Help", label: _("Public Access Help"), url: help_page_path("public_access/public_access") }, { category: "Help", label: _("Rake Tasks Help"), url: help_page_path("raketasks/README") }, - { category: "Help", label: _("SSH Keys Help"), url: help_page_path("ssh/README") }, + { category: "Help", label: _("SSH Keys Help"), url: help_page_path("ssh/index") }, { category: "Help", label: _("System Hooks Help"), url: help_page_path("system_hooks/system_hooks") }, { category: "Help", label: _("Webhooks Help"), url: help_page_path("user/project/integrations/webhooks") } ] @@ -301,7 +314,7 @@ module SearchHelper if @scope == scope li_class = 'active' - count = @search_results.formatted_count(scope) + count = @timeout ? 0 : @search_results.formatted_count(scope) else badge_class = 'js-search-count hidden' badge_data = { url: search_count_path(search_params) } diff --git a/app/helpers/services_helper.rb b/app/helpers/services_helper.rb deleted file mode 100644 index 83000189ab3..00000000000 --- a/app/helpers/services_helper.rb +++ /dev/null @@ -1,185 +0,0 @@ -# frozen_string_literal: true - -module ServicesHelper - def service_event_description(event) - case event - when "push", "push_events" - s_("ProjectService|Trigger event for pushes to the repository.") - when "tag_push", "tag_push_events" - s_("ProjectService|Trigger event for new tags pushed to the repository.") - when "note", "note_events" - s_("ProjectService|Trigger event for new comments.") - when "confidential_note", "confidential_note_events" - s_("ProjectService|Trigger event for new comments on confidential issues.") - when "issue", "issue_events" - s_("ProjectService|Trigger event when an issue is created, updated, or closed.") - when "confidential_issue", "confidential_issue_events" - s_("ProjectService|Trigger event when a confidential issue is created, updated, or closed.") - when "merge_request", "merge_request_events" - s_("ProjectService|Trigger event when a merge request is created, updated, or merged.") - when "pipeline", "pipeline_events" - s_("ProjectService|Trigger event when a pipeline status changes.") - when "wiki_page", "wiki_page_events" - s_("ProjectService|Trigger event when a wiki page is created or updated.") - when "commit", "commit_events" - s_("ProjectService|Trigger event when a commit is created or updated.") - when "deployment" - s_("ProjectService|Trigger event when a deployment starts or finishes.") - when "alert" - s_("ProjectService|Trigger event when a new, unique alert is recorded.") - end - end - - def service_event_field_name(event) - event = event.pluralize if %w[merge_request issue confidential_issue].include?(event) - "#{event}_events" - end - - def scoped_integrations_path - if @project.present? - project_settings_integrations_path(@project) - elsif @group.present? - group_settings_integrations_path(@group) - else - integrations_admin_application_settings_path - end - end - - def scoped_integration_path(integration) - if @project.present? - project_service_path(@project, integration) - elsif @group.present? - group_settings_integration_path(@group, integration) - else - admin_application_settings_integration_path(integration) - end - end - - def scoped_edit_integration_path(integration) - if @project.present? - edit_project_service_path(@project, integration) - elsif @group.present? - edit_group_settings_integration_path(@group, integration) - else - edit_admin_application_settings_integration_path(integration) - end - end - - def scoped_test_integration_path(integration) - if @project.present? - test_project_service_path(@project, integration) - elsif @group.present? - test_group_settings_integration_path(@group, integration) - else - test_admin_application_settings_integration_path(integration) - end - end - - def scoped_reset_integration_path(integration, group: nil) - return '' unless integration.persisted? - - if group.present? - reset_group_settings_integration_path(group, integration) - else - reset_admin_application_settings_integration_path(integration) - end - 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, - type: integration.to_param, - merge_request_events: integration.merge_requests_events.to_s, - 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, - 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), - reset_path: scoped_reset_integration_path(integration, group: group) - } - - if integration.is_a?(Integrations::Jira) - 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) - ServiceEventSerializer.new(service: integration).represent(integration.configurable_events).to_json - end - - def fields_for_service(integration) - 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 instance_level_integrations? - !Gitlab.com? - end - - def jira_issue_breadcrumb_link(issue_reference) - link_to '', { class: 'gl-display-flex gl-align-items-center gl-white-space-nowrap' } do - icon = image_tag image_path('illustrations/logos/jira.svg'), width: 15, height: 15, class: 'gl-mr-2' - [icon, issue_reference].join.html_safe - end - end - - extend self - - private - - def integration_level(integration) - if integration.instance_level? - 'instance' - elsif integration.group_level? - 'group' - else - '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 - - def show_service_templates_nav_link? - Feature.disabled?(:disable_service_templates, type: :development, default_enabled: :yaml) - end -end - -ServicesHelper.prepend_mod_with('ServicesHelper') - -# The methods in `EE::ServicesHelper` should be available as both instance and -# class methods. -ServicesHelper.extend_mod_with('ServicesHelper') diff --git a/app/helpers/sessions_helper.rb b/app/helpers/sessions_helper.rb index ef737b25bc7..117f662fec6 100644 --- a/app/helpers/sessions_helper.rb +++ b/app/helpers/sessions_helper.rb @@ -1,6 +1,15 @@ # frozen_string_literal: true module SessionsHelper + include Gitlab::Utils::StrongMemoize + + def recently_confirmed_com? + strong_memoize(:recently_confirmed_com) do + ::Gitlab.dev_env_or_com? && + !!flash[:notice]&.include?(t(:confirmed, scope: [:devise, :confirmations])) + end + end + def unconfirmed_email? flash[:alert] == t(:unconfirmed, scope: [:devise, :failure]) end diff --git a/app/helpers/sidebars_helper.rb b/app/helpers/sidebars_helper.rb index 39ad8ed8a0f..77af6e37099 100644 --- a/app/helpers/sidebars_helper.rb +++ b/app/helpers/sidebars_helper.rb @@ -2,34 +2,75 @@ module SidebarsHelper def sidebar_tracking_attributes_by_object(object) + sidebar_attributes_for_object(object).fetch(:tracking_attrs, {}) + end + + def sidebar_qa_selector(object) + sidebar_attributes_for_object(object).fetch(:sidebar_qa_selector, nil) + end + + def scope_qa_menu_item(object) + sidebar_attributes_for_object(object).fetch(:scope_qa_menu_item, nil) + end + + def scope_avatar_classes(object) + %w[avatar-container rect-avatar s32].tap do |klasses| + klass = sidebar_attributes_for_object(object).fetch(:scope_avatar_class, nil) + klasses << klass if klass + 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 + + def group_sidebar_context(group, user) + context_data = group_sidebar_context_data(group, user) + + Sidebars::Groups::Context.new(**context_data) + end + + private + + def sidebar_attributes_for_object(object) case object when Project - sidebar_project_tracking_attrs + sidebar_project_attributes when Group - sidebar_group_tracking_attrs + sidebar_group_attributes when User - sidebar_user_profile_tracking_attrs + sidebar_user_attributes 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) + def sidebar_project_attributes + { + tracking_attrs: sidebar_project_tracking_attrs, + sidebar_qa_selector: 'project_sidebar', + scope_qa_menu_item: 'Project scope', + scope_avatar_class: 'project_avatar' + } end - def sidebar_refactor_enabled? - Feature.enabled?(:sidebar_refactor, current_user, default_enabled: :yaml) + def sidebar_group_attributes + { + tracking_attrs: sidebar_group_tracking_attrs, + sidebar_qa_selector: 'group_sidebar', + scope_qa_menu_item: 'Group scope', + scope_avatar_class: 'group_avatar' + } end - def sidebar_refactor_disabled? - !sidebar_refactor_enabled? + def sidebar_user_attributes + { + tracking_attrs: sidebar_user_profile_tracking_attrs + } end - private - def sidebar_project_tracking_attrs tracking_attrs('projects_side_navigation', 'render', 'projects_side_navigation') end @@ -54,6 +95,13 @@ module SidebarsHelper show_cluster_hint: show_gke_cluster_integration_callout?(project) } end + + def group_sidebar_context_data(group, user) + { + current_user: user, + container: group + } + end end SidebarsHelper.prepend_mod_with('SidebarsHelper') diff --git a/app/helpers/sorting_helper.rb b/app/helpers/sorting_helper.rb index 0bb9e9e9bdd..da32dfb0b9b 100644 --- a/app/helpers/sorting_helper.rb +++ b/app/helpers/sorting_helper.rb @@ -26,6 +26,9 @@ module SortingHelper sort_value_recently_updated => sort_title_recently_updated, sort_value_popularity => sort_title_popularity, sort_value_priority => sort_title_priority, + sort_value_merged_date => sort_title_merged_date, + sort_value_merged_recently => sort_title_merged_recently, + sort_value_merged_earlier => sort_title_merged_earlier, sort_value_upvotes => sort_title_upvotes, sort_value_contacted_date => sort_title_contacted_date, sort_value_relative_position => sort_title_relative_position, @@ -168,19 +171,6 @@ module SortingHelper } end - def member_sort_options_hash - { - sort_value_access_level_asc => sort_title_access_level_asc, - sort_value_access_level_desc => sort_title_access_level_desc, - sort_value_last_joined => sort_title_last_joined, - sort_value_name => sort_title_name_asc, - sort_value_name_desc => sort_title_name_desc, - sort_value_oldest_joined => sort_title_oldest_joined, - sort_value_oldest_signin => sort_title_oldest_signin, - sort_value_recently_signin => sort_title_recently_signin - } - end - def sortable_item(item, path, sorted_by) link_to item, path, class: sorted_by == item ? 'is-active' : '' end @@ -191,6 +181,7 @@ module SortingHelper sort_value_oldest_updated => sort_value_recently_updated, sort_value_milestone_later => sort_value_milestone, sort_value_due_date_later => sort_value_due_date, + sort_value_merged_recently => sort_value_merged_date, sort_value_least_popular => sort_value_popularity } end @@ -203,6 +194,8 @@ module SortingHelper sort_value_milestone => sort_value_milestone_later, sort_value_due_date => sort_value_due_date_later, sort_value_due_date_soon => sort_value_due_date_later, + sort_value_merged_date => sort_value_merged_recently, + sort_value_merged_earlier => sort_value_merged_recently, sort_value_popularity => sort_value_least_popular, sort_value_most_popular => sort_value_least_popular }.merge(issuable_sort_option_overrides) @@ -223,7 +216,7 @@ module SortingHelper def sort_direction_icon(sort_value) case sort_value - when sort_value_milestone, sort_value_due_date, /_asc\z/ + when sort_value_milestone, sort_value_due_date, sort_value_merged_date, /_asc\z/ 'sort-lowest' else 'sort-highest' diff --git a/app/helpers/sorting_titles_values_helper.rb b/app/helpers/sorting_titles_values_helper.rb index 28d70f1db45..9b839f4e9bc 100644 --- a/app/helpers/sorting_titles_values_helper.rb +++ b/app/helpers/sorting_titles_values_helper.rb @@ -2,14 +2,6 @@ module SortingTitlesValuesHelper # Titles. - def sort_title_access_level_asc - s_('SortOptions|Access level, ascending') - end - - def sort_title_access_level_desc - s_('SortOptions|Access level, descending') - end - def sort_title_created_date s_('SortOptions|Created date') end @@ -34,6 +26,18 @@ module SortingTitlesValuesHelper s_('SortOptions|Label priority') end + def sort_title_merged_date + s_('SortOptions|Merged date') + end + + def sort_title_merged_recently + s_('SortOptions|Merged recently') + end + + def sort_title_merged_earlier + s_('SortOptions|Merged earlier') + end + def sort_title_largest_group s_('SortOptions|Largest group') end @@ -42,10 +46,6 @@ module SortingTitlesValuesHelper s_('SortOptions|Largest repository') end - def sort_title_last_joined - s_('SortOptions|Last joined') - end - def sort_title_latest_activity s_('SortOptions|Last updated') end @@ -82,10 +82,6 @@ module SortingTitlesValuesHelper s_('SortOptions|Oldest created') end - def sort_title_oldest_joined - s_('SortOptions|Oldest joined') - end - def sort_title_oldest_signin s_('SortOptions|Oldest sign in') end @@ -167,14 +163,6 @@ module SortingTitlesValuesHelper end # Values. - def sort_value_access_level_asc - 'access_level_asc' - end - - def sort_value_access_level_desc - 'access_level_desc' - end - def sort_value_created_date 'created_date' end @@ -199,6 +187,18 @@ module SortingTitlesValuesHelper 'label_priority' end + def sort_value_merged_date + 'merged_at' + end + + def sort_value_merged_recently + 'merged_at_desc' + end + + def sort_value_merged_earlier + 'merged_at_asc' + end + def sort_value_largest_group 'storage_size_desc' end @@ -207,10 +207,6 @@ module SortingTitlesValuesHelper 'storage_size_desc' end - def sort_value_last_joined - 'last_joined' - end - def sort_value_latest_activity 'latest_activity_desc' end @@ -247,10 +243,6 @@ module SortingTitlesValuesHelper 'oldest_sign_in' end - def sort_value_oldest_joined - 'oldest_joined' - end - def sort_value_oldest_updated 'updated_asc' end diff --git a/app/helpers/tracking_helper.rb b/app/helpers/tracking_helper.rb index 7957038c21e..3f53bd535b2 100644 --- a/app/helpers/tracking_helper.rb +++ b/app/helpers/tracking_helper.rb @@ -17,6 +17,6 @@ module TrackingHelper def tracking_enabled? Rails.env.production? && - ::Gitlab::CurrentSettings.snowplow_enabled? + ::Gitlab::Tracking.enabled? end end diff --git a/app/helpers/user_callouts_helper.rb b/app/helpers/user_callouts_helper.rb index c44da915105..4e6af298fcd 100644 --- a/app/helpers/user_callouts_helper.rb +++ b/app/helpers/user_callouts_helper.rb @@ -43,7 +43,7 @@ module UserCalloutsHelper end def show_customize_homepage_banner? - !user_dismissed?(CUSTOMIZE_HOMEPAGE) + current_user.default_dashboard? && !user_dismissed?(CUSTOMIZE_HOMEPAGE) end def show_feature_flags_new_version? diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb index c1d05c2d3cf..93a0166f43e 100644 --- a/app/helpers/users_helper.rb +++ b/app/helpers/users_helper.rb @@ -123,130 +123,38 @@ module UsersHelper !user.confirmed? end - def user_block_data(user, message) - { - path: block_admin_user_path(user), - method: 'put', - modal_attributes: { - title: s_('AdminUsers|Block user %{username}?') % { username: sanitize_name(user.name) }, - messageHtml: message, - okVariant: 'warning', - okTitle: s_('AdminUsers|Block') - }.to_json - } - end - - def user_unblock_data(user) - { - path: unblock_admin_user_path(user), - method: 'put', - modal_attributes: { - title: s_('AdminUsers|Unblock user %{username}?') % { username: sanitize_name(user.name) }, - message: s_('AdminUsers|You can always block their account again if needed.'), - okVariant: 'info', - okTitle: s_('AdminUsers|Unblock') - }.to_json - } - end - - def user_block_effects - header = tag.p s_('AdminUsers|Blocking user has the following effects:') - - list = tag.ul do - concat tag.li s_('AdminUsers|User will not be able to login') - concat tag.li s_('AdminUsers|User will not be able to access git repositories') - concat tag.li s_('AdminUsers|Personal projects will be left') - concat tag.li s_('AdminUsers|Owned groups will be left') - end - - header + list - end - - def user_ban_data(user) - { - path: ban_admin_user_path(user), - method: 'put', - modal_attributes: { - title: s_('AdminUsers|Ban user %{username}?') % { username: sanitize_name(user.name) }, - message: s_('AdminUsers|You can unban their account in the future. Their data remains intact.'), - okVariant: 'warning', - okTitle: s_('AdminUsers|Ban') - }.to_json - } - end - - def user_unban_data(user) - { - path: unban_admin_user_path(user), - method: 'put', - modal_attributes: { - title: s_('AdminUsers|Unban %{username}?') % { username: sanitize_name(user.name) }, - message: s_('AdminUsers|You ban their account in the future if necessary.'), - okVariant: 'info', - okTitle: s_('AdminUsers|Unban') - }.to_json - } - end - - def user_ban_effects - header = tag.p s_('AdminUsers|Banning the user has the following effects:') - - list = tag.ul do - concat tag.li s_('AdminUsers|User will be blocked') - end - - link_start = ''.html_safe % { url: help_page_path("user/admin_area/moderate_users", anchor: "ban-a-user") } - info = tag.p s_('AdminUsers|Learn more about %{link_start}banned users.%{link_end}').html_safe % { link_start: link_start, link_end: ''.html_safe } - - header + list + info - end - def ban_feature_available? Feature.enabled?(:ban_user_feature_flag) end - def user_deactivation_data(user, message) - { - path: deactivate_admin_user_path(user), - method: 'put', - modal_attributes: { - title: s_('AdminUsers|Deactivate user %{username}?') % { username: sanitize_name(user.name) }, - messageHtml: message, - okVariant: 'warning', - okTitle: s_('AdminUsers|Deactivate') - }.to_json - } - end + def confirm_user_data(user) + message = if user.unconfirmed_email.present? + _('This user has an unconfirmed email address (%{email}). You may force a confirmation.') % { email: user.unconfirmed_email } + else + _('This user has an unconfirmed email address. You may force a confirmation.') + end + + modal_attributes = Gitlab::Json.dump({ + title: s_('AdminUsers|Confirm user %{username}?') % { username: sanitize_name(user.name) }, + messageHtml: message, + actionPrimary: { + text: s_('AdminUsers|Confirm user'), + attributes: [{ variant: 'info', 'data-qa-selector': 'confirm_user_confirm_button' }] + }, + actionSecondary: { + text: _('Cancel'), + attributes: [{ variant: 'default' }] + } + }) - def user_activation_data(user) { - path: activate_admin_user_path(user), + path: confirm_admin_user_path(user), method: 'put', - modal_attributes: { - title: s_('AdminUsers|Activate user %{username}?') % { username: sanitize_name(user.name) }, - message: s_('AdminUsers|You can always deactivate their account again if needed.'), - okVariant: 'info', - okTitle: s_('AdminUsers|Activate') - }.to_json + modal_attributes: modal_attributes, + qa_selector: 'confirm_user_button' } end - def user_deactivation_effects - header = tag.p s_('AdminUsers|Deactivating a user has the following effects:') - - list = tag.ul do - concat tag.li s_('AdminUsers|The user will be logged out') - concat tag.li s_('AdminUsers|The user will not be able to access git repositories') - concat tag.li s_('AdminUsers|The user will not be able to access the API') - concat tag.li s_('AdminUsers|The user will not receive any notifications') - concat tag.li s_('AdminUsers|The user will not be able to use slash commands') - concat tag.li s_('AdminUsers|When the user logs back in, their account will reactivate as a fully active account') - concat tag.li s_('AdminUsers|Personal projects, group and user history will be left intact') - end - - header + list - end - def user_display_name(user) return s_('UserProfile|Blocked user') if user.blocked? @@ -256,6 +164,13 @@ module UsersHelper user.name end + def admin_user_actions_data_attributes(user) + { + user: Admin::UserEntity.represent(user, { current_user: current_user }).to_json, + paths: admin_users_paths.to_json + } + end + private def admin_users_paths @@ -270,7 +185,9 @@ module UsersHelper unlock: unlock_admin_user_path(:id), delete: admin_user_path(:id), delete_with_contributions: admin_user_path(:id), - admin_user: admin_user_path(:id) + admin_user: admin_user_path(:id), + ban: ban_admin_user_path(:id), + unban: unban_admin_user_path(:id) } end diff --git a/app/helpers/whats_new_helper.rb b/app/helpers/whats_new_helper.rb index 5fca00c5dce..ccccfcb930b 100644 --- a/app/helpers/whats_new_helper.rb +++ b/app/helpers/whats_new_helper.rb @@ -32,11 +32,11 @@ module WhatsNewHelper def whats_new_variants_description(variant) case variant when 'all_tiers' - _("What's new presents new features from all tiers to help you keep track of all new features.") + _("Include new features from all tiers.") when 'current_tier' - _("What's new presents new features for your current subscription tier, while hiding new features not available to your subscription tier.") + _("Only include features new to your current subscription tier.") when 'disabled' - _("What's new is disabled and can no longer be viewed.") + _("%{italic_start}What's new%{italic_end} is inactive and cannot be viewed.").html_safe % { italic_start: ''.html_safe, italic_end: ''.html_safe } end end end -- cgit v1.2.3