diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-03-04 21:07:54 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-03-04 21:07:54 +0000 |
commit | 2fd92f2dc784ade9cb4e1c33dd60cbfad7b86818 (patch) | |
tree | 7779f36689db97a46e0268a4aec1d49f283eb0c8 /app | |
parent | 42ca24aa5bbab7a2d43bc866d9bee9876941cea2 (diff) | |
download | gitlab-ce-2fd92f2dc784ade9cb4e1c33dd60cbfad7b86818.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
25 files changed, 184 insertions, 51 deletions
diff --git a/app/assets/javascripts/error_tracking/components/error_details.vue b/app/assets/javascripts/error_tracking/components/error_details.vue index 093d993c3ad..43fa97e4095 100644 --- a/app/assets/javascripts/error_tracking/components/error_details.vue +++ b/app/assets/javascripts/error_tracking/components/error_details.vue @@ -108,16 +108,6 @@ export default { 'errorStatus', ]), ...mapGetters('details', ['stacktrace']), - reported() { - return sprintf( - __('Reported %{timeAgo} by %{reportedBy}'), - { - reportedBy: `<strong class="error-details-meta-culprit">${this.error.culprit}</strong>`, - timeAgo: this.timeFormatted(this.stacktraceData.date_received), - }, - false, - ); - }, firstReleaseLink() { return `${this.error.externalBaseUrl}/releases/${this.error.firstReleaseShortVersion}`; }, @@ -227,8 +217,19 @@ export default { </gl-alert> <div class="error-details-header d-flex py-2 justify-content-between"> - <div class="error-details-meta my-auto"> - <span v-if="!loadingStacktrace && stacktrace" v-html="reported"></span> + <div + v-if="!loadingStacktrace && stacktrace" + class="error-details-meta my-auto" + data-qa-selector="reported_text" + > + <gl-sprintf :message="__('Reported %{timeAgo} by %{reportedBy}')"> + <template #reportedBy> + <strong class="error-details-meta-culprit">{{ error.culprit }}</strong> + </template> + <template #timeAgo> + {{ timeFormatted(stacktraceData.date_received) }} + </template> + </gl-sprintf> </div> <div class="error-details-actions"> <div class="d-inline-flex bv-d-sm-down-none"> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue index 57be97855e3..b1fb377e47a 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue @@ -1,5 +1,6 @@ <script> import { GlLoadingIcon } from '@gitlab/ui'; +import { escape } from 'lodash'; import simplePoll from '../../../lib/utils/simple_poll'; import eventHub from '../../event_hub'; import statusIcon from '../mr_widget_status_icon.vue'; @@ -44,11 +45,10 @@ export default { fastForwardMergeText() { return sprintf( __( - `Fast-forward merge is not possible. Rebase the source branch onto %{startTag}${this.mr.targetBranch}%{endTag} to allow this merge request to be merged.`, + 'Fast-forward merge is not possible. Rebase the source branch onto %{targetBranch} to allow this merge request to be merged.', ), { - startTag: '<span class="label-branch">', - endTag: '</span>', + targetBranch: `<span class="label-branch">${escape(this.mr.targetBranch)}</span>`, }, false, ); diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index 82bef91230e..8b0dd73c565 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -1090,6 +1090,20 @@ button.mini-pipeline-graph-dropdown-toggle { } } +.codequality-report { + .media { + padding: $gl-padding; + } + + .media-body { + flex-direction: row; + } + + .report-block-container { + height: auto !important; + } +} + .progress-bar.bg-primary { background-color: $blue-500 !important; } diff --git a/app/controllers/concerns/uploads_actions.rb b/app/controllers/concerns/uploads_actions.rb index 655575e0944..549a443b1a8 100644 --- a/app/controllers/concerns/uploads_actions.rb +++ b/app/controllers/concerns/uploads_actions.rb @@ -9,6 +9,7 @@ module UploadsActions included do prepend_before_action :set_request_format_from_path_extension + rescue_from FileUploader::InvalidSecret, with: :render_404 end def create diff --git a/app/controllers/groups/group_links_controller.rb b/app/controllers/groups/group_links_controller.rb index d3360acd245..23daa29ac43 100644 --- a/app/controllers/groups/group_links_controller.rb +++ b/app/controllers/groups/group_links_controller.rb @@ -24,7 +24,7 @@ class Groups::GroupLinksController < Groups::ApplicationController end def update - @group_link.update(group_link_params) + Groups::GroupLinks::UpdateService.new(@group_link).execute(group_link_params) end def destroy diff --git a/app/graphql/types/diff_refs_type.rb b/app/graphql/types/diff_refs_type.rb index 03d080d784b..4049a204f66 100644 --- a/app/graphql/types/diff_refs_type.rb +++ b/app/graphql/types/diff_refs_type.rb @@ -8,7 +8,7 @@ module Types field :head_sha, GraphQL::STRING_TYPE, null: false, description: 'SHA of the HEAD at the time the comment was made' - field :base_sha, GraphQL::STRING_TYPE, null: false, + field :base_sha, GraphQL::STRING_TYPE, null: true, description: 'Merge base of the branch the comment was made on' field :start_sha, GraphQL::STRING_TYPE, null: false, description: 'SHA of the branch being compared against' diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index f9d7e8a2b7a..481e1807a78 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -6,6 +6,9 @@ class ApplicationSetting < ApplicationRecord include TokenAuthenticatable include ChronicDurationAttribute + GRAFANA_URL_ERROR_MESSAGE = 'Please check your Grafana URL setting in ' \ + 'Admin Area > Settings > Metrics and profiling > Metrics - Grafana' + add_authentication_token_field :runners_registration_token, encrypted: -> { Feature.enabled?(:application_settings_tokens_optional_encryption, default_enabled: true) ? :optional : :required } add_authentication_token_field :health_check_access_token add_authentication_token_field :static_objects_external_storage_auth_token @@ -38,6 +41,14 @@ class ApplicationSetting < ApplicationRecord chronic_duration_attr_writer :archive_builds_in_human_readable, :archive_builds_in_seconds + validates :grafana_url, + system_hook_url: { + blocked_message: "is blocked: %{exception_message}. " + GRAFANA_URL_ERROR_MESSAGE + }, + if: :grafana_url_absolute? + + validate :validate_grafana_url + validates :uuid, presence: true validates :outbound_local_requests_whitelist, @@ -362,6 +373,19 @@ class ApplicationSetting < ApplicationRecord end after_commit :expire_performance_bar_allowed_user_ids_cache, if: -> { previous_changes.key?('performance_bar_allowed_group_id') } + def validate_grafana_url + unless parsed_grafana_url + self.errors.add( + :grafana_url, + "must be a valid relative or absolute URL. #{GRAFANA_URL_ERROR_MESSAGE}" + ) + end + end + + def grafana_url_absolute? + parsed_grafana_url&.absolute? + end + def sourcegraph_url_is_com? !!(sourcegraph_url =~ /\Ahttps:\/\/(www\.)?sourcegraph\.com/) end @@ -394,6 +418,12 @@ class ApplicationSetting < ApplicationRecord rescue RegexpError errors.add(:email_restrictions, _('is not a valid regular expression')) end + + private + + def parsed_grafana_url + @parsed_grafana_url ||= Gitlab::Utils.parse_url(grafana_url) + end end ApplicationSetting.prepend_if_ee('EE::ApplicationSetting') diff --git a/app/models/badge.rb b/app/models/badge.rb index eb351425e66..3400d6d407d 100644 --- a/app/models/badge.rb +++ b/app/models/badge.rb @@ -32,7 +32,9 @@ class Badge < ApplicationRecord end def rendered_image_url(project = nil) - build_rendered_url(image_url, project) + Gitlab::AssetProxy.proxy_url( + build_rendered_url(image_url, project) + ) end private diff --git a/app/models/group.rb b/app/models/group.rb index d6a4af5af15..e9b3e3c3369 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -516,18 +516,29 @@ class Group < Namespace group_group_links_query = GroupGroupLink.where(shared_group_id: self_and_ancestors_ids) cte = Gitlab::SQL::CTE.new(:group_group_links_cte, group_group_links_query) + cte_alias = cte.table.alias(GroupGroupLink.table_name) link = GroupGroupLink .with(cte.to_arel) + .select(smallest_value_arel([cte_alias[:group_access], group_member_table[:access_level]], + 'group_access')) .from([group_member_table, cte.alias_to(group_group_link_table)]) .where(group_member_table[:user_id].eq(user.id)) + .where(group_member_table[:requested_at].eq(nil)) .where(group_member_table[:source_id].eq(group_group_link_table[:shared_with_group_id])) + .where(group_member_table[:source_type].eq('Namespace')) .reorder(Arel::Nodes::Descending.new(group_group_link_table[:group_access])) .first link&.group_access end + def smallest_value_arel(args, column_alias) + Arel::Nodes::As.new( + Arel::Nodes::NamedFunction.new('LEAST', args), + Arel::Nodes::SqlLiteral.new(column_alias)) + end + def self.groups_including_descendants_by(group_ids) Gitlab::ObjectHierarchy .new(Group.where(id: group_ids)) diff --git a/app/models/members/group_member.rb b/app/models/members/group_member.rb index bdff9e28df1..bc3be67bd32 100644 --- a/app/models/members/group_member.rb +++ b/app/models/members/group_member.rb @@ -66,6 +66,7 @@ class GroupMember < Member def after_accept_invite notification_service.accept_group_invite(self) + update_two_factor_requirement super end diff --git a/app/models/user_detail.rb b/app/models/user_detail.rb index 1621f336111..5dc74421705 100644 --- a/app/models/user_detail.rb +++ b/app/models/user_detail.rb @@ -3,5 +3,5 @@ class UserDetail < ApplicationRecord belongs_to :user - validates :job_title, presence: true, length: { maximum: 200 } + validates :job_title, length: { maximum: 200 } end diff --git a/app/presenters/ci/pipeline_presenter.rb b/app/presenters/ci/pipeline_presenter.rb index 37abefb5664..ce9a3346b4b 100644 --- a/app/presenters/ci/pipeline_presenter.rb +++ b/app/presenters/ci/pipeline_presenter.rb @@ -134,7 +134,11 @@ module Ci def all_related_merge_requests strong_memoize(:all_related_merge_requests) do - pipeline.ref ? pipeline.all_merge_requests_by_recency.to_a : [] + if pipeline.ref && can?(current_user, :read_merge_request, pipeline.project) + pipeline.all_merge_requests_by_recency.to_a + else + [] + end end end end diff --git a/app/services/auth/container_registry_authentication_service.rb b/app/services/auth/container_registry_authentication_service.rb index 09a84950755..629c1cbdc5c 100644 --- a/app/services/auth/container_registry_authentication_service.rb +++ b/app/services/auth/container_registry_authentication_service.rb @@ -3,12 +3,24 @@ module Auth class ContainerRegistryAuthenticationService < BaseService AUDIENCE = 'container_registry' + REGISTRY_LOGIN_ABILITIES = [ + :read_container_image, + :create_container_image, + :destroy_container_image, + :update_container_image, + :admin_container_image, + :build_read_container_image, + :build_create_container_image, + :build_destroy_container_image + ].freeze def execute(authentication_abilities:) @authentication_abilities = authentication_abilities return error('UNAVAILABLE', status: 404, message: 'registry not enabled') unless registry.enabled + return error('DENIED', status: 403, message: 'access forbidden') unless has_registry_ability? + unless scopes.any? || current_user || project return error('DENIED', status: 403, message: 'access forbidden') end @@ -197,5 +209,11 @@ module Auth def has_authentication_ability?(capability) @authentication_abilities.to_a.include?(capability) end + + def has_registry_ability? + @authentication_abilities.any? do |ability| + REGISTRY_LOGIN_ABILITIES.include?(ability) + end + end end end diff --git a/app/services/groups/group_links/destroy_service.rb b/app/services/groups/group_links/destroy_service.rb index 29aa8de4e68..6835b6c4637 100644 --- a/app/services/groups/group_links/destroy_service.rb +++ b/app/services/groups/group_links/destroy_service.rb @@ -6,19 +6,17 @@ module Groups def execute(one_or_more_links) links = Array(one_or_more_links) - GroupGroupLink.transaction do - GroupGroupLink.delete(links) + if GroupGroupLink.delete(links) + Gitlab::AppLogger.info( + "GroupGroupLinks with ids: #{links.map(&:id)} have been deleted.") groups_to_refresh = links.map(&:shared_with_group) groups_to_refresh.uniq.each do |group| group.refresh_members_authorized_projects end - - Gitlab::AppLogger.info("GroupGroupLinks with ids: #{links.map(&:id)} have been deleted.") - rescue => ex - Gitlab::AppLogger.error(ex) - - raise + else + Gitlab::AppLogger.info( + "Failed to delete GroupGroupLinks with ids: #{links.map(&:id)}.") end end end diff --git a/app/services/groups/group_links/update_service.rb b/app/services/groups/group_links/update_service.rb new file mode 100644 index 00000000000..71b52cb616c --- /dev/null +++ b/app/services/groups/group_links/update_service.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module Groups + module GroupLinks + class UpdateService < BaseService + def initialize(group_link, user = nil) + super(group_link.shared_group, user) + + @group_link = group_link + end + + def execute(group_link_params) + group_link.update!(group_link_params) + + if requires_authorization_refresh?(group_link_params) + group_link.shared_with_group.refresh_members_authorized_projects + end + end + + private + + attr_accessor :group_link + + def requires_authorization_refresh?(params) + params.include?(:group_access) + end + end + end +end diff --git a/app/services/projects/lfs_pointers/lfs_download_service.rb b/app/services/projects/lfs_pointers/lfs_download_service.rb index bd70012c76c..52c73bcff03 100644 --- a/app/services/projects/lfs_pointers/lfs_download_service.rb +++ b/app/services/projects/lfs_pointers/lfs_download_service.rb @@ -16,17 +16,14 @@ module Projects @lfs_download_object = lfs_download_object end - # rubocop: disable CodeReuse/ActiveRecord def execute return unless project&.lfs_enabled? && lfs_download_object return error("LFS file with oid #{lfs_oid} has invalid attributes") unless lfs_download_object.valid? - return if LfsObject.exists?(oid: lfs_oid) wrap_download_errors do download_lfs_file! end end - # rubocop: enable CodeReuse/ActiveRecord private @@ -39,14 +36,24 @@ module Projects def download_lfs_file! with_tmp_file do |tmp_file| download_and_save_file!(tmp_file) - project.lfs_objects << LfsObject.new(oid: lfs_oid, - size: lfs_size, - file: tmp_file) + + project.lfs_objects << find_or_create_lfs_object(tmp_file) success end end + def find_or_create_lfs_object(tmp_file) + lfs_obj = LfsObject.safe_find_or_create_by!( + oid: lfs_oid, + size: lfs_size + ) + + lfs_obj.update!(file: tmp_file) unless lfs_obj.file.file + + lfs_obj + end + def download_and_save_file!(file) digester = Digest::SHA256.new response = Gitlab::HTTP.get(lfs_sanitized_url, download_headers) do |fragment| diff --git a/app/services/projects/lfs_pointers/lfs_object_download_list_service.rb b/app/services/projects/lfs_pointers/lfs_object_download_list_service.rb index d6e6480bdad..75106297043 100644 --- a/app/services/projects/lfs_pointers/lfs_object_download_list_service.rb +++ b/app/services/projects/lfs_pointers/lfs_object_download_list_service.rb @@ -26,12 +26,12 @@ module Projects return [] end - # Getting all Lfs pointers already in the database and linking them to the project - linked_oids = LfsLinkService.new(project).execute(lfs_pointers_in_repository.keys) - # Retrieving those oids not present in the database which we need to download - missing_oids = lfs_pointers_in_repository.except(*linked_oids) - # Downloading the required information and gathering it inside a LfsDownloadObject for each oid - LfsDownloadLinkListService.new(project, remote_uri: current_endpoint_uri).execute(missing_oids) + # Downloading the required information and gathering it inside an + # LfsDownloadObject for each oid + # + LfsDownloadLinkListService + .new(project, remote_uri: current_endpoint_uri) + .execute(lfs_pointers_in_repository) rescue LfsDownloadLinkListService::DownloadLinksError => e raise LfsObjectDownloadListError, "The LFS objects download list couldn't be imported. Error: #{e.message}" end diff --git a/app/services/web_hook_service.rb b/app/services/web_hook_service.rb index 514ba998d2c..178a321e20c 100644 --- a/app/services/web_hook_service.rb +++ b/app/services/web_hook_service.rb @@ -13,8 +13,14 @@ class WebHookService end end + GITLAB_EVENT_HEADER = 'X-Gitlab-Event' + attr_accessor :hook, :data, :hook_name, :request_options + def self.hook_to_event(hook_name) + hook_name.to_s.singularize.titleize + end + def initialize(hook, data, hook_name) @hook = hook @data = data @@ -112,7 +118,7 @@ class WebHookService @headers ||= begin { 'Content-Type' => 'application/json', - 'X-Gitlab-Event' => hook_name.singularize.titleize + GITLAB_EVENT_HEADER => self.class.hook_to_event(hook_name) }.tap do |hash| hash['X-Gitlab-Token'] = Gitlab::Utils.remove_line_breaks(hook.token) if hook.token.present? end diff --git a/app/uploaders/file_uploader.rb b/app/uploaders/file_uploader.rb index 0fc71d2e3f3..505b51c2006 100644 --- a/app/uploaders/file_uploader.rb +++ b/app/uploaders/file_uploader.rb @@ -16,6 +16,9 @@ class FileUploader < GitlabUploader MARKDOWN_PATTERN = %r{\!?\[.*?\]\(/uploads/(?<secret>[0-9a-f]{32})/(?<file>.*?)\)}.freeze DYNAMIC_PATH_PATTERN = %r{.*(?<secret>\h{32})/(?<identifier>.*)}.freeze + VALID_SECRET_PATTERN = %r{\A\h{10,32}\z}.freeze + + InvalidSecret = Class.new(StandardError) after :remove, :prune_store_dir @@ -153,6 +156,10 @@ class FileUploader < GitlabUploader def secret @secret ||= self.class.generate_secret + + raise InvalidSecret unless @secret =~ VALID_SECRET_PATTERN + + @secret end # return a new uploader with a file copy on another project diff --git a/app/validators/addressable_url_validator.rb b/app/validators/addressable_url_validator.rb index 300bd01ed22..99f503c3f06 100644 --- a/app/validators/addressable_url_validator.rb +++ b/app/validators/addressable_url_validator.rb @@ -23,7 +23,8 @@ # protect against Server-side Request Forgery (SSRF), or check for the right port. # # Configuration options: -# * <tt>message</tt> - A custom error message (default is: "must be a valid URL"). +# * <tt>message</tt> - A custom error message, used when the URL is blank. (default is: "must be a valid URL"). +# * <tt>blocked_message</tt> - A custom error message, used when the URL is blocked. Default: +'is blocked: %{exception_message}'+. # * <tt>schemes</tt> - Array of URI schemes. Default: +['http', 'https']+ # * <tt>allow_localhost</tt> - Allow urls pointing to +localhost+. Default: +true+ # * <tt>allow_local_network</tt> - Allow urls pointing to private network addresses. Default: +true+ @@ -59,7 +60,8 @@ class AddressableUrlValidator < ActiveModel::EachValidator }.freeze DEFAULT_OPTIONS = BLOCKER_VALIDATE_OPTIONS.merge({ - message: 'must be a valid URL' + message: 'must be a valid URL', + blocked_message: 'is blocked: %{exception_message}' }).freeze def initialize(options) @@ -80,7 +82,7 @@ class AddressableUrlValidator < ActiveModel::EachValidator Gitlab::UrlBlocker.validate!(value, blocker_args) rescue Gitlab::UrlBlocker::BlockedUrlError => e - record.errors.add(attribute, "is blocked: #{e.message}") + record.errors.add(attribute, options.fetch(:blocked_message) % { exception_message: e.message }) end private diff --git a/app/views/admin/application_settings/_grafana.html.haml b/app/views/admin/application_settings/_grafana.html.haml index b6e02bde895..700be7db54f 100644 --- a/app/views/admin/application_settings/_grafana.html.haml +++ b/app/views/admin/application_settings/_grafana.html.haml @@ -1,4 +1,4 @@ -= form_for @application_setting, url: admin_application_settings_path(anchor: 'js-grafana-settings'), html: { class: 'fieldset-form' } do |f| += form_for @application_setting, url: metrics_and_profiling_admin_application_settings_path(anchor: 'js-grafana-settings'), html: { class: 'fieldset-form' } do |f| = form_errors(@application_setting) %fieldset diff --git a/app/views/clusters/clusters/_form.html.haml b/app/views/clusters/clusters/_form.html.haml index a85b005b2b4..40fc7d110fc 100644 --- a/app/views/clusters/clusters/_form.html.haml +++ b/app/views/clusters/clusters/_form.html.haml @@ -3,7 +3,7 @@ .form-group %h5= s_('ClusterIntegration|Integration status') %label.append-bottom-0.js-cluster-enable-toggle-area - = render "shared/buttons/project_feature_toggle", is_checked: @cluster.enabled?, label: s_("ClusterIntegration|Toggle Kubernetes cluster"), disabled: !can?(current_user, :update_cluster, @cluster) do + = render "shared/buttons/project_feature_toggle", is_checked: @cluster.enabled?, label: s_("ClusterIntegration|Toggle Kubernetes cluster"), disabled: !can?(current_user, :update_cluster, @cluster), data: { qa_selector: 'integration_status_toggle' } do = field.hidden_field :enabled, { class: 'js-project-feature-toggle-input'} .form-text.text-muted= s_('ClusterIntegration|Enable or disable GitLab\'s connection to your Kubernetes cluster.') @@ -22,7 +22,7 @@ .form-group %h5= s_('ClusterIntegration|Base domain') - = field.text_field :base_domain, class: 'col-md-6 form-control js-select-on-focus qa-base-domain' + = field.text_field :base_domain, class: 'col-md-6 form-control js-select-on-focus', data: { qa_selector: 'base_domain_field' } .form-text.text-muted - auto_devops_url = help_page_path('topics/autodevops/index') - auto_devops_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: auto_devops_url } @@ -37,4 +37,4 @@ - if can?(current_user, :update_cluster, @cluster) .form-group - = field.submit _('Save changes'), class: 'btn btn-success qa-save-domain' + = field.submit _('Save changes'), class: 'btn btn-success', data: { qa_selector: 'save_changes_button' } diff --git a/app/views/clusters/clusters/user/_form.html.haml b/app/views/clusters/clusters/user/_form.html.haml index 39b6d74d9f9..ce226d29113 100644 --- a/app/views/clusters/clusters/user/_form.html.haml +++ b/app/views/clusters/clusters/user/_form.html.haml @@ -54,4 +54,4 @@ = render('clusters/clusters/namespace', platform_field: platform_kubernetes_field) .form-group - = field.submit s_('ClusterIntegration|Add Kubernetes cluster'), class: 'btn btn-success' + = field.submit s_('ClusterIntegration|Add Kubernetes cluster'), class: 'btn btn-success', data: { qa_selector: 'add_kubernetes_cluster_button' } diff --git a/app/views/layouts/nav/sidebar/_admin.html.haml b/app/views/layouts/nav/sidebar/_admin.html.haml index 9f70124ba0d..78cd3f62dec 100644 --- a/app/views/layouts/nav/sidebar/_admin.html.haml +++ b/app/views/layouts/nav/sidebar/_admin.html.haml @@ -83,7 +83,7 @@ = _('Requests Profiles') - if Gitlab::CurrentSettings.current_application_settings.grafana_enabled? = nav_link do - = link_to Gitlab::CurrentSettings.current_application_settings.grafana_url, target: '_blank', title: _('Metrics Dashboard') do + = link_to Gitlab::CurrentSettings.current_application_settings.grafana_url, target: '_blank', title: _('Metrics Dashboard'), rel: 'noopener noreferrer' do %span = _('Metrics Dashboard') = render_if_exists 'layouts/nav/ee/admin/new_monitoring_sidebar' diff --git a/app/views/shared/issuable/form/_branch_chooser.html.haml b/app/views/shared/issuable/form/_branch_chooser.html.haml index 29ac17c43b9..8d9e5ddf065 100644 --- a/app/views/shared/issuable/form/_branch_chooser.html.haml +++ b/app/views/shared/issuable/form/_branch_chooser.html.haml @@ -8,7 +8,9 @@ .form-group.row.d-flex.gl-pl-3.gl-pr-3.branch-selector .align-self-center - %span= s_('From %{source_title} into').html_safe % { source_title: "<code>#{source_title}</code>".html_safe } + %span + = _('From <code>%{source_title}</code> into').html_safe % { source_title: source_title } + - if issuable.new_record? %code= target_title |