From db384e6b19af03b4c3c82a5760d83a3fd79f7982 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Fri, 18 Aug 2023 10:50:51 +0000 Subject: Add latest changes from gitlab-org/gitlab@16-3-stable-ee --- .../admin/application_settings/settings_helper.rb | 60 ---------- app/helpers/admin/broadcast_messages_helper.rb | 123 +++++++++++++++++++++ app/helpers/admin/deploy_key_helper.rb | 2 +- app/helpers/application_helper.rb | 21 +++- app/helpers/application_settings_helper.rb | 11 +- app/helpers/broadcast_messages_helper.rb | 121 -------------------- app/helpers/ci/runners_helper.rb | 37 ++++++- app/helpers/commits_helper.rb | 27 +++-- app/helpers/dropdowns_helper.rb | 5 + app/helpers/emails_helper.rb | 39 +++++++ app/helpers/environments_helper.rb | 4 +- app/helpers/integrations_helper.rb | 5 +- app/helpers/issuables_helper.rb | 29 +++-- app/helpers/issues_helper.rb | 9 +- app/helpers/jira_connect_helper.rb | 2 +- app/helpers/labels_helper.rb | 8 +- app/helpers/markup_helper.rb | 32 +++--- app/helpers/merge_requests_helper.rb | 6 +- app/helpers/mirror_helper.rb | 5 + app/helpers/nav_helper.rb | 2 +- app/helpers/notes_helper.rb | 4 + app/helpers/packages_helper.rb | 2 +- app/helpers/profiles_helper.rb | 15 +++ app/helpers/projects/cluster_agents_helper.rb | 2 +- app/helpers/projects/observability_helper.rb | 10 ++ app/helpers/projects_helper.rb | 36 +++++- app/helpers/sessions_helper.rb | 23 +++- app/helpers/sidebars_helper.rb | 44 +++++--- app/helpers/snippets_helper.rb | 34 ++---- app/helpers/time_helper.rb | 24 ++-- app/helpers/todos_helper.rb | 1 + app/helpers/tree_helper.rb | 3 +- app/helpers/users/callouts_helper.rb | 2 + app/helpers/users_helper.rb | 36 ++++++ 34 files changed, 481 insertions(+), 303 deletions(-) create mode 100644 app/helpers/admin/broadcast_messages_helper.rb delete mode 100644 app/helpers/broadcast_messages_helper.rb (limited to 'app/helpers') diff --git a/app/helpers/admin/application_settings/settings_helper.rb b/app/helpers/admin/application_settings/settings_helper.rb index 9ea07ba4e6e..1741d6a953a 100644 --- a/app/helpers/admin/application_settings/settings_helper.rb +++ b/app/helpers/admin/application_settings/settings_helper.rb @@ -15,66 +15,6 @@ module Admin def project_missing_pipeline_yaml?(project) project.repository&.gitlab_ci_yml.blank? end - - def code_suggestions_description - link_start = code_suggestions_link_start(code_suggestions_docs_url) - - # rubocop:disable Layout/LineLength - # rubocop:disable Style/FormatString - s_('CodeSuggestionsSM|Enable Code Suggestions for users of this instance. %{link_start}What are Code Suggestions?%{link_end}') - .html_safe % { link_start: link_start, link_end: ''.html_safe } - # rubocop:enable Style/FormatString - # rubocop:enable Layout/LineLength - end - - def code_suggestions_token_explanation - link_start = code_suggestions_link_start(code_suggestions_pat_docs_url) - - # rubocop:disable Layout/LineLength - # rubocop:disable Style/FormatString - s_('CodeSuggestionsSM|On GitLab.com, create a token. This token is required to use Code Suggestions on your self-managed instance. %{link_start}How do I create a token?%{link_end}') - .html_safe % { link_start: link_start, link_end: ''.html_safe } - # rubocop:enable Style/FormatString - # rubocop:enable Layout/LineLength - end - - def code_suggestions_agreement - terms_link_start = code_suggestions_link_start(code_suggestions_agreement_url) - ai_docs_link_start = code_suggestions_link_start(code_suggestions_ai_docs_url) - - # rubocop:disable Layout/LineLength - # rubocop:disable Style/FormatString - s_('CodeSuggestionsSM|By enabling this feature, you agree to the %{terms_link_start}GitLab Testing Agreement%{link_end} and acknowledge that GitLab will send data from the instance, including personal data, to our %{ai_docs_link_start}AI providers%{link_end} to provide this feature.') - .html_safe % { terms_link_start: terms_link_start, ai_docs_link_start: ai_docs_link_start, link_end: ''.html_safe } - # rubocop:enable Style/FormatString - # rubocop:enable Layout/LineLength - end - - private - - # rubocop:disable Gitlab/DocUrl - # We want to link SaaS docs for flexibility for every URL related to Code Suggestions on Self Managed. - # We expect to update docs often during the Beta and we want to point user to the most up to date information. - def code_suggestions_docs_url - 'https://docs.gitlab.com/ee/user/project/repository/code_suggestions.html' - end - - def code_suggestions_agreement_url - 'https://about.gitlab.com/handbook/legal/testing-agreement/' - end - - def code_suggestions_ai_docs_url - 'https://docs.gitlab.com/ee/user/ai_features.html#third-party-services' - end - - def code_suggestions_pat_docs_url - 'https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html#create-a-personal-access-token' - end - # rubocop:enable Gitlab/DocUrl - - def code_suggestions_link_start(url) - "".html_safe - end end end end diff --git a/app/helpers/admin/broadcast_messages_helper.rb b/app/helpers/admin/broadcast_messages_helper.rb new file mode 100644 index 00000000000..e087361d52e --- /dev/null +++ b/app/helpers/admin/broadcast_messages_helper.rb @@ -0,0 +1,123 @@ +# frozen_string_literal: true + +module Admin + module BroadcastMessagesHelper + include Gitlab::Utils::StrongMemoize + + def current_broadcast_banner_messages + System::BroadcastMessage.current_banner_messages( + current_path: request.path, + user_access_level: current_user_access_level_for_project_or_group + ).select do |message| + cookies["hide_broadcast_message_#{message.id}"].blank? + end + end + + def current_broadcast_notification_message + not_hidden_messages = System::BroadcastMessage.current_notification_messages( + current_path: request.path, + user_access_level: current_user_access_level_for_project_or_group + ).select do |message| + cookies["hide_broadcast_message_#{message.id}"].blank? + end + not_hidden_messages.last + end + + def broadcast_message(message, opts = {}) + return unless message.present? + + render "shared/broadcast_message", { message: message, **opts } + end + + def broadcast_message_status(broadcast_message) + if broadcast_message.active? + 'Active' + elsif broadcast_message.ended? + 'Expired' + else + 'Pending' + end + end + + def render_broadcast_message(broadcast_message) + if broadcast_message.notification? + Banzai.render_field_and_post_process(broadcast_message, :message, { + current_user: current_user, + skip_project_check: true, + broadcast_message_placeholders: true + }).html_safe + else + Banzai.render_field(broadcast_message, :message).html_safe + end + end + + def target_access_level_options + System::BroadcastMessage::ALLOWED_TARGET_ACCESS_LEVELS.map do |access_level| + [Gitlab::Access.human_access(access_level), access_level] + end + end + + def target_access_levels_display(access_levels) + access_levels.map do |access_level| + Gitlab::Access.human_access(access_level) + end.join(', ') + end + + def admin_broadcast_messages_data(broadcast_messages) + broadcast_messages.map do |message| + { + id: message.id, + status: broadcast_message_status(message), + message: message.message, + theme: message.theme, + broadcast_type: message.broadcast_type, + dismissable: message.dismissable, + starts_at: message.starts_at.iso8601, + ends_at: message.ends_at.iso8601, + target_roles: target_access_levels_display(message.target_access_levels), + target_path: message.target_path, + type: message.broadcast_type.capitalize, + edit_path: edit_admin_broadcast_message_path(message), + delete_path: "#{admin_broadcast_message_path(message)}.js" + } + end.to_json + end + + def broadcast_message_data(broadcast_message) + { + id: broadcast_message.id, + message: broadcast_message.message, + broadcast_type: broadcast_message.broadcast_type, + theme: broadcast_message.theme, + dismissable: broadcast_message.dismissable.to_s, + target_access_levels: broadcast_message.target_access_levels, + messages_path: admin_broadcast_messages_path, + preview_path: preview_admin_broadcast_messages_path, + target_path: broadcast_message.target_path, + starts_at: broadcast_message.starts_at.iso8601, + ends_at: broadcast_message.ends_at.iso8601, + target_access_level_options: target_access_level_options.to_json, + show_in_cli: broadcast_message.show_in_cli.to_s + } + end + + private + + def current_user_access_level_for_project_or_group + return unless current_user.present? + + strong_memoize(:current_user_access_level_for_project_or_group) do + case controller + when Projects::ApplicationController + next unless @project + + @project.team.max_member_access(current_user.id) + when Groups::ApplicationController + next unless @group + + @group.max_member_access_for_user(current_user) + end + end + end + end +end diff --git a/app/helpers/admin/deploy_key_helper.rb b/app/helpers/admin/deploy_key_helper.rb index caf3757a68e..8b23c3e1e13 100644 --- a/app/helpers/admin/deploy_key_helper.rb +++ b/app/helpers/admin/deploy_key_helper.rb @@ -7,7 +7,7 @@ module Admin edit_path: edit_admin_deploy_key_path(':id'), delete_path: admin_deploy_key_path(':id'), create_path: new_admin_deploy_key_path, - empty_state_svg_path: image_path('illustrations/empty-state/empty-deploy-keys-lg.svg') + empty_state_svg_path: image_path('illustrations/empty-state/empty-access-token-md.svg') } end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index ce338a8afdc..2bf239979f7 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -317,7 +317,7 @@ module ApplicationHelper class_names << 'with-performance-bar' if performance_bar_enabled? class_names << 'with-top-bar' if show_super_sidebar? && !@hide_top_bar class_names << system_message_class - class_names << 'logged-out-marketing-header' if !current_user && ::Gitlab.com? + class_names << 'logged-out-marketing-header' if !current_user && ::Gitlab.com? && !show_super_sidebar? class_names end @@ -466,6 +466,25 @@ module ApplicationHelper form_with(**args.merge({ builder: ::Gitlab::FormBuilders::GitlabUiFormBuilder }), &block) end + def hidden_resource_icon(resource, css_class: nil) + issuable_title = _('This %{issuable} is hidden because its author has been banned') + + case resource + when Issue + title = format(issuable_title, issuable: _('issue')) + when MergeRequest + title = format(issuable_title, issuable: _('merge request')) + when Project + title = _('This project is hidden because its creator has been banned') + end + + return unless title + + content_tag(:span, class: 'has-tooltip', title: title) do + sprite_icon('spam', css_class: ['gl-vertical-align-text-bottom', css_class].compact_blank.join(' ')) + end + end + private def browser_id diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb index aa2466372e1..a45425474b5 100644 --- a/app/helpers/application_settings_helper.rb +++ b/app/helpers/application_settings_helper.rb @@ -218,7 +218,6 @@ module ApplicationSettingsHelper :admin_mode, :after_sign_out_path, :after_sign_up_text, - :ai_access_token, :akismet_api_key, :akismet_enabled, :allow_local_requests_from_hooks_and_services, @@ -242,6 +241,7 @@ module ApplicationSettingsHelper :default_artifacts_expire_in, :default_branch_name, :default_branch_protection, + :default_branch_protection_defaults, :default_ci_config_path, :default_group_visibility, :default_preferred_language, @@ -310,7 +310,6 @@ module ApplicationSettingsHelper :inactive_projects_delete_after_months, :inactive_projects_min_size_mb, :inactive_projects_send_warning_email_after_months, - :instance_level_code_suggestions_enabled, :invisible_captcha_enabled, :jira_connect_application_key, :jira_connect_public_key_storage_enabled, @@ -319,6 +318,8 @@ module ApplicationSettingsHelper :max_attachment_size, :max_export_size, :max_import_size, + :max_import_remote_file_size, + :max_decompressed_archive_size, :max_pages_size, :max_pages_custom_domains_per_project, :max_terraform_state_size_bytes, @@ -457,6 +458,7 @@ module ApplicationSettingsHelper :wiki_asciidoc_allow_uri_includes, :container_registry_delete_tags_service_timeout, :rate_limiting_response_text, + :package_registry_allow_anyone_to_pull_option, :package_registry_cleanup_policies_worker_capacity, :container_registry_expiration_policies_worker_capacity, :container_registry_cleanup_tags_service_max_list_size, @@ -491,6 +493,7 @@ module ApplicationSettingsHelper :invitation_flow_enforcement, :can_create_group, :bulk_import_enabled, + :bulk_import_max_download_file_size, :allow_runner_registration_token, :user_defaults_to_private_profile, :deactivation_email_additional_text, @@ -498,7 +501,9 @@ module ApplicationSettingsHelper :gitlab_dedicated_instance, :ci_max_includes, :allow_account_deletion, - :gitlab_shell_operation_limit + :gitlab_shell_operation_limit, + :namespace_aggregation_schedule_lease_duration_in_seconds, + :ci_max_total_yaml_size_bytes ].tap do |settings| next if Gitlab.com? diff --git a/app/helpers/broadcast_messages_helper.rb b/app/helpers/broadcast_messages_helper.rb deleted file mode 100644 index a62ffa144f1..00000000000 --- a/app/helpers/broadcast_messages_helper.rb +++ /dev/null @@ -1,121 +0,0 @@ -# frozen_string_literal: true - -module BroadcastMessagesHelper - include Gitlab::Utils::StrongMemoize - - def current_broadcast_banner_messages - BroadcastMessage.current_banner_messages( - current_path: request.path, - user_access_level: current_user_access_level_for_project_or_group - ).select do |message| - cookies["hide_broadcast_message_#{message.id}"].blank? - end - end - - def current_broadcast_notification_message - not_hidden_messages = BroadcastMessage.current_notification_messages( - current_path: request.path, - user_access_level: current_user_access_level_for_project_or_group - ).select do |message| - cookies["hide_broadcast_message_#{message.id}"].blank? - end - not_hidden_messages.last - end - - def broadcast_message(message, opts = {}) - return unless message.present? - - render "shared/broadcast_message", { message: message, **opts } - end - - def broadcast_message_status(broadcast_message) - if broadcast_message.active? - 'Active' - elsif broadcast_message.ended? - 'Expired' - else - 'Pending' - end - end - - def render_broadcast_message(broadcast_message) - if broadcast_message.notification? - Banzai.render_field_and_post_process(broadcast_message, :message, { - current_user: current_user, - skip_project_check: true, - broadcast_message_placeholders: true - }).html_safe - else - Banzai.render_field(broadcast_message, :message).html_safe - end - end - - def target_access_level_options - BroadcastMessage::ALLOWED_TARGET_ACCESS_LEVELS.map do |access_level| - [Gitlab::Access.human_access(access_level), access_level] - end - end - - def target_access_levels_display(access_levels) - access_levels.map do |access_level| - Gitlab::Access.human_access(access_level) - end.join(', ') - end - - def admin_broadcast_messages_data(broadcast_messages) - broadcast_messages.map do |message| - { - id: message.id, - status: broadcast_message_status(message), - message: message.message, - theme: message.theme, - broadcast_type: message.broadcast_type, - dismissable: message.dismissable, - starts_at: message.starts_at.iso8601, - ends_at: message.ends_at.iso8601, - target_roles: target_access_levels_display(message.target_access_levels), - target_path: message.target_path, - type: message.broadcast_type.capitalize, - edit_path: edit_admin_broadcast_message_path(message), - delete_path: "#{admin_broadcast_message_path(message)}.js" - } - end.to_json - end - - def broadcast_message_data(broadcast_message) - { - id: broadcast_message.id, - message: broadcast_message.message, - broadcast_type: broadcast_message.broadcast_type, - theme: broadcast_message.theme, - dismissable: broadcast_message.dismissable.to_s, - target_access_levels: broadcast_message.target_access_levels, - messages_path: admin_broadcast_messages_path, - preview_path: preview_admin_broadcast_messages_path, - target_path: broadcast_message.target_path, - starts_at: broadcast_message.starts_at.iso8601, - ends_at: broadcast_message.ends_at.iso8601, - target_access_level_options: target_access_level_options.to_json, - show_in_cli: broadcast_message.show_in_cli.to_s - } - end - - private - - def current_user_access_level_for_project_or_group - return unless current_user.present? - - strong_memoize(:current_user_access_level_for_project_or_group) do - case controller - when Projects::ApplicationController - next unless @project - - @project.team.max_member_access(current_user.id) - when Groups::ApplicationController - next unless @group - - @group.max_member_access_for_user(current_user) - end - end - end -end diff --git a/app/helpers/ci/runners_helper.rb b/app/helpers/ci/runners_helper.rb index b1481f668bb..7cc554bbeeb 100644 --- a/app/helpers/ci/runners_helper.rb +++ b/app/helpers/ci/runners_helper.rb @@ -71,21 +71,35 @@ module Ci new_runner_path: new_admin_runner_path, registration_token: Gitlab::CurrentSettings.runners_registration_token, online_contact_timeout_secs: ::Ci::Runner::ONLINE_CONTACT_TIMEOUT.to_i, - stale_timeout_secs: ::Ci::Runner::STALE_TIMEOUT.to_i + stale_timeout_secs: ::Ci::Runner::STALE_TIMEOUT.to_i, + tag_suggestions_path: tag_list_admin_runners_path(format: :json) } end def group_shared_runners_settings_data(group) - { + data = { group_id: group.id, group_name: group.name, group_is_empty: (group.projects.empty? && group.children.empty?).to_s, shared_runners_setting: group.shared_runners_setting, - parent_shared_runners_setting: group.parent&.shared_runners_setting, + runner_enabled_value: Namespace::SR_ENABLED, runner_disabled_value: Namespace::SR_DISABLED_AND_UNOVERRIDABLE, - runner_allow_override_value: Namespace::SR_DISABLED_AND_OVERRIDABLE + runner_allow_override_value: Namespace::SR_DISABLED_AND_OVERRIDABLE, + + parent_shared_runners_setting: group.parent&.shared_runners_setting, + parent_name: nil, + parent_settings_path: nil } + + if group.parent && can?(current_user, :admin_group, group.parent) + data.merge!({ + parent_name: group.parent.name, + parent_settings_path: group_settings_ci_cd_path(group.parent, anchor: 'runners-settings') + }) + end + + data end def group_runners_data_attributes(group) @@ -99,11 +113,22 @@ module Ci end def toggle_shared_runners_settings_data(project) - { + data = { is_enabled: project.shared_runners_enabled?.to_s, is_disabled_and_unoverridable: (project.group&.shared_runners_setting == Namespace::SR_DISABLED_AND_UNOVERRIDABLE).to_s, - update_path: toggle_shared_runners_project_runners_path(project) + update_path: toggle_shared_runners_project_runners_path(project), + group_name: nil, + group_settings_path: nil } + + if project.group && can?(current_user, :admin_group, project.group) + data.merge!({ + group_name: project.group.name, + group_settings_path: group_settings_ci_cd_path(project.group, anchor: 'runners-settings') + }) + end + + data end end end diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index ee86553d75d..42871dcc56f 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -111,7 +111,7 @@ module CommitsHelper tooltip = _("Browse Directory") end - link_to url, class: "btn gl-button btn-default btn-icon has-tooltip", title: tooltip, data: { container: "body" } do + render Pajamas::ButtonComponent.new(href: url, button_options: { title: tooltip, class: 'has-tooltip btn-icon', data: { container: 'body' } }) do sprite_icon('folder-open') end end @@ -143,6 +143,16 @@ module CommitsHelper end end + def local_committed_date(commit, user) + server_timezone = Time.zone + user_timezone = user.timezone if user + user_timezone = ActiveSupport::TimeZone.new(user_timezone) if user_timezone + + timezone = user_timezone || server_timezone + + commit.committed_date.in_time_zone(timezone).to_date + end + def cherry_pick_projects_data(project) [project, project.forked_from_project].compact.map do |project| { @@ -188,12 +198,11 @@ module CommitsHelper entity = mode == 'raw' ? 'rawButton' : 'renderedButton' title = "Display #{mode} diff" - link_to( - "##{mode}-diff-#{file_hash}", - class: "btn gl-button btn-default btn-file-option has-tooltip btn-show-#{mode}-diff", - title: title, - data: { file_hash: file_hash, diff_toggle_entity: entity } - ) do + render Pajamas::ButtonComponent.new( + href: "##{mode}-diff-#{file_hash}", + button_options: { title: title, + class: "btn-file-option has-tooltip btn-show-#{mode}-diff", + data: { file_hash: file_hash, diff_toggle_entity: entity } }) do sprite_icon(icon) end end @@ -242,7 +251,7 @@ module CommitsHelper path = project_blob_path(project, tree_join(commit_sha, diff_new_path)) title = replaced ? _('View replaced file @ ') : _('View file @ ') - link_to(path, class: 'btn gl-button btn-default gl-ml-3') do + render Pajamas::ButtonComponent.new(href: path, button_options: { class: 'gl-ml-3' }) do raw(title) + content_tag(:span, truncate_sha(commit_sha), class: 'commit-sha') end end @@ -253,7 +262,7 @@ module CommitsHelper external_url = environment.external_url_for(diff_new_path, commit_sha) return unless external_url - link_to(external_url, class: 'btn gl-button btn-default btn-file-option has-tooltip', target: '_blank', rel: 'noopener noreferrer', title: "View on #{environment.formatted_external_url}", data: { container: 'body' }) do + render Pajamas::ButtonComponent.new(href: external_url, target: '_blank', button_options: { rel: 'noopener noreferrer', title: "View on #{environment.formatted_external_url}", data: { container: 'body' } }) do sprite_icon('external-link') end end diff --git a/app/helpers/dropdowns_helper.rb b/app/helpers/dropdowns_helper.rb index 475ba3dcba8..ce18bedd25f 100644 --- a/app/helpers/dropdowns_helper.rb +++ b/app/helpers/dropdowns_helper.rb @@ -1,6 +1,11 @@ # frozen_string_literal: true module DropdownsHelper + def dropdown_data_attr(options: {}) + output = content_tag(:div, "", id: "js-template-selectors-menu", data: options[:data]) + output.html_safe + end + # rubocop:disable Metrics/CyclomaticComplexity def dropdown_tag(toggle_text, options: {}, &block) content_tag :div, class: "dropdown #{options[:wrapper_class] if options.key?(:wrapper_class)}" do diff --git a/app/helpers/emails_helper.rb b/app/helpers/emails_helper.rb index 7213bd074fc..af0f1bd6808 100644 --- a/app/helpers/emails_helper.rb +++ b/app/helpers/emails_helper.rb @@ -2,6 +2,7 @@ module EmailsHelper include AppearancesHelper + include SafeFormatHelper # Google Actions # https://developers.google.com/gmail/markup/reference/go-to-action @@ -236,6 +237,44 @@ module EmailsHelper end end + def member_about_to_expire_text(member_source, days_to_expire, format: nil) + days_formatted = pluralize(days_to_expire, 'day') + + case member_source + when Project + url = project_url(member_source) + when Group + url = group_url(member_source) + end + + case format + when :html + link_to = generate_link(member_source.human_name, url).html_safe + safe_format(_("Your membership in %{link_to} %{project_or_group_name} will expire in %{days_formatted}."), link_to: link_to, project_or_group_name: member_source.model_name.singular, days_formatted: days_formatted) + else + _("Your membership in %{project_or_group} %{project_or_group_name} will expire in %{days_formatted}.") % { project_or_group: member_source.human_name, project_or_group_name: member_source.model_name.singular, days_formatted: days_formatted } + end + end + + def member_about_to_expire_link(member, member_source, format: nil) + project_or_group = member_source.human_name + + case member_source + when Project + url = project_project_members_url(member_source, search: member.user.username) + when Group + url = group_group_members_url(member_source, search: member.user.username) + end + + case format + when :html + link_to = generate_link("#{member_source.class.name.downcase} membership", url).html_safe + safe_format(_('For additional information, review your %{link_to} or contact your %{project_or_group} owner.'), link_to: link_to, project_or_group: project_or_group) + else + _('For additional information, review your %{project_or_group} membership: %{url} or contact your %{project_or_group} owner.') % { project_or_group: project_or_group, url: url } + end + end + def group_membership_expiration_changed_text(member, group) if member.expires? days = (member.expires_at - Date.today).to_i diff --git a/app/helpers/environments_helper.rb b/app/helpers/environments_helper.rb index 3360a5256af..cd768ba8a7b 100644 --- a/app/helpers/environments_helper.rb +++ b/app/helpers/environments_helper.rb @@ -54,7 +54,6 @@ module EnvironmentsHelper { 'settings_path' => edit_project_settings_integration_path(project, 'prometheus'), 'clusters_path' => project_clusters_path(project), - 'dashboards_endpoint' => project_performance_monitoring_dashboards_path(project, format: :json), 'default_branch' => project.default_branch, 'project_path' => project_path(project), 'tags_path' => project_tags_path(project), @@ -82,8 +81,7 @@ module EnvironmentsHelper { 'deployments_endpoint' => project_environment_deployments_path(project, environment, format: :json), 'operations_settings_path' => project_settings_operations_path(project), - 'can_access_operations_settings' => can?(current_user, :admin_operations, project).to_s, - 'panel_preview_endpoint' => project_metrics_dashboards_builder_path(project, format: :json) + 'can_access_operations_settings' => can?(current_user, :admin_operations, project).to_s } end diff --git a/app/helpers/integrations_helper.rb b/app/helpers/integrations_helper.rb index 4b5fadf3397..645a08bfcc0 100644 --- a/app/helpers/integrations_helper.rb +++ b/app/helpers/integrations_helper.rb @@ -162,7 +162,7 @@ module IntegrationsHelper end def integrations_help_page_path - help_page_path('user/admin_area/settings/project_integration_management') + help_page_path('administration/settings/project_integration_management') end def project_jira_issues_integration? @@ -179,7 +179,8 @@ module IntegrationsHelper 'incident' => _('Incident'), 'test_case' => _('Test case'), 'requirement' => _('Requirement'), - 'task' => _('Task') + 'task' => _('Task'), + 'ticket' => _('Service Desk Ticket') } issue_type_i18n_map[issue_type] || issue_type diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index e921e9bae4d..c83545fa7a7 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -251,9 +251,16 @@ module IssuablesHelper def issue_only_initial_data(issuable) return {} unless issuable.is_a?(Issue) - { + data = { + authorId: issuable.author.id, + authorName: issuable.author.name, + authorUsername: issuable.author.username, + authorWebUrl: url_for(user_path(issuable.author)), + createdAt: issuable.created_at.to_time.iso8601, hasClosingMergeRequest: issuable.merge_requests_count(current_user) != 0, + isFirstContribution: issuable.first_contribution?, issueType: issuable.issue_type, + serviceDeskReplyTo: issuable.present(current_user: current_user).service_desk_reply_to, zoomMeetingUrl: ZoomMeeting.canonical_meeting_url(issuable), sentryIssueIdentifier: SentryIssue.find_by(issue: issuable)&.sentry_issue_identifier, # rubocop:disable CodeReuse/ActiveRecord iid: issuable.iid.to_s, @@ -261,6 +268,16 @@ module IssuablesHelper canCreateIncident: create_issue_type_allowed?(issuable.project, :incident), **incident_only_initial_data(issuable) } + + data.tap do |d| + if issuable.duplicated? && can?(current_user, :read_issue, issuable.duplicated_to) + d[:duplicatedToIssueUrl] = url_for([issuable.duplicated_to.project, issuable.duplicated_to, { only_path: false }]) + end + + if issuable.moved? && can?(current_user, :read_issue, issuable.moved_to) + d[:movedToIssueUrl] = url_for([issuable.moved_to.project, issuable.moved_to, { only_path: false }]) + end + end end def incident_only_initial_data(issue) @@ -366,16 +383,6 @@ module IssuablesHelper end end - def hidden_issuable_icon(issuable) - title = format( - _('This %{issuable} is hidden because its author has been banned'), - issuable: issuable.is_a?(Issue) ? _('issue') : _('merge request') - ) - content_tag(:span, class: 'has-tooltip', title: title) do - sprite_icon('spam', css_class: 'gl-vertical-align-text-bottom') - end - end - def issuable_type_selector_data(issuable) { selected_type: issuable.issue_type, diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index d9b9b27d16c..ed655b562c2 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -55,7 +55,7 @@ module IssuesHelper def hidden_issue_icon(issue) return unless issue_hidden?(issue) - hidden_issuable_icon(issue) + hidden_resource_icon(issue) end def award_user_list(awards, current_user, limit: 10) @@ -195,7 +195,8 @@ module IssuesHelper is_signed_in: current_user.present?.to_s, jira_integration_path: help_page_url('integration/jira/issues', anchor: 'view-jira-issues'), rss_path: url_for(safe_params.merge(rss_url_options)), - sign_in_path: new_user_session_path + sign_in_path: new_user_session_path, + has_issue_date_filter_feature: Feature.enabled?(:issue_date_filter, namespace).to_s } end @@ -220,7 +221,9 @@ module IssuesHelper quick_actions_help_path: help_page_path('user/project/quick_actions'), releases_path: project_releases_path(project, format: :json), reset_path: new_issuable_address_project_path(project, issuable_type: 'issue'), - show_new_issue_link: show_new_issue_link?(project).to_s + show_new_issue_link: show_new_issue_link?(project).to_s, + report_abuse_path: add_category_abuse_reports_path, + register_path: new_user_registration_path(redirect_to_referer: 'yes') ) end diff --git a/app/helpers/jira_connect_helper.rb b/app/helpers/jira_connect_helper.rb index 5cf68db0611..2309dfc2a2b 100644 --- a/app/helpers/jira_connect_helper.rb +++ b/app/helpers/jira_connect_helper.rb @@ -5,7 +5,7 @@ module JiraConnectHelper skip_groups = subscriptions.map(&:namespace_id) { - groups_path: api_v4_groups_path(params: { min_access_level: Gitlab::Access::MAINTAINER, skip_groups: skip_groups }), + groups_path: api_v4_groups_path(params: { skip_groups: skip_groups }), subscriptions: subscriptions.map { |s| serialize_subscription(s) }.to_json, subscriptions_path: jira_connect_subscriptions_path(format: :json), gitlab_user_path: current_user ? user_path(current_user) : nil, diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb index c4967a42a45..79bab0969d1 100644 --- a/app/helpers/labels_helper.rb +++ b/app/helpers/labels_helper.rb @@ -46,11 +46,11 @@ module LabelsHelper end end - def render_label(label, link: nil, tooltip: true, dataset: nil, small: false) + def render_label(label, link: nil, tooltip: true, dataset: nil, small: false, tooltip_shows_title: false) html = render_colored_label(label) if link - title = label_tooltip_title(label) if tooltip + title = label_tooltip_title(label, tooltip_shows_title: tooltip_shows_title) if tooltip html = render_label_link(html, link: link, title: title, dataset: dataset) end @@ -74,8 +74,8 @@ module LabelsHelper %(#{label_html}).html_safe end - def label_tooltip_title(label) - Sanitize.clean(label.description) + def label_tooltip_title(label, tooltip_shows_title: false) + Sanitize.clean(tooltip_shows_title ? label.title : label.description) end def suggested_colors diff --git a/app/helpers/markup_helper.rb b/app/helpers/markup_helper.rb index 91fce6d6820..1a44f3554b0 100644 --- a/app/helpers/markup_helper.rb +++ b/app/helpers/markup_helper.rb @@ -110,8 +110,8 @@ module MarkupHelper prepare_asciidoc_context(file_name, context) html = Markup::RenderingService - .new(text, file_name: file_name, context: context, postprocess_context: postprocess_context) - .execute + .new(text, file_name: file_name, context: context, postprocess_context: postprocess_context) + .execute Hamlit::RailsHelpers.preserve(html) end @@ -124,8 +124,8 @@ module MarkupHelper prepare_asciidoc_context(wiki_page.path, context) html = Markup::RenderingService - .new(text, file_name: wiki_page.path, context: context, postprocess_context: postprocess_context) - .execute + .new(text, file_name: wiki_page.path, context: context, postprocess_context: postprocess_context) + .execute Hamlit::RailsHelpers.preserve(html) end @@ -192,15 +192,21 @@ module MarkupHelper def markdown_toolbar_button(options = {}) data = options[:data].merge({ container: 'body' }) - css_classes = %w[gl-button btn btn-default-tertiary btn-icon btn-sm js-md has-tooltip] << options[:css_class].to_s - content_tag :button, - type: 'button', - class: css_classes.join(' '), - data: data, - title: options[:title], - aria: { label: options[:title] } do - sprite_icon(options[:icon]) - end + css_classes = %w[js-md has-tooltip] << options[:css_class].to_s + + render Pajamas::ButtonComponent.new( + category: :tertiary, + size: :small, + icon: options[:icon], + button_options: { + class: css_classes.join(' '), + data: data, + title: options[:title], + aria: { + label: options[:title] + } + } + ) end def render_markdown_field(object, field, context = {}) diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb index af1c85532c3..32a183d6cd8 100644 --- a/app/helpers/merge_requests_helper.rb +++ b/app/helpers/merge_requests_helper.rb @@ -303,12 +303,16 @@ module MergeRequestsHelper def hidden_merge_request_icon(merge_request) return unless merge_request.hidden? - hidden_issuable_icon(merge_request) + hidden_resource_icon(merge_request) end def tab_count_display(merge_request, count) merge_request.preparing? ? "-" : count end + + def review_bar_data(_merge_request, _user) + { new_comment_template_path: profile_comment_templates_path } + end end MergeRequestsHelper.prepend_mod_with('MergeRequestsHelper') diff --git a/app/helpers/mirror_helper.rb b/app/helpers/mirror_helper.rb index 06deaeb5e9e..158aa5e0944 100644 --- a/app/helpers/mirror_helper.rb +++ b/app/helpers/mirror_helper.rb @@ -15,6 +15,11 @@ module MirrorHelper 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 + + def mirrored_repositories_count + count = @project.mirror == true ? 1 : 0 + count + @project.remote_mirrors.to_a.count(&:enabled) + end end MirrorHelper.prepend_mod_with('MirrorHelper') diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb index c7864c1d45f..4cbd5029ac9 100644 --- a/app/helpers/nav_helper.rb +++ b/app/helpers/nav_helper.rb @@ -90,7 +90,7 @@ module NavHelper # The new sidebar is not enabled for anonymous use # Once we enable the new sidebar by default, this # should return true - return false unless user + return Feature.enabled?(:super_sidebar_logged_out) unless user # Users who got the special `super_sidebar_nav_enrolled` enabled, # see the new nav as long as they don't explicitly opt-out via the toggle diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb index 3e8872dc199..af8da86b391 100644 --- a/app/helpers/notes_helper.rb +++ b/app/helpers/notes_helper.rb @@ -178,6 +178,10 @@ module NotesHelper def notes_data(issuable) data = { + noteableType: @noteable.class.underscore, + noteableId: @noteable.id, + projectId: @project&.id, + groupId: @group&.id, discussionsPath: discussions_path(issuable), registerPath: new_session_path(:user, redirect_to_referer: 'yes', anchor: 'register-pane'), newSessionPath: new_session_path(:user, redirect_to_referer: 'yes'), diff --git a/app/helpers/packages_helper.rb b/app/helpers/packages_helper.rb index 31fcc77925b..fefc19d7c1a 100644 --- a/app/helpers/packages_helper.rb +++ b/app/helpers/packages_helper.rb @@ -16,7 +16,7 @@ module PackagesHelper end def package_registry_project_url(project_id, registry_type = :maven) - project_api_path = expose_path(api_v4_projects_path(id: project_id)) + project_api_path = api_v4_projects_path(id: project_id) package_registry_project_path = "#{project_api_path}/packages/#{registry_type}" expose_url(package_registry_project_path) end diff --git a/app/helpers/profiles_helper.rb b/app/helpers/profiles_helper.rb index 26463003f8d..05605394d57 100644 --- a/app/helpers/profiles_helper.rb +++ b/app/helpers/profiles_helper.rb @@ -73,6 +73,21 @@ module ProfilesHelper def prevent_delete_account? false end + + def user_profile_data(user) + { + profile_path: profile_path, + profile_avatar_path: profile_avatar_path, + avatar_url: avatar_icon_for_user(user, current_user: current_user), + has_avatar: user.avatar?.to_s, + gravatar_enabled: gravatar_enabled?.to_s, + gravatar_link: { hostname: Gitlab.config.gravatar.host, url: "https://#{Gitlab.config.gravatar.host}" }.to_json, + brand_profile_image_guidelines: current_appearance&.profile_image_guidelines? ? brand_profile_image_guidelines : '', + cropper_css_path: ActionController::Base.helpers.stylesheet_path('lazy_bundles/cropper.css'), + user_path: user_path(current_user), + **user_status_properties(user) + } + end end ProfilesHelper.prepend_mod diff --git a/app/helpers/projects/cluster_agents_helper.rb b/app/helpers/projects/cluster_agents_helper.rb index f62f5eadfb4..d285cfa03c2 100644 --- a/app/helpers/projects/cluster_agents_helper.rb +++ b/app/helpers/projects/cluster_agents_helper.rb @@ -6,7 +6,7 @@ module Projects::ClusterAgentsHelper activity_empty_state_image: image_path('illustrations/empty-state/empty-state-agents.svg'), agent_name: agent_name, can_admin_vulnerability: can?(current_user, :admin_vulnerability, project).to_s, - empty_state_svg_path: image_path('illustrations/operations-dashboard_empty.svg'), + empty_state_svg_path: image_path('illustrations/empty-state/empty-radar-md.svg'), project_path: project.full_path, kas_address: Gitlab::Kas.external_url, kas_version: Gitlab::Kas.version_info, diff --git a/app/helpers/projects/observability_helper.rb b/app/helpers/projects/observability_helper.rb index 24bc1928a36..4515fdb1bc3 100644 --- a/app/helpers/projects/observability_helper.rb +++ b/app/helpers/projects/observability_helper.rb @@ -9,5 +9,15 @@ module Projects oauthUrl: Gitlab::Observability.oauth_url }) end + + def observability_tracing_details_model(project, trace_id) + Gitlab::Json.generate({ + tracingIndexUrl: namespace_project_tracing_index_path(project.group, project), + traceId: trace_id, + tracingUrl: Gitlab::Observability.tracing_url(project), + provisioningUrl: Gitlab::Observability.provisioning_url(project), + oauthUrl: Gitlab::Observability.oauth_url + }) + end end end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index e27ee1acb22..754e1b7c2a2 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -464,14 +464,23 @@ module ProjectsHelper project.forking_enabled? && can?(user, :read_code, project) end - def fork_button_disabled_tooltip(project) + def fork_button_data_attributes(project) return unless current_user - if !current_user.can?(:fork_project, project) - s_("ProjectOverview|You don't have permission to fork this project") - elsif !current_user.can?(:create_fork) - s_('ProjectOverview|You have reached your project limit') + if current_user.already_forked?(project) && current_user.forkable_namespaces.size < 2 + user_fork_url = namespace_project_path(current_user, current_user.fork_of(project)) end + + { + forks_count: project.forks_count, + project_full_path: project.full_path, + project_forks_url: project_forks_path(project), + user_fork_url: user_fork_url, + new_fork_url: new_project_fork_path(project), + can_read_code: can?(current_user, :read_code, project).to_s, + can_fork_project: can?(current_user, :fork_project, project).to_s, + can_create_fork: can?(current_user, :create_fork).to_s + } end def import_from_bitbucket_message @@ -551,6 +560,20 @@ module ProjectsHelper project_settings_repository_path(@project, anchor: 'js-branch-rules') end + def visibility_level_content(project, css_class: nil, icon_css_class: nil) + if project.created_and_owned_by_banned_user? && Feature.enabled?(:hide_projects_of_banned_users) + return hidden_resource_icon(project, css_class: css_class) + end + + title = visibility_icon_description(project) + container_class = ['has-tooltip', css_class].compact.join(' ') + data = { container: 'body', placement: 'top' } + + content_tag(:span, class: container_class, data: data, title: title) do + visibility_level_icon(project.visibility_level, options: { class: icon_css_class }) + end + end + private def can_admin_project_clusters?(project) @@ -706,6 +729,7 @@ module ProjectsHelper { packagesEnabled: !!project.packages_enabled, packageRegistryAccessLevel: feature.package_registry_access_level, + packageRegistryAllowAnyoneToPullOption: ::Gitlab::CurrentSettings.package_registry_allow_anyone_to_pull_option, visibilityLevel: project.visibility_level, requestAccessEnabled: !!project.request_access_enabled, issuesAccessLevel: feature.issues_access_level, @@ -719,7 +743,7 @@ module ProjectsHelper analyticsAccessLevel: feature.analytics_access_level, containerRegistryEnabled: !!project.container_registry_enabled, lfsEnabled: !!project.lfs_enabled, - emailsDisabled: project.emails_disabled?, + emailsEnabled: project.emails_enabled?, monitorAccessLevel: feature.monitor_access_level, showDefaultAwardEmojis: project.show_default_award_emojis?, warnAboutPotentiallyUnwantedCharacters: project.warn_about_potentially_unwanted_characters?, diff --git a/app/helpers/sessions_helper.rb b/app/helpers/sessions_helper.rb index 9ef347fff16..cf5cc92587f 100644 --- a/app/helpers/sessions_helper.rb +++ b/app/helpers/sessions_helper.rb @@ -40,10 +40,6 @@ module SessionsHelper request.env['rack.session.options'][:expire_after] = expiry_s end - def send_rate_limited?(user) - Gitlab::ApplicationRateLimiter.peek(:email_verification_code_send, scope: user) - end - def obfuscated_email(email) # Moved to Gitlab::Utils::Email in 15.9 Gitlab::Utils::Email.obfuscated_email(email) @@ -52,4 +48,23 @@ module SessionsHelper def remember_me_enabled? Gitlab::CurrentSettings.remember_me_enabled? end + + def unconfirmed_verification_email?(user) + token_valid_from = ::Users::EmailVerification::ValidateTokenService::TOKEN_VALID_FOR_MINUTES.minutes.ago + user.email_reset_offered_at.nil? && user.pending_reconfirmation? && user.confirmation_sent_at >= token_valid_from + end + + def verification_email(user) + unconfirmed_verification_email?(user) ? user.unconfirmed_email : user.email + end + + def verification_data(user) + { + obfuscated_email: obfuscated_email(verification_email(user)), + verify_path: session_path(:user), + resend_path: users_resend_verification_code_path, + offer_email_reset: user.email_reset_offered_at.nil?.to_s, + update_email_path: users_update_email_path + } + end end diff --git a/app/helpers/sidebars_helper.rb b/app/helpers/sidebars_helper.rb index 90917cb96e0..1bd7da0a352 100644 --- a/app/helpers/sidebars_helper.rb +++ b/app/helpers/sidebars_helper.rb @@ -45,14 +45,37 @@ module SidebarsHelper end def super_sidebar_context(user, group:, project:, panel:, panel_type:) # rubocop:disable Metrics/AbcSize + return super_sidebar_logged_out_context(panel: panel, panel_type: panel_type) unless user + + super_sidebar_logged_in_context(user, group: group, project: project, panel: panel, panel_type: panel_type) + end + + def super_sidebar_logged_out_context(panel:, panel_type:) # rubocop:disable Metrics/AbcSize { + is_logged_in: false, + context_switcher_links: context_switcher_links, current_menu_items: panel.super_sidebar_menu_items, current_context_header: panel.super_sidebar_context_header, + support_path: support_url, + display_whats_new: display_whats_new?, + whats_new_most_recent_release_items_count: whats_new_most_recent_release_items_count, + whats_new_version_digest: whats_new_version_digest, + show_version_check: show_version_check?, + gitlab_version: Gitlab.version_info, + gitlab_version_check: gitlab_version_check, + search: search_data, + panel_type: panel_type + } + end + + def super_sidebar_logged_in_context(user, group:, project:, panel:, panel_type:) # rubocop:disable Metrics/AbcSize + super_sidebar_logged_out_context(panel: panel, panel_type: panel_type).merge({ + is_logged_in: true, name: user.name, username: user.username, avatar_url: user.avatar_url, has_link_to_profile: current_user_menu?(:profile), - link_to_profile: user_url(user), + link_to_profile: user_path(user), logo_url: current_appearance&.header_logo_path, status: user_status_menu_data(user), settings: { @@ -75,26 +98,16 @@ module SidebarsHelper merge_request_menu: create_merge_request_menu(user), projects_path: dashboard_projects_path, groups_path: dashboard_groups_path, - support_path: support_url, - display_whats_new: display_whats_new?, - whats_new_most_recent_release_items_count: whats_new_most_recent_release_items_count, - whats_new_version_digest: whats_new_version_digest, - show_version_check: show_version_check?, - gitlab_version: Gitlab.version_info, - gitlab_version_check: gitlab_version_check, gitlab_com_but_not_canary: Gitlab.com_but_not_canary?, gitlab_com_and_canary: Gitlab.com_and_canary?, canary_toggle_com_url: Gitlab::Saas.canary_toggle_com_url, current_context: super_sidebar_current_context(project: project, group: group), - context_switcher_links: context_switcher_links, - search: search_data, pinned_items: user.pinned_nav_items[panel_type] || super_sidebar_default_pins(panel_type), - panel_type: panel_type, - update_pins_url: pins_url, + update_pins_url: pins_path, is_impersonating: impersonating?, stop_impersonation_path: admin_impersonation_path, shortcut_links: shortcut_links(user, project: project) - } + }) end def super_sidebar_nav_panel( @@ -331,8 +344,7 @@ module SidebarsHelper def context_switcher_links links = [ - # We should probably not return "You work" when used is not logged-in - { title: s_('Navigation|Your work'), link: root_path, icon: 'work' }, + ({ title: s_('Navigation|Your work'), link: root_path, icon: 'work' } if current_user), { title: s_('Navigation|Explore'), link: explore_root_path, icon: 'compass' } ] @@ -368,7 +380,7 @@ module SidebarsHelper end # rubocop: enable Cop/UserAdmin - links + links.compact end def impersonating? diff --git a/app/helpers/snippets_helper.rb b/app/helpers/snippets_helper.rb index 2f9117a74be..31ce8317d51 100644 --- a/app/helpers/snippets_helper.rb +++ b/app/helpers/snippets_helper.rb @@ -45,36 +45,26 @@ module SnippetsHelper def embedded_raw_snippet_button(snippet, blob) return if blob.empty? || blob.binary? || blob.stored_externally? - link_to( - external_snippet_icon('doc-code'), - gitlab_raw_snippet_blob_url(snippet, blob.path), - class: 'gl-button btn btn-default', - target: '_blank', - rel: 'noopener noreferrer', - title: 'Open raw' - ) + render Pajamas::ButtonComponent.new(href: gitlab_raw_snippet_blob_url(snippet, blob.path), target: '_blank', + button_options: { rel: 'noopener noreferrer', title: 'Open raw' }) do + external_snippet_icon('doc-code') + end end def embedded_snippet_download_button(snippet, blob) - link_to( - external_snippet_icon('download'), - gitlab_raw_snippet_blob_url(snippet, blob.path, nil, inline: false), - class: 'gl-button btn btn-default', - target: '_blank', - title: 'Download', - rel: 'noopener noreferrer' - ) + render Pajamas::ButtonComponent.new(href: gitlab_raw_snippet_blob_url(snippet, blob.path, nil, inline: false), + target: '_blank', button_options: { rel: 'noopener noreferrer', title: 'Download' }) do + external_snippet_icon('download') + end end def embedded_copy_snippet_button(blob) return unless blob.rendered_as_text?(ignore_errors: false) - content_tag( - :button, - class: 'gl-button btn btn-default copy-to-clipboard-btn', - title: 'Copy snippet contents', - onclick: "copyToClipboard('.blob-content[data-blob-id=\"#{blob.id}\"] > pre')" - ) do + button_options = { title: 'Copy snippet contents', + onclick: "copyToClipboard('.blob-content[data-blob-id=\"#{blob.id}\"] > pre')" } + + render Pajamas::ButtonComponent.new(button_options: button_options) do external_snippet_icon('copy-to-clipboard') end end diff --git a/app/helpers/time_helper.rb b/app/helpers/time_helper.rb index ad473875a53..0a5751c5221 100644 --- a/app/helpers/time_helper.rb +++ b/app/helpers/time_helper.rb @@ -1,20 +1,20 @@ # frozen_string_literal: true module TimeHelper + TIME_UNIT_TRANSLATION = { + seconds: ->(seconds) { n_('%d second', '%d seconds', seconds) % seconds }, + minutes: ->(minutes) { n_('%d minute', '%d minutes', minutes) % minutes }, + hours: ->(hours) { n_('%d hour', '%d hours', hours) % hours }, + days: ->(days) { n_('%d day', '%d days', days) % days }, + weeks: ->(weeks) { n_('%d week', '%d weeks', weeks) % weeks }, + months: ->(months) { n_('%d month', '%d months', months) % months }, + years: ->(years) { n_('%d year', '%d years', years) % years } + }.freeze + def time_interval_in_words(interval_in_seconds) - interval_in_seconds = interval_in_seconds.to_i - minutes = interval_in_seconds / 60 - seconds = interval_in_seconds - minutes * 60 + time_parts = ActiveSupport::Duration.build(interval_in_seconds.to_i).parts - if minutes >= 1 - if seconds % 60 == 0 - n_('%d minute', '%d minutes', minutes) % minutes - else - [n_('%d minute', '%d minutes', minutes) % minutes, n_('%d second', '%d seconds', seconds) % seconds].to_sentence - end - else - n_('%d second', '%d seconds', seconds) % seconds - end + time_parts.map { |unit, value| TIME_UNIT_TRANSLATION[unit].call(value) }.to_sentence end def duration_in_numbers(duration_in_seconds) diff --git a/app/helpers/todos_helper.rb b/app/helpers/todos_helper.rb index 9b0810f3d17..4f17634f3e4 100644 --- a/app/helpers/todos_helper.rb +++ b/app/helpers/todos_helper.rb @@ -30,6 +30,7 @@ module TodosHelper when Todo::MEMBER_ACCESS_REQUESTED then format( s_("Todos|has requested access to %{what} %{which}"), what: _(todo.member_access_type), which: _(todo.target.name) ) + when Todo::REVIEW_SUBMITTED then s_('Todos|reviewed your merge request') end end diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb index 84512453b7c..880fb8aa9d8 100644 --- a/app/helpers/tree_helper.rb +++ b/app/helpers/tree_helper.rb @@ -153,7 +153,8 @@ module TreeHelper project_short_path: project.path, ref: ref, escaped_ref: ActionDispatch::Journey::Router::Utils.escape_path(ref), - full_name: project.name_with_namespace + full_name: project.name_with_namespace, + ref_type: @ref_type } end diff --git a/app/helpers/users/callouts_helper.rb b/app/helpers/users/callouts_helper.rb index 0f4cbd6642b..12f78d9bd16 100644 --- a/app/helpers/users/callouts_helper.rb +++ b/app/helpers/users/callouts_helper.rb @@ -89,6 +89,8 @@ module Users end def gitlab_com_user_created_after_new_nav_rollout? + return true unless current_user + Gitlab.com? && current_user.created_at >= Date.new(2023, 6, 2) end diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb index 29998a996e2..ac279904fd2 100644 --- a/app/helpers/users_helper.rb +++ b/app/helpers/users_helper.rb @@ -104,6 +104,24 @@ module UsersHelper Gitlab.config.gitlab.impersonation_enabled end + def can_impersonate_user(user, impersonation_in_progress) + can?(user, :log_in) && !user.password_expired? && !impersonation_in_progress + end + + def impersonation_error_text(user, impersonation_in_progress) + if impersonation_in_progress + _("You are already impersonating another user") + elsif user.blocked? + _("You cannot impersonate a blocked user") + elsif user.password_expired? + _("You cannot impersonate a user with an expired password") + elsif user.internal? + _("You cannot impersonate an internal user") + else + _("You cannot impersonate a user who cannot log in") + end + end + def user_badges_in_admin_section(user) [].tap do |badges| badges << blocked_user_badge(user) if user.blocked? @@ -208,6 +226,24 @@ module UsersHelper end end + def user_profile_actions_data(user) + basic_actions_data = { + user_id: user.id + } + + if can?(current_user, :read_user_profile, user) + basic_actions_data[:rss_subscription_path] = user_path(user, rss_url_options) + end + + return basic_actions_data if !current_user || current_user == user + + basic_actions_data.merge( + report_abuse_path: add_category_abuse_reports_path, + reported_user_id: user.id, + reported_from_url: user_url(user) + ) + end + private def admin_users_paths -- cgit v1.2.3