From 48aff82709769b098321c738f3444b9bdaa694c6 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Wed, 21 Oct 2020 07:08:36 +0000 Subject: Add latest changes from gitlab-org/gitlab@13-5-stable-ee --- app/helpers/analytics/navbar_helper.rb | 2 +- app/helpers/analytics/unique_visits_helper.rb | 3 +- app/helpers/application_helper.rb | 24 ++++++- app/helpers/application_settings_helper.rb | 14 +++- app/helpers/avatars_helper.rb | 2 +- app/helpers/blob_helper.rb | 26 ++++---- app/helpers/boards_helper.rb | 22 ++++++- app/helpers/ci/runners_helper.rb | 8 +++ app/helpers/clusters_helper.rb | 20 +++--- .../container_expiration_policies_helper.rb | 5 ++ app/helpers/dropdowns_helper.rb | 2 +- app/helpers/emails_helper.rb | 20 ++++++ app/helpers/events_helper.rb | 16 +---- app/helpers/external_link_helper.rb | 2 +- app/helpers/feature_flags_helper.rb | 19 ++++++ app/helpers/form_helper.rb | 26 +++++++- app/helpers/gitlab_routing_helper.rb | 12 ++++ app/helpers/gitpod_helper.rb | 10 +++ app/helpers/groups/group_members_helper.rb | 42 ++++++++++-- app/helpers/icons_helper.rb | 20 +++--- app/helpers/invite_members_helper.rb | 21 ++++++ app/helpers/issuables_helper.rb | 58 ++++++++++------- app/helpers/issues_helper.rb | 15 +++++ app/helpers/labels_helper.rb | 31 +++------ app/helpers/merge_requests_helper.rb | 4 -- app/helpers/mirror_helper.rb | 6 +- app/helpers/namespaces_helper.rb | 2 +- app/helpers/nav_helper.rb | 3 +- app/helpers/operations_helper.rb | 2 +- app/helpers/packages_helper.rb | 22 +++---- app/helpers/page_layout_helper.rb | 8 +++ app/helpers/pagination_helper.rb | 10 +-- app/helpers/preferences_helper.rb | 4 +- app/helpers/projects/alert_management_helper.rb | 4 +- app/helpers/projects/incidents_helper.rb | 7 +- app/helpers/projects_helper.rb | 32 +++++---- app/helpers/releases_helper.rb | 9 +++ app/helpers/reminder_emails_helper.rb | 76 ++++++++++++++++++++++ app/helpers/search_helper.rb | 51 +++++++++++---- app/helpers/services_helper.rb | 6 +- app/helpers/snippets_helper.rb | 25 ------- app/helpers/ssh_keys_helper.rb | 18 +++++ app/helpers/startupjs_helper.rb | 17 +++++ app/helpers/suggest_pipeline_helper.rb | 2 +- app/helpers/system_note_helper.rb | 6 +- app/helpers/tags_helper.rb | 9 +++ app/helpers/timeboxes_helper.rb | 4 +- app/helpers/todos_helper.rb | 9 +++ app/helpers/tree_helper.rb | 45 +++++-------- app/helpers/user_callouts_helper.rb | 6 +- app/helpers/users_helper.rb | 13 +++- app/helpers/visibility_level_helper.rb | 17 ----- app/helpers/web_ide_button_helper.rb | 61 +++++++++++++++++ app/helpers/webpack_helper.rb | 14 ++-- app/helpers/whats_new_helper.rb | 27 ++++---- app/helpers/wiki_helper.rb | 3 +- 56 files changed, 681 insertions(+), 261 deletions(-) create mode 100644 app/helpers/feature_flags_helper.rb create mode 100644 app/helpers/gitpod_helper.rb create mode 100644 app/helpers/invite_members_helper.rb create mode 100644 app/helpers/reminder_emails_helper.rb create mode 100644 app/helpers/ssh_keys_helper.rb create mode 100644 app/helpers/startupjs_helper.rb create mode 100644 app/helpers/web_ide_button_helper.rb (limited to 'app/helpers') diff --git a/app/helpers/analytics/navbar_helper.rb b/app/helpers/analytics/navbar_helper.rb index ddf2655c887..bc0b5e7c74f 100644 --- a/app/helpers/analytics/navbar_helper.rb +++ b/app/helpers/analytics/navbar_helper.rb @@ -28,7 +28,7 @@ module Analytics private def navbar_sub_item(args) - NavbarSubItem.new(args) + NavbarSubItem.new(**args) end def cycle_analytics_navbar_link(project, current_user) diff --git a/app/helpers/analytics/unique_visits_helper.rb b/app/helpers/analytics/unique_visits_helper.rb index ded7f54e44e..4c709b2ed23 100644 --- a/app/helpers/analytics/unique_visits_helper.rb +++ b/app/helpers/analytics/unique_visits_helper.rb @@ -14,8 +14,7 @@ module Analytics end def track_visit(target_id) - return unless Feature.enabled?(:track_unique_visits) - return unless Gitlab::CurrentSettings.usage_ping_enabled? + return unless Feature.enabled?(:track_unique_visits, default_enabled: true) return unless visitor_id Gitlab::Analytics::UniqueVisits.new.track_visit(visitor_id, target_id) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index a81225c8954..2a6b00c0bd8 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -6,10 +6,18 @@ require 'uri' module ApplicationHelper include StartupCssHelper - # See https://docs.gitlab.com/ee/development/ee_features.html#code-in-app-views + # See https://docs.gitlab.com/ee/development/ee_features.html#code-in-appviews # rubocop: disable CodeReuse/ActiveRecord - def render_if_exists(partial, locals = {}) - render(partial, locals) if partial_exists?(partial) + # We allow partial to be nil so that collection views can be passed in + # `render partial: 'some/view', collection: @some_collection` + def render_if_exists(partial = nil, **options) + return unless partial_exists?(partial || options[:partial]) + + if partial.nil? + render(**options) + else + render(partial, options) + end end def partial_exists?(partial) @@ -204,6 +212,10 @@ module ApplicationHelper Gitlab::CurrentSettings.current_application_settings.help_page_support_url.presence || promo_url + '/getting-help/' end + def instance_review_permitted? + ::Gitlab::CurrentSettings.instance_review_permitted? && current_user&.admin? + end + def static_objects_external_storage_enabled? Gitlab::CurrentSettings.static_objects_external_storage_enabled? end @@ -349,6 +361,12 @@ module ApplicationHelper } end + def add_page_specific_style(path) + content_for :page_specific_styles do + stylesheet_link_tag_defer path + end + end + def page_startup_api_calls @api_startup_calls end diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb index 9245cc1cb1c..9c408efe8cd 100644 --- a/app/helpers/application_settings_helper.rb +++ b/app/helpers/application_settings_helper.rb @@ -168,7 +168,7 @@ module ApplicationSettingsHelper def visible_attributes [ - :admin_notification_email, + :abuse_notification_email, :after_sign_out_path, :after_sign_up_text, :akismet_api_key, @@ -230,6 +230,7 @@ module ApplicationSettingsHelper :hashed_storage_enabled, :help_page_hide_commercial_content, :help_page_support_url, + :help_page_documentation_base_url, :help_page_text, :hide_third_party_offers, :home_page_url, @@ -265,6 +266,7 @@ module ApplicationSettingsHelper :receive_max_input_size, :repository_checks_enabled, :repository_storages_weighted, + :require_admin_approval_after_user_signup, :require_two_factor_authentication, :restricted_visibility_levels, :rsa_key_restriction, @@ -345,6 +347,12 @@ module ApplicationSettingsHelper ] end + def deprecated_attributes + [ + :admin_notification_email # ok to remove in REST API v5 + ] + end + def expanded_by_default? Rails.env.test? end @@ -382,6 +390,10 @@ module ApplicationSettingsHelper Gitlab::CurrentSettings.self_monitoring_project&.full_path } end + + def show_documentation_base_url_field? + Feature.enabled?(:help_page_documentation_redirect) + end end ApplicationSettingsHelper.prepend_if_ee('EE::ApplicationSettingsHelper') diff --git a/app/helpers/avatars_helper.rb b/app/helpers/avatars_helper.rb index 68dbc5b65d1..5457f96d506 100644 --- a/app/helpers/avatars_helper.rb +++ b/app/helpers/avatars_helper.rb @@ -60,7 +60,7 @@ module AvatarsHelper avatar_size = options[:size] || 16 user_name = options[:user].try(:name) || options[:user_name] - avatar_url = user_avatar_url_for(options.merge(size: avatar_size)) + avatar_url = user_avatar_url_for(**options.merge(size: avatar_size)) has_tooltip = options[:has_tooltip].nil? ? true : options[:has_tooltip] data_attributes = options[:data] || {} diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 2eff87ae0ec..806fea3ab44 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -1,12 +1,6 @@ # frozen_string_literal: true module BlobHelper - def highlight(file_name, file_content, language: nil, plain: false) - highlighted = Gitlab::Highlight.highlight(file_name, file_content, plain: plain, language: language) - - raw %(
#{highlighted}
) - end - def no_highlight_files %w(credits changelog news copying copyright license authors) end @@ -33,11 +27,19 @@ module BlobHelper end def ide_fork_and_edit_path(project = @project, ref = @ref, path = @path, options = {}) - if current_user - project_forks_path(project, - namespace_key: current_user&.namespace&.id, - continue: edit_blob_fork_params(ide_edit_path(project, ref, path))) - end + fork_path_for_current_user(project, ide_edit_path(project, ref, path)) + end + + def fork_and_edit_path(project = @project, ref = @ref, path = @path, options = {}) + fork_path_for_current_user(project, edit_blob_path(project, ref, path, options)) + end + + def fork_path_for_current_user(project, path) + return unless current_user + + project_forks_path(project, + namespace_key: current_user.namespace&.id, + continue: edit_blob_fork_params(path)) end def encode_ide_path(path) @@ -148,7 +150,7 @@ module BlobHelper # mode - File unix mode # mode - File name def blob_icon(mode, name) - icon("#{file_type_icon_class('file', mode, name)} fw") + sprite_icon(file_type_icon_class('file', mode, name)) end def blob_raw_url(**kwargs) diff --git a/app/helpers/boards_helper.rb b/app/helpers/boards_helper.rb index 6a4a7a8dfb2..c827fb4dd95 100644 --- a/app/helpers/boards_helper.rb +++ b/app/helpers/boards_helper.rb @@ -14,10 +14,14 @@ module BoardsHelper root_path: root_path, full_path: full_path, bulk_update_path: @bulk_issues_path, + can_update: (!!can?(current_user, :admin_issue, board)).to_s, time_tracking_limit_to_hours: Gitlab::CurrentSettings.time_tracking_limit_to_hours.to_s, recent_boards_endpoint: recent_boards_path, parent: current_board_parent.model_name.param_key, - group_id: @group&.id + group_id: @group&.id, + labels_filter_base_path: build_issue_link_base, + labels_fetch_path: labels_fetch_path, + labels_manage_path: labels_manage_path } end @@ -37,6 +41,22 @@ module BoardsHelper end end + def labels_fetch_path + if board.group_board? + group_labels_path(@group, format: :json, only_group_labels: true, include_ancestor_groups: true) + else + project_labels_path(@project, format: :json, include_ancestor_groups: true) + end + end + + def labels_manage_path + if board.group_board? + group_labels_path(@group) + else + project_labels_path(@project) + end + end + def board_base_url if board.group_board? group_boards_url(@group) diff --git a/app/helpers/ci/runners_helper.rb b/app/helpers/ci/runners_helper.rb index 8cdb28b2874..552acf61f47 100644 --- a/app/helpers/ci/runners_helper.rb +++ b/app/helpers/ci/runners_helper.rb @@ -39,6 +39,14 @@ module Ci runner.contacted_at end end + + def group_shared_runners_settings_data(group) + { + update_path: api_v4_groups_path(id: group.id), + shared_runners_availability: group.shared_runners_setting, + parent_shared_runners_availability: group.parent&.shared_runners_setting + } + end end end diff --git a/app/helpers/clusters_helper.rb b/app/helpers/clusters_helper.rb index caad215e996..cc633df77f9 100644 --- a/app/helpers/clusters_helper.rb +++ b/app/helpers/clusters_helper.rb @@ -12,6 +12,18 @@ module ClustersHelper end end + def display_cluster_agents?(_clusterable) + false + end + + def js_cluster_agents_list_data(clusterable_project) + { + default_branch_name: clusterable_project.default_branch, + empty_state_image: image_path('illustrations/clusters_empty.svg'), + project_path: clusterable_project.full_path + } + end + def js_clusters_list_data(path = nil) { ancestor_help_path: help_page_path('user/group/clusters/index', anchor: 'cluster-precedence'), @@ -42,14 +54,6 @@ module ClustersHelper } end - # This method is depreciated and will be removed when associated HAML files are moved to JavaScript - def provider_icon(provider = nil) - img_data = js_clusters_list_data.dig(:img_tags, provider&.to_sym) || - js_clusters_list_data.dig(:img_tags, :default) - - image_tag img_data[:path], alt: img_data[:text], class: 'gl-h-full' - end - def render_gcp_signup_offer return if Gitlab::CurrentSettings.current_application_settings.hide_third_party_offers? return unless show_gcp_signup_offer? diff --git a/app/helpers/container_expiration_policies_helper.rb b/app/helpers/container_expiration_policies_helper.rb index cc6d717ce35..52f68ac53f0 100644 --- a/app/helpers/container_expiration_policies_helper.rb +++ b/app/helpers/container_expiration_policies_helper.rb @@ -24,4 +24,9 @@ module ContainerExpirationPoliciesHelper end end end + + def container_expiration_policies_historic_entry_enabled?(project) + Gitlab::CurrentSettings.container_expiration_policies_enable_historic_entries || + Feature.enabled?(:container_expiration_policies_historic_entry, project) + end end diff --git a/app/helpers/dropdowns_helper.rb b/app/helpers/dropdowns_helper.rb index 84aa08281f6..e1378e485e4 100644 --- a/app/helpers/dropdowns_helper.rb +++ b/app/helpers/dropdowns_helper.rb @@ -103,7 +103,7 @@ module DropdownsHelper def dropdown_filter(placeholder, search_id: nil) content_tag :div, class: "dropdown-input" do filter_output = search_field_tag search_id, nil, class: "dropdown-input-field qa-dropdown-input-field", placeholder: placeholder, autocomplete: 'off' - filter_output << icon('search', class: "dropdown-input-search") + filter_output << sprite_icon('search', css_class: 'dropdown-input-search') filter_output << sprite_icon('close', size: 16, css_class: 'dropdown-input-clear js-dropdown-input-clear') filter_output.html_safe diff --git a/app/helpers/emails_helper.rb b/app/helpers/emails_helper.rb index d5c22927991..0a0dc77e5e2 100644 --- a/app/helpers/emails_helper.rb +++ b/app/helpers/emails_helper.rb @@ -218,8 +218,28 @@ module EmailsHelper _('Please contact your administrator with any questions.') end + def change_reviewer_notification_text(new_reviewers, previous_reviewers, html_tag = nil) + new = new_reviewers.any? ? users_to_sentence(new_reviewers) : s_('ChangeReviewer|Unassigned') + old = previous_reviewers.any? ? users_to_sentence(previous_reviewers) : nil + + if html_tag.present? + new = content_tag(html_tag, new) + old = content_tag(html_tag, old) if old.present? + end + + if old.present? + s_('ChangeReviewer|Reviewer changed from %{old} to %{new}').html_safe % { old: old, new: new } + else + s_('ChangeReviewer|Reviewer changed to %{new}').html_safe % { new: new } + end + end + private + def users_to_sentence(users) + sanitize_name(users.map(&:name).to_sentence) + end + def generate_link(text, url) link_to(text, url, target: :_blank, rel: 'noopener noreferrer') end diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb index 0167f2ef698..f40755b9439 100644 --- a/app/helpers/events_helper.rb +++ b/app/helpers/events_helper.rb @@ -28,19 +28,7 @@ module EventsHelper end def event_action_name(event) - target = if event.target_type - if event.design? || event.design_note? - 'design' - elsif event.wiki_page? - 'wiki page' - elsif event.note? - event.note_target_type - else - event.target_type.titleize.downcase - end - else - 'project' - end + target = event.note_target_type_name || event.target_type_name [event.action_name, target].join(" ") end @@ -229,7 +217,7 @@ module EventsHelper def event_note_title_html(event) if event.note_target capture do - concat content_tag(:span, event.note_target_type, class: "event-target-type gl-mr-2") + concat content_tag(:span, event.note_target_type_name, class: "event-target-type gl-mr-2") concat link_to(event.note_target_reference, event_note_target_url(event), title: event.target_title, class: 'has-tooltip event-target-link gl-mr-2') end else diff --git a/app/helpers/external_link_helper.rb b/app/helpers/external_link_helper.rb index 9dbad1f5032..bf47087543f 100644 --- a/app/helpers/external_link_helper.rb +++ b/app/helpers/external_link_helper.rb @@ -3,7 +3,7 @@ module ExternalLinkHelper def external_link(body, url, options = {}) link_to url, { target: '_blank', rel: 'noopener noreferrer' }.merge(options) do - "#{body} #{icon('external-link')}".html_safe + "#{body} #{sprite_icon('external-link')}".html_safe end end end diff --git a/app/helpers/feature_flags_helper.rb b/app/helpers/feature_flags_helper.rb new file mode 100644 index 00000000000..e50191a471f --- /dev/null +++ b/app/helpers/feature_flags_helper.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module FeatureFlagsHelper + include ::API::Helpers::RelatedResourcesHelpers + + def unleash_api_url(project) + expose_url(api_v4_feature_flags_unleash_path(project_id: project.id)) + end + + def unleash_api_instance_id(project) + project.feature_flags_client_token + end + + def feature_flag_issues_links_endpoint(_project, _feature_flag, _user) + '' + end +end + +FeatureFlagsHelper.prepend_if_ee('::EE::FeatureFlagsHelper') diff --git a/app/helpers/form_helper.rb b/app/helpers/form_helper.rb index 3dde5afcb92..8a8d708b0b2 100644 --- a/app/helpers/form_helper.rb +++ b/app/helpers/form_helper.rb @@ -56,7 +56,7 @@ module FormHelper end def reviewers_dropdown_options(issuable_type) - { + dropdown_data = { toggle_class: 'js-reviewer-search js-multiselect js-save-user-data', title: 'Request review from', filter: true, @@ -69,13 +69,20 @@ module FormHelper project_id: (@target_project || @project)&.id, field_name: "#{issuable_type}[reviewer_ids][]", default_label: 'Unassigned', - 'dropdown-header': 'Reviewer(s)', + 'max-select': 1, + 'dropdown-header': 'Reviewer', multi_select: true, 'input-meta': 'name', 'always-show-selectbox': true, current_user_info: UserSerializer.new.represent(current_user) } } + + if merge_request_supports_multiple_reviewers? + dropdown_data = multiple_reviewers_dropdown_options(dropdown_data) + end + + dropdown_data end # Overwritten @@ -88,6 +95,11 @@ module FormHelper false end + # Overwritten + def merge_request_supports_multiple_reviewers? + false + end + private def multiple_assignees_dropdown_options(options) @@ -99,6 +111,16 @@ module FormHelper new_options end + + def multiple_reviewers_dropdown_options(options) + new_options = options.dup + + new_options[:title] = _('Select reviewer(s)') + new_options[:data][:'dropdown-header'] = _('Reviewer(s)') + new_options[:data].delete(:'max-select') + + new_options + end end FormHelper.prepend_if_ee('::EE::FormHelper') diff --git a/app/helpers/gitlab_routing_helper.rb b/app/helpers/gitlab_routing_helper.rb index d71e6b4c004..7df6bef7914 100644 --- a/app/helpers/gitlab_routing_helper.rb +++ b/app/helpers/gitlab_routing_helper.rb @@ -343,6 +343,18 @@ 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 new file mode 100644 index 00000000000..7edf7dc218d --- /dev/null +++ b/app/helpers/gitpod_helper.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module GitpodHelper + def gitpod_enable_description + link_start = ''.html_safe + link_end = "#{sprite_icon('external-link', size: 12, css_class: 'ml-1 vertical-align-center')}".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 } + end +end diff --git a/app/helpers/groups/group_members_helper.rb b/app/helpers/groups/group_members_helper.rb index dcff2be34da..ee90585112b 100644 --- a/app/helpers/groups/group_members_helper.rb +++ b/app/helpers/groups/group_members_helper.rb @@ -10,17 +10,34 @@ module Groups::GroupMembersHelper end def render_invite_member_for_group(group, default_access_level) - render 'shared/members/invite_member', submit_url: group_group_members_path(group), access_levels: GroupMember.access_level_roles, default_access_level: default_access_level + render 'shared/members/invite_member', submit_url: group_group_members_path(group), access_levels: group.access_level_roles, default_access_level: default_access_level end def linked_groups_data_json(group_links) - GroupGroupLinkSerializer.new.represent(group_links).to_json + GroupGroupLinkSerializer.new.represent(group_links, { current_user: current_user }).to_json end def members_data_json(group, members) members_data(group, members).to_json end + # Overridden in `ee/app/helpers/ee/groups/group_members_helper.rb` + def group_members_list_data_attributes(group, members) + { + members: members_data_json(group, members), + member_path: group_group_member_path(group, ':id'), + group_id: group.id + } + end + + def linked_groups_list_data_attributes(group) + { + members: linked_groups_data_json(group.shared_with_group_links), + member_path: group_group_link_path(group, ':id'), + group_id: group.id + } + end + private def members_data(group, members) @@ -35,7 +52,6 @@ module Groups::GroupMembersHelper requested_at: member.requested_at, can_update: member.can_update?, can_remove: member.can_remove?, - can_override: member.can_override?, access_level: { string_value: member.human_access, integer_value: member.access_level @@ -44,13 +60,14 @@ module Groups::GroupMembersHelper id: source.id, name: source.full_name, web_url: Gitlab::UrlBuilder.build(source) - } + }, + valid_roles: member.valid_level_roles }.merge(member_created_by_data(member.created_by)) - if user.present? - data[:user] = member_user_data(user) - else + if member.invite? data[:invite] = member_invite_data(member) + elsif user.present? + data[:user] = member_user_data(user) end data @@ -77,6 +94,17 @@ module Groups::GroupMembersHelper avatar_url: avatar_icon_for_user(user, AVATAR_SIZE), blocked: user.blocked?, two_factor_enabled: user.two_factor_enabled? + }.merge(member_user_status_data(user.status)) + end + + def member_user_status_data(status) + return {} unless status.present? + + { + status: { + emoji: status.emoji, + message_html: status.message_html + } } end diff --git a/app/helpers/icons_helper.rb b/app/helpers/icons_helper.rb index 0352b0ddf28..1d0001fde72 100644 --- a/app/helpers/icons_helper.rb +++ b/app/helpers/icons_helper.rb @@ -124,7 +124,7 @@ module IconsHelper def file_type_icon_class(type, mode, name) if type == 'folder' - icon_class = 'folder' + icon_class = 'folder-o' elsif type == 'archive' icon_class = 'archive' elsif mode == '120000' @@ -135,36 +135,36 @@ module IconsHelper case File.extname(name).downcase when '.pdf' - icon_class = 'file-pdf-o' + icon_class = 'document' when '.jpg', '.jpeg', '.jif', '.jfif', '.jp2', '.jpx', '.j2k', '.j2c', '.apng', '.png', '.gif', '.tif', '.tiff', '.svg', '.ico', '.bmp', '.webp' - icon_class = 'file-image-o' + icon_class = 'doc-image' when '.zip', '.zipx', '.tar', '.gz', '.gzip', '.tgz', '.bz', '.bzip', '.bz2', '.bzip2', '.car', '.tbz', '.xz', 'txz', '.rar', '.7z', '.lz', '.lzma', '.tlz' - icon_class = 'file-archive-o' + icon_class = 'doc-compressed' when '.mp3', '.wma', '.ogg', '.oga', '.wav', '.flac', '.aac', '.3ga', '.ac3', '.midi', '.m4a', '.ape', '.mpa' - icon_class = 'file-audio-o' + icon_class = 'volume-up' when '.mp4', '.m4p', '.m4v', '.mpg', '.mp2', '.mpeg', '.mpe', '.mpv', '.mpg', '.mpeg', '.m2v', '.m2ts', '.avi', '.mkv', '.flv', '.ogv', '.mov', '.3gp', '.3g2' - icon_class = 'file-video-o' + icon_class = 'live-preview' when '.doc', '.dot', '.docx', '.docm', '.dotx', '.dotm', '.docb', '.odt', '.ott', '.uot', '.rtf' - icon_class = 'file-word-o' + icon_class = 'doc-text' when '.xls', '.xlt', '.xlm', '.xlsx', '.xlsm', '.xltx', '.xltm', '.xlsb', '.xla', '.xlam', '.xll', '.xlw', '.ots', '.ods', '.uos' - icon_class = 'file-excel-o' + icon_class = 'document' when '.ppt', '.pot', '.pps', '.pptx', '.pptm', '.potx', '.potm', '.ppam', '.ppsx', '.ppsm', '.sldx', '.sldm', '.odp', '.otp', '.uop' - icon_class = 'file-powerpoint-o' + icon_class = 'doc-chart' else - icon_class = 'file-text-o' + icon_class = 'doc-text' end end diff --git a/app/helpers/invite_members_helper.rb b/app/helpers/invite_members_helper.rb new file mode 100644 index 00000000000..ac6ac9979b3 --- /dev/null +++ b/app/helpers/invite_members_helper.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module InviteMembersHelper + include Gitlab::Utils::StrongMemoize + + def invite_members_allowed?(group) + Feature.enabled?(:invite_members_group_modal, group) && can?(current_user, :admin_group_member, group) + end + + def directly_invite_members? + strong_memoize(:directly_invite_members) do + experiment_enabled?(:invite_members_version_a) && can_import_members? + end + end + + def indirectly_invite_members? + strong_memoize(:indirectly_invite_members) do + experiment_enabled?(:invite_members_version_b) && !can_import_members? + end + end +end diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index b255597b18d..f8e7711959a 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -4,7 +4,10 @@ module IssuablesHelper include GitlabRoutingHelper def sidebar_gutter_toggle_icon - sidebar_gutter_collapsed? ? icon('angle-double-left', { 'aria-hidden': 'true' }) : icon('angle-double-right', { 'aria-hidden': 'true' }) + content_tag(:span, class: 'js-sidebar-toggle-container', data: { is_expanded: !sidebar_gutter_collapsed? }) do + sprite_icon('chevron-double-lg-left', css_class: "js-sidebar-expand #{'hidden' unless sidebar_gutter_collapsed?}") + + sprite_icon('chevron-double-lg-right', css_class: "js-sidebar-collapse #{'hidden' if sidebar_gutter_collapsed?}") + end end def sidebar_gutter_collapsed_class @@ -46,12 +49,6 @@ module IssuablesHelper "#{due_date.to_s(:medium)} (#{remaining_days_in_words(due_date, start_date)})" end - def sidebar_label_filter_path(base_path, label_name) - query_params = { label_name: [label_name] }.to_query - - "#{base_path}?#{query_params}" - end - def multi_label_name(current_labels, default_label) return default_label if current_labels.blank? @@ -79,6 +76,7 @@ module IssuablesHelper when Issue IssueSerializer when MergeRequest + opts[:experiment_enabled] = :suggest_pipeline if experiment_enabled?(:suggest_pipeline) && opts[:serializer] == 'widget' MergeRequestSerializer end @@ -206,7 +204,7 @@ module IssuablesHelper end if access = project.team.human_max_access(issuable.author_id) - output << content_tag(:span, access, class: "user-access-role has-tooltip d-none d-xl-inline-block gl-ml-3 ", title: _("This user is a %{access} of the %{name} project.") % { access: access.downcase, name: project.name }) + output << content_tag(:span, access, class: "user-access-role has-tooltip d-none d-xl-inline-block gl-ml-3 ", title: _("This user has the %{access} role in the %{name} project.") % { access: access.downcase, name: project.name }) elsif project.team.contributor?(issuable.author_id) output << content_tag(:span, _("Contributor"), class: "user-access-role has-tooltip d-none d-xl-inline-block gl-ml-3", title: _("This user has previously committed to the %{name} project.") % { name: project.name }) end @@ -224,19 +222,6 @@ module IssuablesHelper nil end - def issuable_labels_tooltip(labels, limit: 5) - first, last = labels.partition.with_index { |_, i| i < limit } - - if labels && labels.any? - label_names = first.collect { |label| label.fetch(:title) } - label_names << "and #{last.size} more" unless last.empty? - - label_names.join(', ') - else - _("Labels") - end - end - def issuables_state_counter_text(issuable_type, state, display_count) titles = { opened: "Open" @@ -247,7 +232,22 @@ module IssuablesHelper if display_count count = issuables_count_for_state(issuable_type, state) - html << " " << content_tag(:span, number_with_delimiter(count), class: 'badge badge-pill') + tag = + if count == -1 + tooltip = _("Couldn't calculate number of %{issuables}.") % { issuables: issuable_type.to_s.humanize(capitalize: false) } + + content_tag( + :span, + '?', + class: 'badge badge-pill has-tooltip', + aria: { label: tooltip }, + title: tooltip + ) + else + content_tag(:span, number_with_delimiter(count), class: 'badge badge-pill') + end + + html << " " << tag end html.html_safe @@ -342,6 +342,12 @@ module IssuablesHelper issuable.closed? ^ should_inverse ? reopen_issuable_path(issuable) : close_issuable_path(issuable) end + def toggle_draft_issuable_path(issuable) + wip_event = issuable.work_in_progress? ? 'unwip' : 'wip' + + issuable_path(issuable, { merge_request: { wip_event: wip_event } }) + end + def issuable_path(issuable, *options) polymorphic_path(issuable, *options) end @@ -386,6 +392,12 @@ module IssuablesHelper end end + def reviewer_sidebar_data(reviewer, merge_request: nil) + { avatar_url: reviewer.avatar_url, name: reviewer.name, username: reviewer.username }.tap do |data| + data[:can_merge] = merge_request.can_be_merged_by?(reviewer) if merge_request + end + end + def issuable_squash_option?(issuable, project) if issuable.persisted? issuable.squash @@ -420,7 +432,7 @@ module IssuablesHelper def issuable_todo_button_data(issuable, is_collapsed) { - todo_text: _('Add a To Do'), + todo_text: _('Add a to do'), mark_text: _('Mark as done'), todo_icon: sprite_icon('todo-add'), mark_icon: sprite_icon('todo-done', css_class: 'todo-undone'), diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index e8ea39d7ffc..dbf284e70e4 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -137,6 +137,21 @@ module IssuesHelper issue.moved_from.project.service_desk_enabled? && !issue.project.service_desk_enabled? end + + def use_startup_call? + request.query_parameters.empty? && @sort == 'created_date' + end + + def startup_call_params + { + state: 'opened', + with_labels_details: 'true', + page: 1, + per_page: 20, + order_by: 'created_at', + sort: 'desc' + } + end end IssuesHelper.prepend_if_ee('EE::IssuesHelper') diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb index 3142d7d7782..312d535a92c 100644 --- a/app/helpers/labels_helper.rb +++ b/app/helpers/labels_helper.rb @@ -36,11 +36,11 @@ module LabelsHelper # link_to_label(label) { "My Custom Label Text" } # # Returns a String - def link_to_label(label, type: :issue, tooltip: true, small: false, &block) + def link_to_label(label, type: :issue, tooltip: true, small: false, css_class: nil, &block) link = label.filter_path(type: type) if block_given? - link_to link, &block + link_to link, class: css_class, &block else render_label(label, link: link, tooltip: tooltip, small: small) end @@ -61,7 +61,7 @@ module LabelsHelper render_label_text( label.name, suffix: suffix, - css_class: text_color_class_for_bg(label.color), + css_class: "gl-label-text #{text_color_class_for_bg(label.color)}", bg_color: label.color ) end @@ -241,29 +241,14 @@ module LabelsHelper }.merge(opts) end - def sidebar_label_dropdown_data(issuable_type, issuable_sidebar) - label_dropdown_data(nil, { - default_label: "Labels", - field_name: "#{issuable_type}[label_names][]", - ability_name: issuable_type, - namespace_path: issuable_sidebar[:namespace_path], - project_path: issuable_sidebar[:project_path], - issue_update: issuable_sidebar[:issuable_json_path], - labels: issuable_sidebar[:project_labels_path], - display: 'static' - }) - end - - def label_from_hash(hash) - klass = hash[:group_id] ? GroupLabel : ProjectLabel - - klass.new(hash.slice(:color, :description, :title, :group_id, :project_id)) - end - def issuable_types ['issues', 'merge requests'] end + def show_labels_full_path?(project, group) + project || group&.subgroup? + end + private def render_label_link(label_html, link:, title:, dataset:) @@ -281,7 +266,7 @@ module LabelsHelper def render_label_text(name, suffix: '', css_class: nil, bg_color: nil) <<~HTML.chomp.html_safe '.html_safe, strong_close: ''.html_safe } + docs_link_url = help_page_path('topics/git/lfs/index') + docs_link_start = ''.html_safe % { url: docs_link_url } + + html_escape(_('Git LFS objects will be synced if LFS is %{docs_link_start}enabled for the project%{docs_link_end}. Push mirrors will %{strong_open}not%{strong_close} sync LFS objects over SSH.')) % + { docs_link_start: docs_link_start, docs_link_end: ''.html_safe, strong_open: ''.html_safe, strong_close: ''.html_safe } end end diff --git a/app/helpers/namespaces_helper.rb b/app/helpers/namespaces_helper.rb index 81451e398f2..8cf5cd49322 100644 --- a/app/helpers/namespaces_helper.rb +++ b/app/helpers/namespaces_helper.rb @@ -53,7 +53,7 @@ module NamespacesHelper selected = options.delete(:selected) || :current_user options[:groups] = current_user.manageable_groups_with_routes(include_groups_with_developer_maintainer_access: true) - namespaces_options(selected, options) + namespaces_options(selected, **options) end private diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb index 578c7ae7923..3c757a4ef26 100644 --- a/app/helpers/nav_helper.rb +++ b/app/helpers/nav_helper.rb @@ -55,7 +55,8 @@ module NavHelper current_path?('projects/merge_requests/conflicts#show') || current_path?('issues#show') || current_path?('milestones#show') || - current_path?('issues#designs') + current_path?('issues#designs') || + current_path?('incidents#show') end def admin_monitoring_nav_links diff --git a/app/helpers/operations_helper.rb b/app/helpers/operations_helper.rb index 521f394a920..9965a705a01 100644 --- a/app/helpers/operations_helper.rb +++ b/app/helpers/operations_helper.rb @@ -27,7 +27,7 @@ module OperationsHelper 'authorization_key' => alerts_service.token, 'prometheus_url' => notify_project_prometheus_alerts_url(@project, format: :json), 'url' => alerts_service.url, - 'alerts_setup_url' => help_page_path('user/project/integrations/generic_alerts.md', anchor: 'setting-up-generic-alerts'), + '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 } diff --git a/app/helpers/packages_helper.rb b/app/helpers/packages_helper.rb index e6ecc403a88..8f365fd0786 100644 --- a/app/helpers/packages_helper.rb +++ b/app/helpers/packages_helper.rb @@ -34,26 +34,22 @@ module PackagesHelper expose_url(api_v4_group___packages_composer_packages_path(id: group_id, format: '.json')) end - def packages_coming_soon_enabled?(resource) - ::Feature.enabled?(:packages_coming_soon, resource) && ::Gitlab.dev_env_or_com? - end - - def packages_coming_soon_data(resource) - return unless packages_coming_soon_enabled?(resource) - - { - project_path: ::Gitlab.com? ? 'gitlab-org/gitlab' : 'gitlab-org/gitlab-test', - suggested_contributions: help_page_path('user/packages/index', anchor: 'suggested-contributions') - } + def composer_config_repository_name(group_id) + "#{Gitlab.config.gitlab.host}/#{group_id}" end def packages_list_data(type, resource) { resource_id: resource.id, page_type: type, - empty_list_help_url: help_page_path('administration/packages/index'), + empty_list_help_url: help_page_path('user/packages/package_registry/index'), empty_list_illustration: image_path('illustrations/no-packages.svg'), - coming_soon_json: packages_coming_soon_data(resource).to_json + package_help_url: help_page_path('user/packages/index') } end + + def track_package_event(event_name, scope, **args) + ::Packages::CreateEventService.new(nil, current_user, event_name: event_name, scope: scope).execute + track_event(event_name, **args) + end end diff --git a/app/helpers/page_layout_helper.rb b/app/helpers/page_layout_helper.rb index a44760e85ca..6808ffc3e27 100644 --- a/app/helpers/page_layout_helper.rb +++ b/app/helpers/page_layout_helper.rb @@ -40,6 +40,14 @@ module PageLayoutHelper end end + def page_canonical_link(link = nil) + if link + @page_canonical_link = link + else + @page_canonical_link + end + end + def favicon Gitlab::Favicon.main end diff --git a/app/helpers/pagination_helper.rb b/app/helpers/pagination_helper.rb index d05153c9d4b..3167142e193 100644 --- a/app/helpers/pagination_helper.rb +++ b/app/helpers/pagination_helper.rb @@ -1,11 +1,13 @@ # frozen_string_literal: true module PaginationHelper - def paginate_collection(collection, remote: nil) + # total_pages will be inferred from the collection if nil. It is ignored if + # the collection is a Kaminari::PaginatableWithoutCount + def paginate_collection(collection, remote: nil, total_pages: nil) if collection.is_a?(Kaminari::PaginatableWithoutCount) paginate_without_count(collection) elsif collection.respond_to?(:total_pages) - paginate_with_count(collection, remote: remote) + paginate_with_count(collection, remote: remote, total_pages: total_pages) end end @@ -17,7 +19,7 @@ module PaginationHelper ) end - def paginate_with_count(collection, remote: nil) - paginate(collection, remote: remote, theme: 'gitlab') + def paginate_with_count(collection, remote: nil, total_pages: nil) + paginate(collection, remote: remote, theme: 'gitlab', total_pages: total_pages) end end diff --git a/app/helpers/preferences_helper.rb b/app/helpers/preferences_helper.rb index 2c406641882..9bf819febb0 100644 --- a/app/helpers/preferences_helper.rb +++ b/app/helpers/preferences_helper.rb @@ -61,8 +61,8 @@ module PreferencesHelper @user_application_theme ||= Gitlab::Themes.for_user(current_user).css_class end - def user_application_theme_name - @user_application_theme_name ||= Gitlab::Themes.for_user(current_user).name.downcase.tr(' ', '_') + def user_application_theme_css_filename + @user_application_theme_css_filename ||= Gitlab::Themes.for_user(current_user).css_filename end def user_color_scheme diff --git a/app/helpers/projects/alert_management_helper.rb b/app/helpers/projects/alert_management_helper.rb index c2f0b8854e1..5ce3736c8ef 100644 --- a/app/helpers/projects/alert_management_helper.rb +++ b/app/helpers/projects/alert_management_helper.rb @@ -9,7 +9,9 @@ module Projects::AlertManagementHelper 'populating-alerts-help-url' => help_page_url('operations/incident_management/index.md', anchor: 'enable-alert-management'), 'empty-alert-svg-path' => image_path('illustrations/alert-management-empty-state.svg'), 'user-can-enable-alert-management' => can?(current_user, :admin_operations, project).to_s, - 'alert-management-enabled' => alert_management_enabled?(project).to_s + 'alert-management-enabled' => alert_management_enabled?(project).to_s, + 'text-query': params[:search], + 'assignee-username-query': params[:assignee_username] } end diff --git a/app/helpers/projects/incidents_helper.rb b/app/helpers/projects/incidents_helper.rb index e96f0f5a384..63504cb55b9 100644 --- a/app/helpers/projects/incidents_helper.rb +++ b/app/helpers/projects/incidents_helper.rb @@ -1,14 +1,17 @@ # frozen_string_literal: true module Projects::IncidentsHelper - def incidents_data(project) + def incidents_data(project, params) { 'project-path' => project.full_path, 'new-issue-path' => new_project_issue_path(project), 'incident-template-name' => 'incident', 'incident-type' => 'incident', 'issue-path' => project_issues_path(project), - 'empty-list-svg-path' => image_path('illustrations/incident-empty-state.svg') + 'empty-list-svg-path' => image_path('illustrations/incident-empty-state.svg'), + 'text-query': params[:search], + 'author-username-query': params[:author_username], + 'assignee-username-query': params[:assignee_username] } end end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 72cc07b13a5..ae46135e890 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -301,7 +301,6 @@ module ProjectsHelper !disabled && !compact_mode end - # overridden in EE def settings_operations_available? can?(current_user, :read_environment, @project) end @@ -468,16 +467,25 @@ module ProjectsHelper serverless: :read_cluster, error_tracking: :read_sentry_issue, alert_management: :read_alert_management_alert, - incidents: :read_incidents, + incidents: :read_issue, labels: :read_label, issues: :read_issue, project_members: :read_project_member, - wiki: :read_wiki + wiki: :read_wiki, + feature_flags: :read_feature_flag } end def can_view_operations_tab?(current_user, project) - [:read_environment, :read_cluster, :metrics_dashboard].any? do |ability| + [ + :metrics_dashboard, + :read_alert_management_alert, + :read_environment, + :read_issue, + :read_sentry_issue, + :read_cluster, + :read_feature_flag + ].any? do |ability| can?(current_user, ability, project) end end @@ -555,7 +563,11 @@ module ProjectsHelper end def sidebar_operations_link_path(project = @project) - metrics_project_environments_path(project) if can?(current_user, :read_environment, project) + if can?(current_user, :read_environment, project) + metrics_project_environments_path(project) + else + project_feature_flags_path(project) + end end def project_last_activity(project) @@ -748,6 +760,8 @@ module ProjectsHelper logs product_analytics metrics_dashboard + feature_flags + tracings ] end @@ -758,10 +772,6 @@ module ProjectsHelper !project.repository.gitlab_ci_yml end - def native_code_navigation_enabled?(project) - Feature.enabled?(:code_navigation, project, default_enabled: true) - end - def show_visibility_confirm_modal?(project) project.unlink_forks_upon_visibility_decrease_enabled? && project.visibility_level > Gitlab::VisibilityLevel::PRIVATE && project.forks_count > 0 end @@ -772,9 +782,7 @@ module ProjectsHelper end def project_access_token_available?(project) - return false if ::Gitlab.com? - - ::Feature.enabled?(:resource_access_token, project, default_enabled: true) + can?(current_user, :admin_resource_access_tokens, project) end end diff --git a/app/helpers/releases_helper.rb b/app/helpers/releases_helper.rb index 979a68ecb7b..050b27840a0 100644 --- a/app/helpers/releases_helper.rb +++ b/app/helpers/releases_helper.rb @@ -29,6 +29,14 @@ module ReleasesHelper end end + def data_for_show_page + { + project_id: @project.id, + project_path: @project.full_path, + tag_name: @release.tag + } + end + def data_for_edit_release_page new_edit_pages_shared_data.merge( tag_name: @release.tag, @@ -48,6 +56,7 @@ module ReleasesHelper def new_edit_pages_shared_data { project_id: @project.id, + 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'), diff --git a/app/helpers/reminder_emails_helper.rb b/app/helpers/reminder_emails_helper.rb new file mode 100644 index 00000000000..bffb3cf7751 --- /dev/null +++ b/app/helpers/reminder_emails_helper.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true + +module ReminderEmailsHelper + def invitation_reminder_salutation(reminder_index, format: nil) + case reminder_index + when 0 + s_('InviteReminderEmail|Invitation pending') + when 1 + if format == :html + s_('InviteReminderEmail|Hey there %{wave_emoji}').html_safe % { wave_emoji: Gitlab::Emoji.gl_emoji_tag('wave') } + else + s_('InviteReminderEmail|Hey there!') + end + when 2 + s_('InviteReminderEmail|In case you missed it...') + end + end + + def invitation_reminder_body(member, reminder_index, format: nil) + options = { + inviter: sanitize_name(member.created_by.name), + strong_start: '', + strong_end: '', + project_or_group_name: member_source.human_name, + project_or_group: member_source.model_name.singular, + role: member.human_access.downcase + } + + if format == :html + options.merge!( + inviter: (link_to member.created_by.name, user_url(member.created_by)).html_safe, + strong_start: ''.html_safe, + strong_end: ''.html_safe + ) + end + + if reminder_index == 2 + options[:invitation_age] = (Date.current - member.created_at.to_date).to_i + end + + body = invitation_reminder_body_text(reminder_index) + + (format == :html ? html_escape(body) : body ) % options + end + + def invitation_reminder_accept_link(token, format: nil) + case format + when :html + link_to s_('InviteReminderEmail|Accept invitation'), invite_url(token), class: 'invite-btn-join' + else + s_('InviteReminderEmail|Accept invitation: %{invite_url}') % { invite_url: invite_url(token) } + end + end + + def invitation_reminder_decline_link(token, format: nil) + case format + when :html + link_to s_('InviteReminderEmail|Decline invitation'), decline_invite_url(token), class: 'invite-btn-decline' + else + s_('InviteReminderEmail|Decline invitation: %{decline_url}') % { decline_url: decline_invite_url(token) } + end + end + + private + + def invitation_reminder_body_text(reminder_index) + case reminder_index + when 0 + s_('InviteReminderEmail|%{inviter} is waiting for you to join the %{strong_start}%{project_or_group_name}%{strong_end} %{project_or_group} as a %{role}.') + when 1 + s_('InviteReminderEmail|This is a friendly reminder that %{inviter} invited you to join the %{strong_start}%{project_or_group_name}%{strong_end} %{project_or_group} as a %{role}.') + when 2 + s_("InviteReminderEmail|It's been %{invitation_age} days since %{inviter} invited you to join the %{strong_start}%{project_or_group_name}%{strong_end} %{project_or_group} as a %{role}. What would you like to do?") + end + end +end diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb index d55ad878b92..3467f6e9a44 100644 --- a/app/helpers/search_helper.rb +++ b/app/helpers/search_helper.rb @@ -1,14 +1,13 @@ # frozen_string_literal: true module SearchHelper - SEARCH_PERMITTED_PARAMS = [:search, :scope, :project_id, :group_id, :repository_ref, :snippets, :state].freeze + SEARCH_PERMITTED_PARAMS = [:search, :scope, :project_id, :group_id, :repository_ref, :snippets, :sort, :state, :confidential].freeze def search_autocomplete_opts(term) return unless current_user resources_results = [ - recent_merge_requests_autocomplete(term), - recent_issues_autocomplete(term), + recent_items_autocomplete(term), groups_autocomplete(term), projects_autocomplete(term) ].flatten @@ -27,6 +26,10 @@ module SearchHelper end end + def recent_items_autocomplete(term) + recent_merge_requests_autocomplete(term) + recent_issues_autocomplete(term) + end + def search_entries_info(collection, scope, term) return if collection.to_a.empty? @@ -86,13 +89,18 @@ module SearchHelper }).html_safe end + def repository_ref(project) + # Always #to_s the repository_ref param in case the value is also a number + params[:repository_ref].to_s.presence || project.default_branch + end + # Overridden in EE def search_blob_title(project, path) path end def search_service - @search_service ||= ::SearchService.new(current_user, params) + @search_service ||= ::SearchService.new(current_user, params.merge(confidential: Gitlab::Utils.to_boolean(params[:confidential]))) end private @@ -123,8 +131,7 @@ module SearchHelper { category: "Help", label: _("Rake Tasks Help"), url: help_page_path("raketasks/README") }, { category: "Help", label: _("SSH Keys Help"), url: help_page_path("ssh/README") }, { category: "Help", label: _("System Hooks Help"), url: help_page_path("system_hooks/system_hooks") }, - { category: "Help", label: _("Webhooks Help"), url: help_page_path("user/project/integrations/webhooks") }, - { category: "Help", label: _("Workflow Help"), url: help_page_path("workflow/README") } + { category: "Help", label: _("Webhooks Help"), url: help_page_path("user/project/integrations/webhooks") } ] end @@ -181,10 +188,10 @@ module SearchHelper end end - def recent_merge_requests_autocomplete(term, limit = 5) + def recent_merge_requests_autocomplete(term) return [] unless current_user - ::Gitlab::Search::RecentMergeRequests.new(user: current_user).search(term).limit(limit).map do |mr| + ::Gitlab::Search::RecentMergeRequests.new(user: current_user).search(term).map do |mr| { category: "Recent merge requests", id: mr.id, @@ -195,10 +202,10 @@ module SearchHelper end end - def recent_issues_autocomplete(term, limit = 5) + def recent_issues_autocomplete(term) return [] unless current_user - ::Gitlab::Search::RecentIssues.new(user: current_user).search(term).limit(limit).map do |i| + ::Gitlab::Search::RecentIssues.new(user: current_user).search(term).map do |i| { category: "Recent issues", id: i.id, @@ -255,11 +262,15 @@ module SearchHelper opts[:data]['labels-endpoint'] = project_labels_path(@project) opts[:data]['milestones-endpoint'] = project_milestones_path(@project) opts[:data]['releases-endpoint'] = project_releases_path(@project) + opts[:data]['environments-endpoint'] = + unfoldered_environment_names_project_path(@project) elsif @group.present? opts[:data]['group-id'] = @group.id opts[:data]['labels-endpoint'] = group_labels_path(@group) opts[:data]['milestones-endpoint'] = group_milestones_path(@group) opts[:data]['releases-endpoint'] = group_releases_path(@group) + opts[:data]['environments-endpoint'] = + unfoldered_environment_names_group_path(@group) else opts[:data]['labels-endpoint'] = dashboard_labels_path opts[:data]['milestones-endpoint'] = dashboard_milestones_path @@ -294,9 +305,25 @@ module SearchHelper sanitize(html, tags: %w(a p ol ul li pre code)) end - def show_user_search_tab? - return false if Feature.disabled?(:users_search, default_enabled: true) + def simple_search_highlight_and_truncate(text, phrase, options = {}) + text = Truncato.truncate( + text, + count_tags: false, + count_tail: false, + max_length: options.delete(:length) { 200 } + ) + + highlight(text, phrase.split, options) + end + + # _search_highlight is used in EE override + def highlight_and_truncate_issue(issue, search_term, _search_highlight) + return unless issue.description.present? + simple_search_highlight_and_truncate(issue.description, search_term, highlighter: '\1') + end + + def show_user_search_tab? if @project project_search_tabs?(:members) else diff --git a/app/helpers/services_helper.rb b/app/helpers/services_helper.rb index 6b5de73a831..114bbf59ae1 100644 --- a/app/helpers/services_helper.rb +++ b/app/helpers/services_helper.rb @@ -24,7 +24,7 @@ module ServicesHelper when "commit", "commit_events" s_("ProjectService|Event will be triggered when a commit is created/updated") when "deployment" - s_("ProjectService|Event will be triggered when a deployment finishes") + s_("ProjectService|Event will be triggered when a deployment starts or finishes") when "alert" s_("ProjectService|Event will be triggered when a new, unique alert is recorded") end @@ -124,6 +124,10 @@ module ServicesHelper @group.present? && Feature.enabled?(:group_level_integrations, @group) end + def instance_level_integrations? + !Gitlab.com? + end + extend self private diff --git a/app/helpers/snippets_helper.rb b/app/helpers/snippets_helper.rb index 94c46feb8ae..1be7e240c1a 100644 --- a/app/helpers/snippets_helper.rb +++ b/app/helpers/snippets_helper.rb @@ -32,31 +32,6 @@ module SnippetsHelper end end - # Get an array of line numbers surrounding a matching - # line, bounded by min/max. - # - # @returns Array of line numbers - def bounded_line_numbers(line, min, max, surrounding_lines) - lower = line - surrounding_lines > min ? line - surrounding_lines : min - upper = line + surrounding_lines < max ? line + surrounding_lines : max - (lower..upper).to_a - end - - def snippet_embed_tag(snippet) - content_tag(:script, nil, src: gitlab_snippet_url(snippet, format: :js)) - end - - def snippet_embed_input(snippet) - content_tag(:input, - nil, - type: :text, - readonly: true, - class: 'js-snippet-url-area snippet-embed-input form-control', - data: { url: gitlab_snippet_url(snippet) }, - value: snippet_embed_tag(snippet), - autocomplete: 'off') - end - def snippet_badge(snippet) return unless attrs = snippet_badge_attributes(snippet) diff --git a/app/helpers/ssh_keys_helper.rb b/app/helpers/ssh_keys_helper.rb new file mode 100644 index 00000000000..381db893943 --- /dev/null +++ b/app/helpers/ssh_keys_helper.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module SshKeysHelper + def ssh_key_delete_modal_data(key, path) + { + path: path, + method: 'delete', + qa_selector: 'delete_ssh_key_button', + modal_attributes: { + 'data-qa-selector': 'ssh_key_delete_modal', + title: _('Are you sure you want to delete this SSH key?'), + message: _('This action cannot be undone, and will permanently delete the %{key} SSH key') % { key: key.title }, + okVariant: 'danger', + okTitle: _('Delete') + } + } + end +end diff --git a/app/helpers/startupjs_helper.rb b/app/helpers/startupjs_helper.rb new file mode 100644 index 00000000000..b595590c7c9 --- /dev/null +++ b/app/helpers/startupjs_helper.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module StartupjsHelper + def page_startup_graphql_calls + @graphql_startup_calls + end + + def add_page_startup_graphql_call(query, variables = {}) + @graphql_startup_calls ||= [] + file_location = File.join(Rails.root, "app/graphql/queries/#{query}.query.graphql") + + return unless File.exist?(file_location) + + query_str = File.read(file_location) + @graphql_startup_calls << { query: query_str, variables: variables } + end +end diff --git a/app/helpers/suggest_pipeline_helper.rb b/app/helpers/suggest_pipeline_helper.rb index aa67f0ea770..d64e8d6f2cd 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? - Feature.enabled?(:suggest_pipeline) && + experiment_enabled?(:suggest_pipeline) && current_user && params[:suggest_gitlab_ci_yml] == 'true' end diff --git a/app/helpers/system_note_helper.rb b/app/helpers/system_note_helper.rb index 0227ad1092d..79f4810e13a 100644 --- a/app/helpers/system_note_helper.rb +++ b/app/helpers/system_note_helper.rb @@ -2,6 +2,8 @@ module SystemNoteHelper ICON_NAMES_BY_ACTION = { + 'approved' => 'approval', + 'unapproved' => 'unapproval', 'cherry_pick' => 'cherry-pick-commit', 'commit' => 'commit', 'description' => 'pencil-square', @@ -11,6 +13,7 @@ module SystemNoteHelper 'closed' => 'issue-close', 'time_tracking' => 'timer', 'assignee' => 'user', + 'reviewer' => 'user', 'title' => 'pencil-square', 'task' => 'task-done', 'label' => 'label', @@ -34,7 +37,8 @@ module SystemNoteHelper 'designs_discussion_added' => 'doc-image', 'status' => 'status', 'alert_issue_added' => 'issues', - 'new_alert_added' => 'warning' + 'new_alert_added' => 'warning', + 'severity' => 'information-o' }.freeze def system_note_icon_name(note) diff --git a/app/helpers/tags_helper.rb b/app/helpers/tags_helper.rb index 4984b51555d..bfc8803f514 100644 --- a/app/helpers/tags_helper.rb +++ b/app/helpers/tags_helper.rb @@ -38,4 +38,13 @@ module TagsHelper text.html_safe end + + def delete_tag_modal_attributes(tag_name) + { + title: s_('TagsPage|Delete tag'), + message: s_('TagsPage|Deleting the %{tag_name} tag cannot be undone. Are you sure?') % { tag_name: tag_name }, + okVariant: 'danger', + okTitle: s_('TagsPage|Delete tag') + }.to_json + end end diff --git a/app/helpers/timeboxes_helper.rb b/app/helpers/timeboxes_helper.rb index 34919f994ee..bbf8cf7dac3 100644 --- a/app/helpers/timeboxes_helper.rb +++ b/app/helpers/timeboxes_helper.rb @@ -228,8 +228,8 @@ module TimeboxesHelper end alias_method :milestone_date_range, :timebox_date_range - def milestone_tab_path(milestone, tab) - url_for(action: tab, format: :json) + def milestone_tab_path(milestone, tab, params = {}) + url_for(params.merge(action: tab, format: :json)) end def update_milestone_path(milestone, params = {}) diff --git a/app/helpers/todos_helper.rb b/app/helpers/todos_helper.rb index 9865f7dfbef..7b0e0df8998 100644 --- a/app/helpers/todos_helper.rb +++ b/app/helpers/todos_helper.rb @@ -16,6 +16,7 @@ module TodosHelper def todo_action_name(todo) case todo.action when Todo::ASSIGNED then todo.self_added? ? 'assigned' : 'assigned you' + when Todo::REVIEW_REQUESTED then 'requested a review of' when Todo::MENTIONED then "mentioned #{todo_action_subject(todo)} on" when Todo::BUILD_FAILED then 'The build failed for' when Todo::MARKED then 'added a todo for' @@ -26,6 +27,13 @@ module TodosHelper end end + def todo_self_addressing(todo) + case todo.action + when Todo::ASSIGNED then 'to yourself' + when Todo::REVIEW_REQUESTED then 'from yourself' + end + end + def todo_target_link(todo) text = raw(todo_target_type_name(todo) + ' ') + if todo.for_commit? @@ -141,6 +149,7 @@ module TodosHelper [ { id: '', text: 'Any Action' }, { id: Todo::ASSIGNED, text: 'Assigned' }, + { id: Todo::REVIEW_REQUESTED, text: 'Review requested' }, { id: Todo::MENTIONED, text: 'Mentioned' }, { id: Todo::MARKED, text: 'Added' }, { id: Todo::BUILD_FAILED, text: 'Pipelines' }, diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb index 7644ed783eb..563450159b5 100644 --- a/app/helpers/tree_helper.rb +++ b/app/helpers/tree_helper.rb @@ -1,6 +1,9 @@ # frozen_string_literal: true module TreeHelper + include BlobHelper + include WebIdeButtonHelper + FILE_LIMIT = 1_000 # Sorts a repository's tree so that folders are before files and renders @@ -31,7 +34,7 @@ module TreeHelper # mode - File unix mode # name - File name def tree_icon(type, mode, name) - icon([file_type_icon_class(type, mode, name), 'fw']) + sprite_icon(file_type_icon_class(type, mode, name)) end # Using Rails `*_path` methods can be slow, especially when generating @@ -199,38 +202,26 @@ module TreeHelper } end - def ide_base_path(project) - can_push_code = current_user&.can?(:push_code, project) - fork_path = current_user&.fork_of(project)&.full_path + def web_ide_button_data(options = {}) + { + project_path: project_to_use.full_path, + ref: ActionDispatch::Journey::Router::Utils.escape_path(@ref), - if can_push_code - project.full_path - else - fork_path || project.full_path - end - end + is_fork: fork?, + needs_to_fork: needs_to_fork?, + gitpod_enabled: !current_user.nil? && current_user.gitpod_enabled, + is_blob: !options[:blob].nil?, - def vue_ide_link_data(project, ref) - can_collaborate = can_collaborate_with_project?(project) - can_create_mr_from_fork = can?(current_user, :fork_project, project) && can?(current_user, :create_merge_request_in, project) - show_web_ide_button = (can_collaborate || current_user&.already_forked?(project) || can_create_mr_from_fork) + show_edit_button: show_edit_button?, + show_web_ide_button: show_web_ide_button?, + show_gitpod_button: show_gitpod_button?, - { - ide_base_path: ide_base_path(project), - needs_to_fork: !can_collaborate && !current_user&.already_forked?(project), - show_web_ide_button: show_web_ide_button, - show_gitpod_button: show_web_ide_button && Gitlab::Gitpod.feature_and_settings_enabled?(project), - gitpod_url: full_gitpod_url(project, ref), - gitpod_enabled: current_user&.gitpod_enabled + web_ide_url: web_ide_url, + edit_url: edit_url, + gitpod_url: gitpod_url } end - def full_gitpod_url(project, ref) - return "" unless Gitlab::Gitpod.feature_and_settings_enabled?(project) - - "#{Gitlab::CurrentSettings.gitpod_url}##{project_tree_url(project, tree_join(ref, @path || ''))}" - end - def directory_download_links(project, ref, archive_prefix) Gitlab::Workhorse::ARCHIVE_FORMATS.map do |fmt| { diff --git a/app/helpers/user_callouts_helper.rb b/app/helpers/user_callouts_helper.rb index 967271a8431..0cdf53d6174 100644 --- a/app/helpers/user_callouts_helper.rb +++ b/app/helpers/user_callouts_helper.rb @@ -9,7 +9,7 @@ module UserCalloutsHelper TABS_POSITION_HIGHLIGHT = 'tabs_position_highlight' WEBHOOKS_MOVED = 'webhooks_moved' CUSTOMIZE_HOMEPAGE = 'customize_homepage' - WEB_IDE_ALERT_DISMISSED = 'web_ide_alert_dismissed' + FEATURE_FLAGS_NEW_VERSION = 'feature_flags_new_version' def show_admin_integrations_moved? !user_dismissed?(ADMIN_INTEGRATIONS_MOVED) @@ -51,8 +51,8 @@ module UserCalloutsHelper customize_homepage && !user_dismissed?(CUSTOMIZE_HOMEPAGE) end - def show_web_ide_alert? - !user_dismissed?(WEB_IDE_ALERT_DISMISSED) + def show_feature_flags_new_version? + !user_dismissed?(FEATURE_FLAGS_NEW_VERSION) end private diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb index c1bca6b4c41..f47937e6d57 100644 --- a/app/helpers/users_helper.rb +++ b/app/helpers/users_helper.rb @@ -84,7 +84,7 @@ module UsersHelper def user_badges_in_admin_section(user) [].tap do |badges| - badges << { text: s_('AdminUsers|Blocked'), variant: 'danger' } if user.blocked? + badges << blocked_user_badge(user) if user.blocked? badges << { text: s_('AdminUsers|Admin'), variant: 'success' } if user.admin? badges << { text: s_('AdminUsers|External'), variant: 'secondary' } if user.external? badges << { text: s_("AdminUsers|It's you!"), variant: nil } if current_user == user @@ -106,8 +106,19 @@ module UsersHelper end end + def can_force_email_confirmation?(user) + !user.confirmed? + end + private + def blocked_user_badge(user) + pending_approval_badge = { text: s_('AdminUsers|Pending approval'), variant: 'info' } + return pending_approval_badge if user.blocked_pending_approval? + + { text: s_('AdminUsers|Blocked'), variant: 'danger' } + end + def get_profile_tabs tabs = [] diff --git a/app/helpers/visibility_level_helper.rb b/app/helpers/visibility_level_helper.rb index 304b58d232a..a7b9e17c898 100644 --- a/app/helpers/visibility_level_helper.rb +++ b/app/helpers/visibility_level_helper.rb @@ -23,8 +23,6 @@ module VisibilityLevelHelper project_visibility_level_description(level) when Group group_visibility_level_description(level) - when Snippet - snippet_visibility_level_description(level, form_model) end end @@ -50,21 +48,6 @@ module VisibilityLevelHelper end end - def snippet_visibility_level_description(level, snippet = nil) - case level - when Gitlab::VisibilityLevel::PRIVATE - if snippet.is_a? ProjectSnippet - _("The snippet is visible only to project members.") - else - _("The snippet is visible only to me.") - end - when Gitlab::VisibilityLevel::INTERNAL - _("The snippet is visible to any logged in user.") - when Gitlab::VisibilityLevel::PUBLIC - _("The snippet can be accessed without any authentication.") - end - end - # Note: these messages closely mirror the form validation strings found in the project # model and any changes or additons to these may also need to be made there. def disallowed_project_visibility_level_description(level, project) diff --git a/app/helpers/web_ide_button_helper.rb b/app/helpers/web_ide_button_helper.rb new file mode 100644 index 00000000000..0a4d47eed52 --- /dev/null +++ b/app/helpers/web_ide_button_helper.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +module WebIdeButtonHelper + def project_fork + current_user&.fork_of(@project) + end + + def project_to_use + fork? ? project_fork : @project + end + + def can_collaborate? + can_collaborate_with_project?(@project) + end + + def can_create_mr_from_fork? + can?(current_user, :fork_project, @project) && can?(current_user, :create_merge_request_in, @project) + end + + def show_web_ide_button? + can_collaborate? || can_create_mr_from_fork? + end + + def show_edit_button? + readable_blob? && show_web_ide_button? + end + + def show_gitpod_button? + show_web_ide_button? && Gitlab::Gitpod.feature_and_settings_enabled?(@project) + end + + def can_push_code? + current_user&.can?(:push_code, @project) + end + + def fork? + !project_fork.nil? && !can_push_code? + end + + def readable_blob? + !readable_blob({}, @path, @project, @ref).nil? + end + + def needs_to_fork? + !can_collaborate? && !current_user&.already_forked?(@project) + end + + def web_ide_url + ide_edit_path(project_to_use, @ref, @path || '') + end + + def edit_url + readable_blob? ? edit_blob_path(@project, @ref, @path || '') : '' + end + + def gitpod_url + return "" unless Gitlab::Gitpod.feature_and_settings_enabled?(@project) + + "#{Gitlab::CurrentSettings.gitpod_url}##{project_tree_url(@project, tree_join(@ref, @path || ''))}" + end +end diff --git a/app/helpers/webpack_helper.rb b/app/helpers/webpack_helper.rb index 345ddcf023a..170e3c45a21 100644 --- a/app/helpers/webpack_helper.rb +++ b/app/helpers/webpack_helper.rb @@ -57,10 +57,12 @@ module WebpackHelper end def webpack_public_host - if Rails.env.test? && Rails.configuration.webpack.dev_server.enabled - host = Rails.configuration.webpack.dev_server.host - port = Rails.configuration.webpack.dev_server.port - protocol = Rails.configuration.webpack.dev_server.https ? 'https' : 'http' + # We do not proxy the webpack output in the 'test' environment, + # so we must reference the webpack dev server directly. + if Rails.env.test? && Gitlab.config.webpack.dev_server.enabled + host = Gitlab.config.webpack.dev_server.host + port = Gitlab.config.webpack.dev_server.port + protocol = Gitlab.config.webpack.dev_server.https ? 'https' : 'http' "#{protocol}://#{host}:#{port}" else ActionController::Base.asset_host.try(:chomp, '/') @@ -68,8 +70,8 @@ module WebpackHelper end def webpack_public_path - relative_path = Rails.application.config.relative_url_root - webpack_path = Rails.application.config.webpack.public_path + relative_path = Gitlab.config.gitlab.relative_url_root + webpack_path = Gitlab.config.webpack.public_path File.join(webpack_public_host.to_s, relative_path.to_s, webpack_path.to_s, '') end end diff --git a/app/helpers/whats_new_helper.rb b/app/helpers/whats_new_helper.rb index f0044daa645..c183ed7f12a 100644 --- a/app/helpers/whats_new_helper.rb +++ b/app/helpers/whats_new_helper.rb @@ -1,24 +1,27 @@ # frozen_string_literal: true module WhatsNewHelper - EMPTY_JSON = ''.to_json + include Gitlab::WhatsNew - def whats_new_most_recent_release_items - YAML.load_file(most_recent_release_file_path).to_json + 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 + end + end - rescue => e - Gitlab::ErrorTracking.track_exception(e, yaml_file_path: most_recent_release_file_path) + def whats_new_storage_key + return unless whats_new_most_recent_version - EMPTY_JSON + ['display-whats-new-notification', whats_new_most_recent_version].join('-') end private - def most_recent_release_file_path - Dir.glob(files_path).max - end - - def files_path - Rails.root.join('data', 'whats_new', '*.yml') + 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 + end end end diff --git a/app/helpers/wiki_helper.rb b/app/helpers/wiki_helper.rb index 8c756b9370b..786081ca815 100644 --- a/app/helpers/wiki_helper.rb +++ b/app/helpers/wiki_helper.rb @@ -142,7 +142,8 @@ module WikiHelper 'wiki-format' => page.format, 'wiki-title-size' => page.title.bytesize, 'wiki-content-size' => page.raw_content.bytesize, - 'wiki-directory-nest-level' => page.path.scan('/').count + 'wiki-directory-nest-level' => page.path.scan('/').count, + 'wiki-container-type' => page.wiki.container.class.name } end -- cgit v1.2.1