diff options
Diffstat (limited to 'app/helpers')
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 << ' · '.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 << ' · '.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 |