summaryrefslogtreecommitdiff
path: root/app/helpers
diff options
context:
space:
mode:
Diffstat (limited to 'app/helpers')
-rw-r--r--app/helpers/application_settings_helper.rb20
-rw-r--r--app/helpers/auth_helper.rb7
-rw-r--r--app/helpers/blob_helper.rb25
-rw-r--r--app/helpers/branches_helper.rb6
-rw-r--r--app/helpers/breadcrumbs_helper.rb49
-rw-r--r--app/helpers/ci/jobs_helper.rb3
-rw-r--r--app/helpers/ci/pipeline_editor_helper.rb12
-rw-r--r--app/helpers/ci/runners_helper.rb8
-rw-r--r--app/helpers/commits_helper.rb2
-rw-r--r--app/helpers/defer_script_tag_helper.rb4
-rw-r--r--app/helpers/diff_helper.rb30
-rw-r--r--app/helpers/dropdowns_helper.rb5
-rw-r--r--app/helpers/emails_helper.rb18
-rw-r--r--app/helpers/gitlab_routing_helper.rb16
-rw-r--r--app/helpers/gitpod_helper.rb5
-rw-r--r--app/helpers/groups_helper.rb17
-rw-r--r--app/helpers/icons_helper.rb65
-rw-r--r--app/helpers/invite_members_helper.rb4
-rw-r--r--app/helpers/issuables_helper.rb26
-rw-r--r--app/helpers/issues_helper.rb23
-rw-r--r--app/helpers/notes_helper.rb1
-rw-r--r--app/helpers/notifications_helper.rb1
-rw-r--r--app/helpers/operations_helper.rb4
-rw-r--r--app/helpers/page_layout_helper.rb51
-rw-r--r--app/helpers/preferences_helper.rb4
-rw-r--r--app/helpers/profiles_helper.rb14
-rw-r--r--app/helpers/projects/terraform_helper.rb10
-rw-r--r--app/helpers/projects_helper.rb45
-rw-r--r--app/helpers/recaptcha_experiment_helper.rb9
-rw-r--r--app/helpers/recaptcha_helper.rb7
-rw-r--r--app/helpers/releases_helper.rb9
-rw-r--r--app/helpers/search_helper.rb102
-rw-r--r--app/helpers/services_helper.rb12
-rw-r--r--app/helpers/sorting_helper.rb29
-rw-r--r--app/helpers/sourcegraph_helper.rb26
-rw-r--r--app/helpers/stat_anchors_helper.rb24
-rw-r--r--app/helpers/suggest_pipeline_helper.rb2
-rw-r--r--app/helpers/time_helper.rb4
-rw-r--r--app/helpers/tree_helper.rb20
-rw-r--r--app/helpers/user_callouts_helper.rb5
-rw-r--r--app/helpers/users_helper.rb63
-rw-r--r--app/helpers/visibility_level_helper.rb4
-rw-r--r--app/helpers/whats_new_helper.rb6
43 files changed, 643 insertions, 154 deletions
diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb
index 9c408efe8cd..512649b3008 100644
--- a/app/helpers/application_settings_helper.rb
+++ b/app/helpers/application_settings_helper.rb
@@ -199,14 +199,14 @@ module ApplicationSettingsHelper
:default_projects_limit,
:default_snippet_visibility,
:disabled_oauth_sign_in_sources,
- :domain_blacklist,
- :domain_blacklist_enabled,
- # TODO Remove domain_blacklist_raw in APIv5 (See https://gitlab.com/gitlab-org/gitlab-foss/issues/67204)
- :domain_blacklist_raw,
- :domain_whitelist,
- # TODO Remove domain_whitelist_raw in APIv5 (See https://gitlab.com/gitlab-org/gitlab-foss/issues/67204)
- :domain_whitelist_raw,
- :outbound_local_requests_whitelist_raw,
+ :domain_denylist,
+ :domain_denylist_enabled,
+ # TODO Remove domain_denylist_raw in APIv5 (See https://gitlab.com/gitlab-org/gitlab-foss/issues/67204)
+ :domain_denylist_raw,
+ :domain_allowlist,
+ # TODO Remove domain_allowlist_raw in APIv5 (See https://gitlab.com/gitlab-org/gitlab-foss/issues/67204)
+ :domain_allowlist_raw,
+ :outbound_local_requests_allowlist_raw,
:dsa_key_restriction,
:ecdsa_key_restriction,
:ed25519_key_restriction,
@@ -394,6 +394,10 @@ module ApplicationSettingsHelper
def show_documentation_base_url_field?
Feature.enabled?(:help_page_documentation_redirect)
end
+
+ def signup_enabled?
+ !!Gitlab::CurrentSettings.signup_enabled
+ end
end
ApplicationSettingsHelper.prepend_if_ee('EE::ApplicationSettingsHelper')
diff --git a/app/helpers/auth_helper.rb b/app/helpers/auth_helper.rb
index 7f8cb66a84f..cc43ea85a11 100644
--- a/app/helpers/auth_helper.rb
+++ b/app/helpers/auth_helper.rb
@@ -151,6 +151,13 @@ module AuthHelper
current_user.allow_password_authentication_for_web? && !current_user.password_automatically_set?
end
+ def google_tag_manager_enabled?
+ Gitlab.com? &&
+ extra_config.has_key?('google_tag_manager_id') &&
+ extra_config.google_tag_manager_id.present? &&
+ !current_user
+ end
+
extend self
end
diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb
index 806fea3ab44..981b5e4d92b 100644
--- a/app/helpers/blob_helper.rb
+++ b/app/helpers/blob_helper.rb
@@ -26,6 +26,25 @@ module BlobHelper
File.join(segments)
end
+ def ide_merge_request_path(merge_request, path = '')
+ target_project = merge_request.target_project
+ source_project = merge_request.source_project
+
+ if merge_request.merged?
+ branch = merge_request.target_branch_exists? ? merge_request.target_branch : target_project.default_branch
+
+ return ide_edit_path(target_project, branch, path)
+ end
+
+ if target_project != source_project
+ params = { target_project: target_project.full_path }
+ end
+
+ result = File.join(ide_path, 'project', source_project.full_path, 'merge_requests', merge_request.to_param)
+ result += "?#{params.to_query}" unless params.nil?
+ 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))
end
@@ -49,7 +68,7 @@ module BlobHelper
def edit_blob_button(project = @project, ref = @ref, path = @path, options = {})
return unless blob = readable_blob(options, path, project, ref)
- common_classes = "btn btn-primary js-edit-blob ml-2 #{options[:extra_class]}"
+ common_classes = "btn btn-primary js-edit-blob gl-mr-3 #{options[:extra_class]}"
data = { track_event: 'click_edit', track_label: 'Edit' }
if Feature.enabled?(:web_ide_primary_edit, project.group)
@@ -69,7 +88,7 @@ module BlobHelper
def ide_edit_button(project = @project, ref = @ref, path = @path, blob:)
return unless blob
- common_classes = 'btn btn-primary ide-edit-button ml-2'
+ common_classes = 'btn btn-primary ide-edit-button gl-mr-3'
data = { track_event: 'click_edit_ide', track_label: 'Web IDE' }
unless Feature.enabled?(:web_ide_primary_edit, project.group)
@@ -363,7 +382,7 @@ module BlobHelper
end
def show_suggest_pipeline_creation_celebration?
- experiment_enabled?(:suggest_pipeline) &&
+ Feature.enabled?(:suggest_pipeline, default_enabled: true) &&
@blob.path == Gitlab::FileDetector::PATTERNS[:gitlab_ci] &&
@blob.auxiliary_viewer&.valid?(project: @project, sha: @commit.sha, user: current_user) &&
@project.uses_default_ci_config? &&
diff --git a/app/helpers/branches_helper.rb b/app/helpers/branches_helper.rb
index 2a0242fe2fa..8f87cd5bfe0 100644
--- a/app/helpers/branches_helper.rb
+++ b/app/helpers/branches_helper.rb
@@ -13,7 +13,11 @@ module BranchesHelper
return [] unless access_levels
access_levels.map do |level|
- { id: level.id, type: :role, access_level: level.access_level }
+ if level.type == :deploy_key
+ { id: level.id, type: level.type, deploy_key_id: level.deploy_key_id }
+ else
+ { id: level.id, type: :role, access_level: level.access_level }
+ end
end
end
end
diff --git a/app/helpers/breadcrumbs_helper.rb b/app/helpers/breadcrumbs_helper.rb
index b067376cea0..ade7c48b03f 100644
--- a/app/helpers/breadcrumbs_helper.rb
+++ b/app/helpers/breadcrumbs_helper.rb
@@ -32,4 +32,53 @@ module BreadcrumbsHelper
@breadcrumb_dropdown_links[location] ||= []
@breadcrumb_dropdown_links[location] << link
end
+
+ def push_to_schema_breadcrumb(text, link)
+ list_item = schema_list_item(text, link, schema_breadcrumb_list.size + 1)
+
+ schema_breadcrumb_list.push(list_item)
+ end
+
+ def schema_breadcrumb_json
+ {
+ '@context': 'https://schema.org',
+ '@type': 'BreadcrumbList',
+ 'itemListElement': build_item_list_elements
+ }.to_json
+ end
+
+ private
+
+ def schema_breadcrumb_list
+ @schema_breadcrumb_list ||= []
+ end
+
+ def build_item_list_elements
+ return @schema_breadcrumb_list unless @breadcrumbs_extra_links&.any?
+
+ last_element = schema_breadcrumb_list.pop
+
+ @breadcrumbs_extra_links.each do |el|
+ push_to_schema_breadcrumb(el[:text], el[:link])
+ end
+
+ last_element['position'] = schema_breadcrumb_list.last['position'] + 1
+ schema_breadcrumb_list.push(last_element)
+ end
+
+ def schema_list_item(text, link, position)
+ {
+ '@type' => 'ListItem',
+ 'position' => position,
+ 'name' => text,
+ 'item' => ensure_absolute_link(link)
+ }
+ end
+
+ def ensure_absolute_link(link)
+ url = URI.parse(link)
+ url.absolute? ? link : URI.join(request.base_url, link).to_s
+ rescue URI::InvalidURIError
+ "#{request.base_url}#{request.path}"
+ end
end
diff --git a/app/helpers/ci/jobs_helper.rb b/app/helpers/ci/jobs_helper.rb
index e876eb64029..d47f6195c61 100644
--- a/app/helpers/ci/jobs_helper.rb
+++ b/app/helpers/ci/jobs_helper.rb
@@ -15,7 +15,8 @@ module Ci
"build_status" => @build.status,
"build_stage" => @build.stage,
"log_state" => '',
- "build_options" => javascript_build_options
+ "build_options" => javascript_build_options,
+ "retry_outdated_job_docs_url" => help_page_path('ci/pipelines/settings', anchor: 'retry-outdated-jobs')
}
end
end
diff --git a/app/helpers/ci/pipeline_editor_helper.rb b/app/helpers/ci/pipeline_editor_helper.rb
new file mode 100644
index 00000000000..3f48b2687b9
--- /dev/null
+++ b/app/helpers/ci/pipeline_editor_helper.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+module Ci
+ module PipelineEditorHelper
+ include ChecksCollaboration
+
+ def can_view_pipeline_editor?(project)
+ can_collaborate_with_project?(project) &&
+ Gitlab::Ci::Features.ci_pipeline_editor_page_enabled?(project)
+ end
+ end
+end
diff --git a/app/helpers/ci/runners_helper.rb b/app/helpers/ci/runners_helper.rb
index 552acf61f47..432aad663e4 100644
--- a/app/helpers/ci/runners_helper.rb
+++ b/app/helpers/ci/runners_helper.rb
@@ -2,13 +2,15 @@
module Ci
module RunnersHelper
+ include IconsHelper
+
def runner_status_icon(runner)
status = runner.status
case status
when :not_connected
- content_tag :i, nil,
- class: "fa fa-warning",
- title: "New runner. Has not connected yet"
+ content_tag(:span, title: "New runner. Has not connected yet") do
+ sprite_icon("warning-solid", size: 24, css_class: "gl-vertical-align-bottom!")
+ end
when :online, :offline, :paused
content_tag :i, nil,
diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb
index f8490d79427..02d48386e31 100644
--- a/app/helpers/commits_helper.rb
+++ b/app/helpers/commits_helper.rb
@@ -196,7 +196,7 @@ module CommitsHelper
return unless external_url
link_to(external_url, class: 'btn btn-file-option has-tooltip', target: '_blank', rel: 'noopener noreferrer', title: "View on #{environment.formatted_external_url}", data: { container: 'body' }) do
- icon('external-link')
+ sprite_icon('external-link')
end
end
diff --git a/app/helpers/defer_script_tag_helper.rb b/app/helpers/defer_script_tag_helper.rb
index d91c6d52683..be927c67aaa 100644
--- a/app/helpers/defer_script_tag_helper.rb
+++ b/app/helpers/defer_script_tag_helper.rb
@@ -1,7 +1,9 @@
# frozen_string_literal: true
module DeferScriptTagHelper
- # Override the default ActionView `javascript_include_tag` helper to support page specific deferred loading
+ # Override the default ActionView `javascript_include_tag` helper to support page specific deferred loading.
+ # PLEASE NOTE: `defer` is also critical so that we don't run JavaScript entrypoints before the DOM is ready.
+ # Please see https://gitlab.com/groups/gitlab-org/-/epics/4538#note_432159769.
def javascript_include_tag(*sources)
super(*sources, defer: true)
end
diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb
index 7c254e069f6..d6d06434590 100644
--- a/app/helpers/diff_helper.rb
+++ b/app/helpers/diff_helper.rb
@@ -64,15 +64,17 @@ module DiffHelper
else
# `sub` and substring-ing would destroy HTML-safeness of `line`
if line.start_with?('+', '-', ' ')
- line.dup.tap do |line|
- line[0] = ''
- end
+ line[1, line.length]
else
line
end
end
end
+ def diff_link_number(line_type, match, text)
+ line_type == match ? " " : text
+ end
+
def parallel_diff_discussions(left, right, diff_file)
return unless @grouped_diff_discussions
@@ -201,6 +203,14 @@ module DiffHelper
set_secure_cookie(:diff_view, params.delete(:view), type: CookiesHelper::COOKIE_TYPE_PERMANENT) if params[:view].present?
end
+ def unified_diff_lines_view_type(project)
+ if Feature.enabled?(:unified_diff_lines, project, default_enabled: true)
+ 'inline'
+ else
+ diff_view
+ end
+ end
+
private
def diff_btn(title, name, selected)
@@ -250,4 +260,18 @@ module DiffHelper
"...#{path[-(max - 3)..-1]}"
end
+
+ def code_navigation_path(diffs)
+ Gitlab::CodeNavigationPath.new(merge_request.project, diffs.diff_refs&.head_sha)
+ end
+
+ def conflicts
+ return unless options[:merge_ref_head_diff]
+
+ conflicts_service = MergeRequests::Conflicts::ListService.new(merge_request) # rubocop:disable CodeReuse/ServiceClass
+
+ return unless conflicts_service.can_be_resolved_in_ui?
+
+ conflicts_service.conflicts.files.index_by(&:our_path)
+ end
end
diff --git a/app/helpers/dropdowns_helper.rb b/app/helpers/dropdowns_helper.rb
index e1378e485e4..e10e9a83b05 100644
--- a/app/helpers/dropdowns_helper.rb
+++ b/app/helpers/dropdowns_helper.rb
@@ -129,8 +129,7 @@ module DropdownsHelper
end
def dropdown_loading
- content_tag :div, class: "dropdown-loading" do
- icon('spinner spin')
- end
+ spinner = loading_icon(container: true, size: "md", css_class: "gl-mt-7")
+ content_tag(:div, spinner, class: "dropdown-loading")
end
end
diff --git a/app/helpers/emails_helper.rb b/app/helpers/emails_helper.rb
index 0a0dc77e5e2..017981c8c8e 100644
--- a/app/helpers/emails_helper.rb
+++ b/app/helpers/emails_helper.rb
@@ -214,6 +214,24 @@ module EmailsHelper
end
end
+ def instance_access_request_text(user, format: nil)
+ gitlab_host = Gitlab.config.gitlab.host
+
+ _('%{username} has asked for a GitLab account on your instance %{host}:') % { username: sanitize_name(user.name), host: gitlab_host }
+ end
+
+ def instance_access_request_link(user, format: nil)
+ url = admin_user_url(user)
+
+ case format
+ when :html
+ user_page = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: url }
+ _("Click %{link_start}here%{link_end} to view the request.").html_safe % { link_start: user_page, link_end: '</a>'.html_safe }
+ else
+ _('Click %{link_to} to view the request.') % { link_to: url }
+ end
+ end
+
def contact_your_administrator_text
_('Please contact your administrator with any questions.')
end
diff --git a/app/helpers/gitlab_routing_helper.rb b/app/helpers/gitlab_routing_helper.rb
index 7df6bef7914..7ae00a70671 100644
--- a/app/helpers/gitlab_routing_helper.rb
+++ b/app/helpers/gitlab_routing_helper.rb
@@ -70,6 +70,10 @@ module GitlabRoutingHelper
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)
@@ -343,18 +347,6 @@ module GitlabRoutingHelper
Gitlab::UrlBuilder.wiki_page_url(wiki, page, only_path: true, **options)
end
- def gitlab_ide_merge_request_path(merge_request)
- target_project = merge_request.target_project
- source_project = merge_request.source_project
- params = {}
-
- if target_project != source_project
- params = { target_project: target_project.full_path }
- end
-
- ide_merge_request_path(source_project.namespace, source_project, merge_request, params)
- end
-
private
def snippet_query_params(snippet, *args)
diff --git a/app/helpers/gitpod_helper.rb b/app/helpers/gitpod_helper.rb
index 7edf7dc218d..875a44c51bb 100644
--- a/app/helpers/gitpod_helper.rb
+++ b/app/helpers/gitpod_helper.rb
@@ -2,9 +2,6 @@
module GitpodHelper
def gitpod_enable_description
- link_start = '<a href="https://gitpod.io/" target="_blank" rel="noopener noreferrer">'.html_safe
- link_end = "#{sprite_icon('external-link', size: 12, css_class: 'ml-1 vertical-align-center')}</a>".html_safe
-
- s_('Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab.').html_safe % { link_start: link_start, link_end: link_end }
+ s_('Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab.')
end
end
diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb
index 06a52457fd6..29ead76a607 100644
--- a/app/helpers/groups_helper.rb
+++ b/app/helpers/groups_helper.rb
@@ -94,12 +94,19 @@ module GroupsHelper
else
full_title << breadcrumb_list_item(group_title_link(parent, hidable: false))
end
+
+ push_to_schema_breadcrumb(simple_sanitize(parent.name), group_path(parent))
end
full_title << render("layouts/nav/breadcrumbs/collapsed_dropdown", location: :before, title: _("Show parent subgroups"))
full_title << breadcrumb_list_item(group_title_link(group))
- full_title << ' &middot; '.html_safe + link_to(simple_sanitize(name), url, class: 'group-path breadcrumb-item-text js-breadcrumb-item-text') if name
+ push_to_schema_breadcrumb(simple_sanitize(group.name), group_path(group))
+
+ if name
+ full_title << ' &middot; '.html_safe + link_to(simple_sanitize(name), url, class: 'group-path breadcrumb-item-text js-breadcrumb-item-text')
+ push_to_schema_breadcrumb(simple_sanitize(name), url)
+ end
full_title.join.html_safe
end
@@ -163,6 +170,10 @@ module GroupsHelper
group_container_registry_nav?
end
+ def group_dependency_proxy_nav?
+ @group.dependency_proxy_feature_available?
+ end
+
def group_packages_list_nav?
@group.packages_feature_enabled?
end
@@ -174,6 +185,10 @@ module GroupsHelper
!multiple_members?(group)
end
+ def show_thanks_for_purchase_banner?
+ params.key?(:purchased_quantity) && params[:purchased_quantity].to_i > 0
+ end
+
private
def just_created?
diff --git a/app/helpers/icons_helper.rb b/app/helpers/icons_helper.rb
index 1d0001fde72..dc6164ee898 100644
--- a/app/helpers/icons_helper.rb
+++ b/app/helpers/icons_helper.rb
@@ -24,9 +24,11 @@ module IconsHelper
end
def custom_icon(icon_name, size: DEFAULT_ICON_SIZE)
- # We can't simply do the below, because there are some .erb SVGs.
- # File.read(Rails.root.join("app/views/shared/icons/_#{icon_name}.svg")).html_safe
- render "shared/icons/#{icon_name}.svg", size: size
+ memoized_icon("#{icon_name}_#{size}") do
+ # We can't simply do the below, because there are some .erb SVGs.
+ # File.read(Rails.root.join("app/views/shared/icons/_#{icon_name}.svg")).html_safe
+ render "shared/icons/#{icon_name}.svg", size: size
+ end
end
def sprite_icon_path
@@ -46,21 +48,23 @@ module IconsHelper
end
def sprite_icon(icon_name, size: DEFAULT_ICON_SIZE, css_class: nil)
- if known_sprites&.exclude?(icon_name)
- exception = ArgumentError.new("#{icon_name} is not a known icon in @gitlab-org/gitlab-svg")
- Gitlab::ErrorTracking.track_and_raise_for_dev_exception(exception)
- end
+ memoized_icon("#{icon_name}_#{size}_#{css_class}") do
+ if known_sprites&.exclude?(icon_name)
+ exception = ArgumentError.new("#{icon_name} is not a known icon in @gitlab-org/gitlab-svg")
+ Gitlab::ErrorTracking.track_and_raise_for_dev_exception(exception)
+ end
- css_classes = []
- css_classes << "s#{size}" if size
- css_classes << "#{css_class}" unless css_class.blank?
+ css_classes = []
+ css_classes << "s#{size}" if size
+ css_classes << "#{css_class}" unless css_class.blank?
- content_tag(
- :svg,
- content_tag(:use, '', { 'xlink:href' => "#{sprite_icon_path}##{icon_name}" } ),
- class: css_classes.empty? ? nil : css_classes.join(' '),
- data: { testid: "#{icon_name}-icon" }
- )
+ content_tag(
+ :svg,
+ content_tag(:use, '', { 'xlink:href' => "#{sprite_icon_path}##{icon_name}" } ),
+ class: css_classes.empty? ? nil : css_classes.join(' '),
+ data: { testid: "#{icon_name}-icon" }
+ )
+ end
end
def loading_icon(container: false, color: 'orange', size: 'sm', css_class: nil)
@@ -76,26 +80,17 @@ module IconsHelper
content_tag(:span, "", class: "gl-snippet-icon gl-snippet-icon-#{name}")
end
- def audit_icon(names, options = {})
- case names
+ def audit_icon(name, css_class: nil)
+ case name
when "standard"
- names = "key"
+ name = "key"
when "two-factor"
- names = "key"
+ name = "key"
when "google_oauth2"
- names = "google"
+ name = "google"
end
- options.include?(:base) ? fa_stacked_icon(names, options) : fa_icon(names, options)
- end
-
- def spinner(text = nil, visible = false)
- css_class = ['loading']
- css_class << 'hide' unless visible
-
- content_tag :div, class: css_class.join(' ') do
- icon('spinner spin') + text
- end
+ sprite_icon(name, css_class: css_class)
end
def boolean_to_icon(value)
@@ -178,4 +173,12 @@ module IconsHelper
@known_sprites ||= Gitlab::Json.parse(File.read(Rails.root.join('node_modules/@gitlab/svgs/dist/icons.json')))['icons']
end
+
+ def memoized_icon(key)
+ @rendered_icons ||= {}
+
+ @rendered_icons[key] || (
+ @rendered_icons[key] = yield
+ )
+ end
end
diff --git a/app/helpers/invite_members_helper.rb b/app/helpers/invite_members_helper.rb
index ac6ac9979b3..cea28fd4611 100644
--- a/app/helpers/invite_members_helper.rb
+++ b/app/helpers/invite_members_helper.rb
@@ -18,4 +18,8 @@ module InviteMembersHelper
experiment_enabled?(:invite_members_version_b) && !can_import_members?
end
end
+
+ def invite_group_members?(group)
+ experiment_enabled?(:invite_members_empty_group_version_a) && Ability.allowed?(current_user, :admin_group_member, group)
+ end
end
diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb
index f8e7711959a..77ced17bc22 100644
--- a/app/helpers/issuables_helper.rb
+++ b/app/helpers/issuables_helper.rb
@@ -76,7 +76,6 @@ module IssuablesHelper
when Issue
IssueSerializer
when MergeRequest
- opts[:experiment_enabled] = :suggest_pipeline if experiment_enabled?(:suggest_pipeline) && opts[:serializer] == 'widget'
MergeRequestSerializer
end
@@ -211,7 +210,7 @@ module IssuablesHelper
output << content_tag(:span, (sprite_icon('first-contribution', css_class: 'gl-icon gl-vertical-align-middle') if issuable.first_contribution?), class: 'has-tooltip gl-ml-2', title: _('1st contribution!'))
- output << content_tag(:span, (issuable.task_status if issuable.tasks?), id: "task_status", class: "d-none d-sm-none d-md-inline-block gl-ml-3")
+ output << content_tag(:span, (issuable.task_status if issuable.tasks?), id: "task_status", class: "d-none d-md-inline-block gl-ml-3")
output << content_tag(:span, (issuable.task_status_short if issuable.tasks?), id: "task_status_short", class: "d-md-none")
output.join.html_safe
@@ -275,7 +274,6 @@ module IssuablesHelper
canUpdate: can?(current_user, :"update_#{issuable.to_ability_name}", issuable),
canDestroy: can?(current_user, :"destroy_#{issuable.to_ability_name}", issuable),
issuableRef: issuable.to_reference,
- issuableStatus: issuable.state,
markdownPreviewPath: preview_markdown_path(parent),
markdownDocsPath: help_page_path('user/markdown'),
lockVersion: issuable.lock_version,
@@ -379,7 +377,12 @@ module IssuablesHelper
end
def issuable_display_type(issuable)
- issuable.model_name.human.downcase
+ case issuable
+ when Issue
+ issuable.issue_type.downcase
+ when MergeRequest
+ issuable.model_name.human.downcase
+ end
end
def has_filter_bar_param?
@@ -489,6 +492,21 @@ module IssuablesHelper
}
end
+ def sidebar_labels_data(issuable_sidebar, project)
+ {
+ allow_label_create: issuable_sidebar.dig(:current_user, :can_admin_label).to_s,
+ allow_scoped_labels: issuable_sidebar[:scoped_labels_available].to_s,
+ can_edit: issuable_sidebar.dig(:current_user, :can_edit).to_s,
+ iid: issuable_sidebar[:iid],
+ issuable_type: issuable_sidebar[:type],
+ labels_fetch_path: issuable_sidebar[:project_labels_path],
+ labels_manage_path: project_labels_path(project),
+ project_issues_path: issuable_sidebar[:project_issuables_path],
+ project_path: project.full_path,
+ selected_labels: issuable_sidebar[:labels].to_json
+ }
+ end
+
def parent
@project || @group
end
diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb
index dbf284e70e4..dee009cd3ab 100644
--- a/app/helpers/issues_helper.rb
+++ b/app/helpers/issues_helper.rb
@@ -152,6 +152,29 @@ module IssuesHelper
sort: 'desc'
}
end
+
+ def issue_header_actions_data(project, issuable, current_user)
+ new_issuable_params = ({ issuable_template: 'incident', issue: { issue_type: 'incident' } } if issuable.incident?)
+
+ {
+ can_create_issue: show_new_issue_link?(project).to_s,
+ can_reopen_issue: can?(current_user, :reopen_issue, issuable).to_s,
+ can_report_spam: issuable.submittable_as_spam_by?(current_user).to_s,
+ can_update_issue: can?(current_user, :update_issue, issuable).to_s,
+ iid: issuable.iid,
+ is_issue_author: (issuable.author == current_user).to_s,
+ issue_type: issuable_display_type(issuable),
+ new_issue_path: new_project_issue_path(project, new_issuable_params),
+ project_path: project.full_path,
+ report_abuse_path: new_abuse_report_path(user_id: issuable.author.id, ref_url: issue_url(issuable)),
+ submit_as_spam_path: mark_as_spam_project_issue_path(project, issuable)
+ }
+ end
+
+ # Overridden in EE
+ def scoped_labels_available?(parent)
+ false
+ end
end
IssuesHelper.prepend_if_ee('EE::IssuesHelper')
diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb
index c02adfcf4c6..871d19c6a8c 100644
--- a/app/helpers/notes_helper.rb
+++ b/app/helpers/notes_helper.rb
@@ -159,6 +159,7 @@ module NotesHelper
members: autocomplete,
issues: autocomplete,
mergeRequests: autocomplete,
+ vulnerabilities: autocomplete,
epics: autocomplete,
milestones: autocomplete,
labels: autocomplete
diff --git a/app/helpers/notifications_helper.rb b/app/helpers/notifications_helper.rb
index 542a9ad2a70..61fcda6a504 100644
--- a/app/helpers/notifications_helper.rb
+++ b/app/helpers/notifications_helper.rb
@@ -67,6 +67,7 @@ module NotificationsHelper
when :custom
_('You will only receive notifications for the events you choose')
when :owner_disabled
+ # Any change must be reflected in board_sidebar_subscription.vue
_('Notifications have been disabled by the project or group owner')
end
end
diff --git a/app/helpers/operations_helper.rb b/app/helpers/operations_helper.rb
index 9965a705a01..8105fce10cf 100644
--- a/app/helpers/operations_helper.rb
+++ b/app/helpers/operations_helper.rb
@@ -29,7 +29,9 @@ module OperationsHelper
'url' => alerts_service.url,
'alerts_setup_url' => help_page_path('operations/incident_management/alert_integrations.md', anchor: 'generic-http-endpoint'),
'alerts_usage_url' => project_alert_management_index_path(@project),
- 'disabled' => disabled.to_s
+ 'disabled' => disabled.to_s,
+ 'project_path' => @project.full_path,
+ 'multi_integrations' => 'false'
}
end
diff --git a/app/helpers/page_layout_helper.rb b/app/helpers/page_layout_helper.rb
index 6808ffc3e27..e3d82e7a091 100644
--- a/app/helpers/page_layout_helper.rb
+++ b/app/helpers/page_layout_helper.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
module PageLayoutHelper
+ include Gitlab::Utils::StrongMemoize
+
def page_title(*titles)
@page_title ||= []
@@ -44,7 +46,7 @@ module PageLayoutHelper
if link
@page_canonical_link = link
else
- @page_canonical_link
+ @page_canonical_link ||= generic_canonical_url
end
end
@@ -57,7 +59,7 @@ module PageLayoutHelper
subject = @project || @user || @group
- image = subject.avatar_url if subject.present?
+ image = subject.avatar_url(only_path: false) if subject.present?
image || default
end
@@ -147,4 +149,49 @@ module PageLayoutHelper
css_class.join(' ')
end
+
+ def page_itemtype(itemtype = nil)
+ if itemtype
+ @page_itemtype = { itemscope: true, itemtype: itemtype }
+ else
+ @page_itemtype || {}
+ end
+ end
+
+ def user_status_properties(user)
+ default_properties = { current_emoji: '', current_message: '', can_set_user_availability: Feature.enabled?(:set_user_availability_status, user), 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
+ })
+ end
+
+ private
+
+ def generic_canonical_url
+ strong_memoize(:generic_canonical_url) do
+ next unless request.get? || request.head?
+ next unless generate_generic_canonical_url?
+
+ # Request#url builds the url without the trailing slash
+ request.url
+ end
+ end
+
+ def generate_generic_canonical_url?
+ # For the main domain it doesn't matter whether there is
+ # a trailing slash or not, they're not considered different
+ # pages
+ return false if request.path == '/'
+
+ # We only need to generate the canonical url when the request has a trailing
+ # slash. In the request object, only the `original_fullpath` and
+ # `original_url` keep the slash if it's present. Both `path` and
+ # `fullpath` would return the path without the slash.
+ # Therefore, we need to process `original_fullpath`
+ request.original_fullpath.sub(request.path, '')[0] == '/'
+ end
end
diff --git a/app/helpers/preferences_helper.rb b/app/helpers/preferences_helper.rb
index 9bf819febb0..5310aef5bad 100644
--- a/app/helpers/preferences_helper.rb
+++ b/app/helpers/preferences_helper.rb
@@ -82,8 +82,8 @@ module PreferencesHelper
def integration_views
[].tap do |views|
- views << 'gitpod' if Gitlab::Gitpod.feature_and_settings_enabled?
- views << 'sourcegraph' if Gitlab::Sourcegraph.feature_available? && Gitlab::CurrentSettings.sourcegraph_enabled
+ views << { name: 'gitpod', message: gitpod_enable_description, message_url: 'https://gitpod.io/', help_link: help_page_path('integration/gitpod.md') } if Gitlab::Gitpod.feature_and_settings_enabled?
+ views << { name: 'sourcegraph', message: sourcegraph_url_message, message_url: Gitlab::CurrentSettings.sourcegraph_url, help_link: help_page_path('user/profile/preferences.md', anchor: 'sourcegraph') } if Gitlab::Sourcegraph.feature_available? && Gitlab::CurrentSettings.sourcegraph_enabled
end
end
diff --git a/app/helpers/profiles_helper.rb b/app/helpers/profiles_helper.rb
index 5a42e581867..04a3b915493 100644
--- a/app/helpers/profiles_helper.rb
+++ b/app/helpers/profiles_helper.rb
@@ -29,4 +29,18 @@ module ProfilesHelper
def user_profile?
params[:controller] == 'users'
end
+
+ def availability_values
+ Types::AvailabilityEnum.enum
+ end
+
+ def user_status_set_to_busy?(status)
+ status&.availability == availability_values[:busy]
+ end
+
+ def show_status_emoji?(status)
+ return false unless status
+
+ status.message.present? || status.emoji != UserStatus::DEFAULT_EMOJI
+ end
end
diff --git a/app/helpers/projects/terraform_helper.rb b/app/helpers/projects/terraform_helper.rb
new file mode 100644
index 00000000000..b286bc4d7a5
--- /dev/null
+++ b/app/helpers/projects/terraform_helper.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+module Projects::TerraformHelper
+ def js_terraform_list_data(project)
+ {
+ empty_state_image: image_path('illustrations/empty-state/empty-serverless-lg.svg'),
+ project_path: project.full_path
+ }
+ end
+end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index ae46135e890..f25b229d198 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -84,18 +84,8 @@ module ProjectsHelper
end
def project_title(project)
- namespace_link =
- if project.group
- group_title(project.group, nil, nil)
- else
- owner = project.namespace.owner
- link_to(simple_sanitize(owner.name), user_path(owner))
- end
-
- project_link = link_to project_path(project) do
- icon = project_icon(project, alt: project.name, class: 'avatar-tile', width: 15, height: 15) if project.avatar_url && !Rails.env.test?
- [icon, content_tag("span", simple_sanitize(project.name), class: "breadcrumb-item-text js-breadcrumb-item-text")].join.html_safe
- end
+ namespace_link = build_namespace_breadcrumb_link(project)
+ project_link = build_project_breadcrumb_link(project)
namespace_link = breadcrumb_list_item(namespace_link) unless project.group
project_link = breadcrumb_list_item project_link
@@ -302,7 +292,7 @@ module ProjectsHelper
end
def settings_operations_available?
- can?(current_user, :read_environment, @project)
+ !@project.archived? && can?(current_user, :admin_operations, @project)
end
def error_tracking_setting_project_json
@@ -465,6 +455,7 @@ module ProjectsHelper
builds: :read_build,
clusters: :read_cluster,
serverless: :read_cluster,
+ terraform: :read_terraform_state,
error_tracking: :read_sentry_issue,
alert_management: :read_alert_management_alert,
incidents: :read_issue,
@@ -484,7 +475,8 @@ module ProjectsHelper
:read_issue,
:read_sentry_issue,
:read_cluster,
- :read_feature_flag
+ :read_feature_flag,
+ :read_terraform_state
].any? do |ability|
can?(current_user, ability, project)
end
@@ -762,6 +754,7 @@ module ProjectsHelper
metrics_dashboard
feature_flags
tracings
+ terraform
]
end
@@ -784,6 +777,30 @@ module ProjectsHelper
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)
+
+ push_to_schema_breadcrumb(project_name, project_path(project))
+
+ link_to project_path(project) do
+ icon = project_icon(project, alt: project_name, class: 'avatar-tile', width: 15, height: 15) if project.avatar_url && !Rails.env.test?
+ [icon, content_tag("span", project_name, class: "breadcrumb-item-text js-breadcrumb-item-text")].join.html_safe
+ end
+ end
+
+ def build_namespace_breadcrumb_link(project)
+ if project.group
+ group_title(project.group, nil, nil)
+ else
+ owner = project.namespace.owner
+ name = simple_sanitize(owner.name)
+ url = user_path(owner)
+
+ push_to_schema_breadcrumb(name, url)
+ link_to(name, url)
+ end
+ end
end
ProjectsHelper.prepend_if_ee('EE::ProjectsHelper')
diff --git a/app/helpers/recaptcha_experiment_helper.rb b/app/helpers/recaptcha_experiment_helper.rb
deleted file mode 100644
index f15e92c0e99..00000000000
--- a/app/helpers/recaptcha_experiment_helper.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-# frozen_string_literal: true
-
-module RecaptchaExperimentHelper
- def show_recaptcha_sign_up?
- !!Gitlab::Recaptcha.enabled?
- end
-end
-
-RecaptchaExperimentHelper.prepend_if_ee('EE::RecaptchaExperimentHelper')
diff --git a/app/helpers/recaptcha_helper.rb b/app/helpers/recaptcha_helper.rb
new file mode 100644
index 00000000000..4ebac1d5b7f
--- /dev/null
+++ b/app/helpers/recaptcha_helper.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+module RecaptchaHelper
+ def show_recaptcha_sign_up?
+ !!Gitlab::Recaptcha.enabled?
+ end
+end
diff --git a/app/helpers/releases_helper.rb b/app/helpers/releases_helper.rb
index 050b27840a0..d9851564585 100644
--- a/app/helpers/releases_helper.rb
+++ b/app/helpers/releases_helper.rb
@@ -51,18 +51,25 @@ module ReleasesHelper
)
end
+ def group_milestone_project_releases_available?(project)
+ false
+ end
+
private
def new_edit_pages_shared_data
{
project_id: @project.id,
+ group_id: @project.group&.id,
+ group_milestones_available: group_milestone_project_releases_available?(@project),
project_path: @project.full_path,
markdown_preview_path: preview_markdown_path(@project),
markdown_docs_path: help_page_path('user/markdown'),
- update_release_api_docs_path: help_page_path('api/releases/index.md', anchor: 'update-a-release'),
release_assets_docs_path: help_page(anchor: 'release-assets'),
manage_milestones_path: project_milestones_path(@project),
new_milestone_path: new_project_milestone_path(@project)
}
end
end
+
+ReleasesHelper.prepend_if_ee('EE::ReleasesHelper')
diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb
index 3467f6e9a44..de1e0e4e05e 100644
--- a/app/helpers/search_helper.rb
+++ b/app/helpers/search_helper.rb
@@ -1,7 +1,16 @@
# frozen_string_literal: true
module SearchHelper
- SEARCH_PERMITTED_PARAMS = [:search, :scope, :project_id, :group_id, :repository_ref, :snippets, :sort, :state, :confidential].freeze
+ SEARCH_GENERIC_PARAMS = [
+ :search,
+ :scope,
+ :project_id,
+ :group_id,
+ :repository_ref,
+ :snippets,
+ :sort,
+ :force_search_results
+ ].freeze
def search_autocomplete_opts(term)
return unless current_user
@@ -9,7 +18,8 @@ module SearchHelper
resources_results = [
recent_items_autocomplete(term),
groups_autocomplete(term),
- projects_autocomplete(term)
+ projects_autocomplete(term),
+ issue_autocomplete(term)
].flatten
search_pattern = Regexp.new(Regexp.escape(term), "i")
@@ -82,11 +92,27 @@ module SearchHelper
end
end
- def search_entries_empty_message(scope, term)
- (s_("SearchResults|We couldn't find any %{scope} matching %{term}") % {
+ def search_entries_empty_message(scope, term, group, project)
+ options = {
scope: search_entries_scope_label(scope, 0),
- term: "<code>#{h(term)}</code>"
- }).html_safe
+ term: "<code>#{h(term)}</code>".html_safe
+ }
+
+ # We check project first because we have 3 possible combinations here:
+ # - group && project
+ # - group
+ # - group: nil, project: nil
+ if project
+ html_escape(_("We couldn't find any %{scope} matching %{term} in project %{project}")) % options.merge(
+ project: link_to(project.full_name, project_path(project), target: '_blank', rel: 'noopener noreferrer').html_safe
+ )
+ elsif group
+ html_escape(_("We couldn't find any %{scope} matching %{term} in group %{group}")) % options.merge(
+ group: link_to(group.full_name, group_path(group), target: '_blank', rel: 'noopener noreferrer').html_safe
+ )
+ else
+ html_escape(_("We couldn't find any %{scope} matching %{term}")) % options
+ end
end
def repository_ref(project)
@@ -140,7 +166,7 @@ module SearchHelper
if @project && @project.repository.root_ref
ref = @ref || @project.repository.root_ref
- [
+ result = [
{ category: "In this project", label: _("Files"), url: project_tree_path(@project, ref) },
{ category: "In this project", label: _("Commits"), url: project_commits_path(@project, ref) },
{ category: "In this project", label: _("Network"), url: project_network_path(@project, ref) },
@@ -152,6 +178,12 @@ module SearchHelper
{ category: "In this project", label: _("Members"), url: project_project_members_path(@project) },
{ category: "In this project", label: _("Wiki"), url: project_wikis_path(@project) }
]
+
+ if can?(current_user, :read_feature_flag, @project)
+ result << { category: "In this project", label: _("Feature Flags"), url: project_feature_flags_path(@project) }
+ end
+
+ result
else
[]
end
@@ -172,6 +204,24 @@ module SearchHelper
end
# rubocop: enable CodeReuse/ActiveRecord
+ def issue_autocomplete(term)
+ return [] unless @project.present? && current_user && term =~ /\A#{Issue.reference_prefix}\d+\z/
+
+ iid = term.sub(Issue.reference_prefix, '').to_i
+ issue = @project.issues.find_by_iid(iid)
+ return [] unless issue && Ability.allowed?(current_user, :read_issue, issue)
+
+ [
+ {
+ category: 'In this project',
+ id: issue.id,
+ label: search_result_sanitize("#{issue.title} (#{issue.to_reference})"),
+ url: issue_path(issue),
+ avatar_url: issue.project.avatar_url || ''
+ }
+ ]
+ end
+
# Autocomplete results for the current user's projects
# rubocop: disable CodeReuse/ActiveRecord
def projects_autocomplete(term, limit = 5)
@@ -225,7 +275,7 @@ module SearchHelper
search_params = params
.merge(search)
.merge({ scope: scope })
- .permit(SEARCH_PERMITTED_PARAMS)
+ .permit(SEARCH_GENERIC_PARAMS)
if @scope == scope
li_class = 'active'
@@ -317,10 +367,10 @@ module SearchHelper
end
# _search_highlight is used in EE override
- def highlight_and_truncate_issue(issue, search_term, _search_highlight)
- return unless issue.description.present?
+ def highlight_and_truncate_issuable(issuable, search_term, _search_highlight)
+ return unless issuable.description.present?
- simple_search_highlight_and_truncate(issue.description, search_term, highlighter: '<span class="gl-text-black-normal gl-font-weight-bold">\1</span>')
+ simple_search_highlight_and_truncate(issuable.description, search_term, highlighter: '<span class="gl-text-black-normal gl-font-weight-bold">\1</span>')
end
def show_user_search_tab?
@@ -330,6 +380,36 @@ module SearchHelper
can?(current_user, :read_users_list)
end
end
+
+ def issuable_state_to_badge_class(issuable)
+ # Closed is considered "danger" for MR so we need to handle separately
+ if issuable.is_a?(::MergeRequest)
+ if issuable.merged?
+ :primary
+ elsif issuable.closed?
+ :danger
+ else
+ :success
+ end
+ else
+ if issuable.closed?
+ :info
+ else
+ :success
+ end
+ end
+ end
+
+ def issuable_state_text(issuable)
+ case issuable.state
+ when 'merged'
+ _("Merged")
+ when 'closed'
+ _("Closed")
+ else
+ _("Open")
+ end
+ end
end
SearchHelper.prepend_if_ee('EE::SearchHelper')
diff --git a/app/helpers/services_helper.rb b/app/helpers/services_helper.rb
index 114bbf59ae1..96eb14be4b4 100644
--- a/app/helpers/services_helper.rb
+++ b/app/helpers/services_helper.rb
@@ -35,13 +35,6 @@ module ServicesHelper
"#{event}_events"
end
- def service_save_button(disabled: false)
- button_tag(class: 'btn btn-success', type: 'submit', disabled: disabled, data: { qa_selector: 'save_changes_button' }) do
- icon('spinner spin', class: 'hidden js-btn-spinner') +
- content_tag(:span, 'Save changes', class: 'js-btn-label')
- end
- end
-
def scoped_integrations_path
if @project.present?
project_settings_integrations_path(@project)
@@ -100,7 +93,8 @@ module ServicesHelper
editable: integration.editable?.to_s,
cancel_path: scoped_integrations_path,
can_test: integration.can_test?.to_s,
- test_path: scoped_test_integration_path(integration)
+ test_path: scoped_test_integration_path(integration),
+ reset_path: ''
}
end
@@ -121,7 +115,7 @@ module ServicesHelper
end
def group_level_integrations?
- @group.present? && Feature.enabled?(:group_level_integrations, @group)
+ @group.present? && Feature.enabled?(:group_level_integrations, @group, default_enabled: true)
end
def instance_level_integrations?
diff --git a/app/helpers/sorting_helper.rb b/app/helpers/sorting_helper.rb
index de6990041a6..10174e5d719 100644
--- a/app/helpers/sorting_helper.rb
+++ b/app/helpers/sorting_helper.rb
@@ -28,7 +28,8 @@ module SortingHelper
sort_value_contacted_date => sort_title_contacted_date,
sort_value_relative_position => sort_title_relative_position,
sort_value_size => sort_title_size,
- sort_value_expire_date => sort_title_expire_date
+ sort_value_expire_date => sort_title_expire_date,
+ sort_value_relevant => sort_title_relevant
}
end
@@ -81,6 +82,13 @@ module SortingHelper
}
end
+ def search_reverse_sort_options_hash
+ {
+ sort_value_recently_created => sort_value_oldest_created,
+ sort_value_oldest_created => sort_value_recently_created
+ }
+ end
+
def groups_sort_options_hash
{
sort_value_name => sort_title_name,
@@ -218,6 +226,10 @@ module SortingHelper
sort_options_hash[sort_value]
end
+ def search_sort_option_title(sort_value)
+ sort_options_hash[sort_value]
+ end
+
def sort_direction_icon(sort_value)
case sort_value
when sort_value_milestone, sort_value_due_date, /_asc\z/
@@ -256,6 +268,13 @@ module SortingHelper
sort_direction_button(url, reverse_sort, sort_value)
end
+ def search_sort_direction_button(sort_value)
+ reverse_sort = search_reverse_sort_options_hash[sort_value]
+ url = page_filter_path(sort: reverse_sort)
+
+ sort_direction_button(url, reverse_sort, sort_value)
+ end
+
# Titles.
def sort_title_access_level_asc
s_('SortOptions|Access level, ascending')
@@ -421,6 +440,10 @@ module SortingHelper
s_('SortOptions|Expired date')
end
+ def sort_title_relevant
+ s_('SortOptions|Relevant')
+ end
+
# Values.
def sort_value_access_level_asc
'access_level_asc'
@@ -582,6 +605,10 @@ module SortingHelper
'expired_asc'
end
+ def sort_value_relevant
+ 'relevant'
+ end
+
def packages_sort_options_hash
{
sort_value_recently_created => sort_title_created_date,
diff --git a/app/helpers/sourcegraph_helper.rb b/app/helpers/sourcegraph_helper.rb
index cc5a5c77e9a..25d7b209b45 100644
--- a/app/helpers/sourcegraph_helper.rb
+++ b/app/helpers/sourcegraph_helper.rb
@@ -2,26 +2,22 @@
module SourcegraphHelper
def sourcegraph_url_message
- link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: Gitlab::CurrentSettings.sourcegraph_url }
- link_end = "#{sprite_icon('external-link', size: 12, css_class: 'ml-1 vertical-align-center')}</a>".html_safe
-
message =
if Gitlab::CurrentSettings.sourcegraph_url_is_com?
- s_('SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}.').html_safe
+ s_('SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}.').html_safe
else
- s_('SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}.').html_safe
+ s_('SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}.').html_safe
end
- message % { link_start: link_start, link_end: link_end }
- end
+ experimental_message =
+ if Gitlab::Sourcegraph.feature_conditional?
+ s_("SourcegraphPreferences|This feature is experimental and currently limited to certain projects.")
+ elsif Gitlab::CurrentSettings.sourcegraph_public_only
+ s_("SourcegraphPreferences|This feature is experimental and limited to public projects.")
+ else
+ s_("SourcegraphPreferences|This feature is experimental.")
+ end
- def sourcegraph_experimental_message
- if Gitlab::Sourcegraph.feature_conditional?
- s_("SourcegraphPreferences|This feature is experimental and currently limited to certain projects.")
- elsif Gitlab::CurrentSettings.sourcegraph_public_only
- s_("SourcegraphPreferences|This feature is experimental and limited to public projects.")
- else
- s_("SourcegraphPreferences|This feature is experimental.")
- end
+ "#{message} #{experimental_message}"
end
end
diff --git a/app/helpers/stat_anchors_helper.rb b/app/helpers/stat_anchors_helper.rb
new file mode 100644
index 00000000000..76e58b45912
--- /dev/null
+++ b/app/helpers/stat_anchors_helper.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module StatAnchorsHelper
+ def stat_anchor_attrs(anchor)
+ {}.tap do |attrs|
+ attrs[:class] = %w(nav-link gl-display-flex gl-align-items-center) << extra_classes(anchor)
+ attrs[:itemprop] = anchor.itemprop if anchor.itemprop
+ end
+ end
+
+ private
+
+ def button_attribute(anchor)
+ "btn-#{anchor.class_modifier || 'missing'}"
+ end
+
+ def extra_classes(anchor)
+ if anchor.is_link
+ 'stat-link'
+ else
+ "btn #{button_attribute(anchor)}"
+ end
+ end
+end
diff --git a/app/helpers/suggest_pipeline_helper.rb b/app/helpers/suggest_pipeline_helper.rb
index d64e8d6f2cd..3151b792344 100644
--- a/app/helpers/suggest_pipeline_helper.rb
+++ b/app/helpers/suggest_pipeline_helper.rb
@@ -2,7 +2,7 @@
module SuggestPipelineHelper
def should_suggest_gitlab_ci_yml?
- experiment_enabled?(:suggest_pipeline) &&
+ Feature.enabled?(:suggest_pipeline, default_enabled: true) &&
current_user &&
params[:suggest_gitlab_ci_yml] == 'true'
end
diff --git a/app/helpers/time_helper.rb b/app/helpers/time_helper.rb
index 719c351242c..ecedbfb2a4f 100644
--- a/app/helpers/time_helper.rb
+++ b/app/helpers/time_helper.rb
@@ -32,4 +32,8 @@ module TimeHelper
"%02d:%02d:%02d" % [hours, minutes, seconds]
end
end
+
+ def time_in_milliseconds
+ (Time.now.to_f * 1000).to_i
+ end
end
diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb
index 563450159b5..692971f4627 100644
--- a/app/helpers/tree_helper.rb
+++ b/app/helpers/tree_helper.rb
@@ -75,11 +75,27 @@ module TreeHelper
if user_access(project).can_push_to_branch?(ref)
ref
else
- project = tree_edit_project(project)
- project.repository.next_branch('patch')
+ patch_branch_name(ref)
end
end
+ # Generate a patch branch name that should look like:
+ # `username-branchname-patch-epoch`
+ # where `epoch` is the last 5 digits of the time since epoch (in
+ # milliseconds)
+ #
+ # Note: this correlates with how the WebIDE formats the branch name
+ # and if this implementation changes, so should the `placeholderBranchName`
+ # definition in app/assets/javascripts/ide/stores/modules/commit/getters.js
+ def patch_branch_name(ref)
+ return unless current_user
+
+ username = current_user.username
+ epoch = time_in_milliseconds.to_s.last(5)
+
+ "#{username}-#{ref}-patch-#{epoch}"
+ end
+
def tree_edit_project(project = @project)
if can?(current_user, :push_code, project)
project
diff --git a/app/helpers/user_callouts_helper.rb b/app/helpers/user_callouts_helper.rb
index 0cdf53d6174..e93c1b82cd7 100644
--- a/app/helpers/user_callouts_helper.rb
+++ b/app/helpers/user_callouts_helper.rb
@@ -10,6 +10,7 @@ module UserCalloutsHelper
WEBHOOKS_MOVED = 'webhooks_moved'
CUSTOMIZE_HOMEPAGE = 'customize_homepage'
FEATURE_FLAGS_NEW_VERSION = 'feature_flags_new_version'
+ REGISTRATION_ENABLED_CALLOUT = 'registration_enabled_callout'
def show_admin_integrations_moved?
!user_dismissed?(ADMIN_INTEGRATIONS_MOVED)
@@ -55,6 +56,10 @@ module UserCalloutsHelper
!user_dismissed?(FEATURE_FLAGS_NEW_VERSION)
end
+ def show_registration_enabled_user_callout?
+ current_user&.admin? && signup_enabled? && !user_dismissed?(REGISTRATION_ENABLED_CALLOUT)
+ end
+
private
def user_dismissed?(feature_name, ignore_dismissal_earlier_than = nil)
diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb
index f47937e6d57..7d4ab192f2f 100644
--- a/app/helpers/users_helper.rb
+++ b/app/helpers/users_helper.rb
@@ -91,18 +91,18 @@ module UsersHelper
end
end
- def work_information(user)
+ def work_information(user, with_schema_markup: false)
return unless user
organization = user.organization
job_title = user.job_title
if organization.present? && job_title.present?
- s_('Profile|%{job_title} at %{organization}') % { job_title: job_title, organization: organization }
+ render_job_title_and_organization(job_title, organization, with_schema_markup: with_schema_markup)
elsif job_title.present?
- job_title
+ render_job_title(job_title, with_schema_markup: with_schema_markup)
elsif organization.present?
- organization
+ render_organization(organization, with_schema_markup: with_schema_markup)
end
end
@@ -110,6 +110,32 @@ 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_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
+
private
def blocked_user_badge(user)
@@ -151,6 +177,35 @@ module UsersHelper
items
end
+
+ def render_job_title(job_title, with_schema_markup: false)
+ if with_schema_markup
+ content_tag :span, itemprop: 'jobTitle' do
+ job_title
+ end
+ else
+ job_title
+ end
+ end
+
+ def render_organization(organization, with_schema_markup: false)
+ if with_schema_markup
+ content_tag :span, itemprop: 'worksFor' do
+ organization
+ end
+ else
+ organization
+ end
+ end
+
+ def render_job_title_and_organization(job_title, organization, with_schema_markup: false)
+ if with_schema_markup
+ job_title = '<span itemprop="jobTitle">'.html_safe + job_title + "</span>".html_safe
+ organization = '<span itemprop="worksFor">'.html_safe + organization + "</span>".html_safe
+ end
+
+ html_escape(s_('Profile|%{job_title} at %{organization}')) % { job_title: job_title, organization: organization }
+ end
end
UsersHelper.prepend_if_ee('EE::UsersHelper')
diff --git a/app/helpers/visibility_level_helper.rb b/app/helpers/visibility_level_helper.rb
index a7b9e17c898..896dcdd2caf 100644
--- a/app/helpers/visibility_level_helper.rb
+++ b/app/helpers/visibility_level_helper.rb
@@ -31,7 +31,7 @@ module VisibilityLevelHelper
when Gitlab::VisibilityLevel::PRIVATE
_("Project access must be granted explicitly to each user. If this project is part of a group, access will be granted to members of the group.")
when Gitlab::VisibilityLevel::INTERNAL
- _("The project can be accessed by any logged in user.")
+ _("The project can be accessed by any logged in user except external users.")
when Gitlab::VisibilityLevel::PUBLIC
_("The project can be accessed without any authentication.")
end
@@ -42,7 +42,7 @@ module VisibilityLevelHelper
when Gitlab::VisibilityLevel::PRIVATE
_("The group and its projects can only be viewed by members.")
when Gitlab::VisibilityLevel::INTERNAL
- _("The group and any internal projects can be viewed by any logged in user.")
+ _("The group and any internal projects can be viewed by any logged in user except external users.")
when Gitlab::VisibilityLevel::PUBLIC
_("The group and any public projects can be viewed without any authentication.")
end
diff --git a/app/helpers/whats_new_helper.rb b/app/helpers/whats_new_helper.rb
index c183ed7f12a..283d443f51b 100644
--- a/app/helpers/whats_new_helper.rb
+++ b/app/helpers/whats_new_helper.rb
@@ -5,7 +5,7 @@ module WhatsNewHelper
def whats_new_most_recent_release_items_count
Gitlab::ProcessMemoryCache.cache_backend.fetch('whats_new:release_items_count', expires_in: CACHE_DURATION) do
- whats_new_most_recent_release_items&.count
+ whats_new_release_items&.count
end
end
@@ -19,9 +19,7 @@ module WhatsNewHelper
def whats_new_most_recent_version
Gitlab::ProcessMemoryCache.cache_backend.fetch('whats_new:release_version', expires_in: CACHE_DURATION) do
- if whats_new_most_recent_release_items
- whats_new_most_recent_release_items.first.try(:[], 'release')
- end
+ whats_new_release_items&.first&.[]('release')
end
end
end